diff --git a/DEPS b/DEPS
index 0a9cd217..e3507297 100644
--- a/DEPS
+++ b/DEPS
@@ -175,11 +175,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '9686528ac8c752563425f73237aa0ed60dcbc0dd',
+  'skia_revision': 'f11e331524605268df714fa41f9a9046c3f08c3e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '3b9e4cb4a5d75f7a8fccd9807443b97a85de4561',
+  'v8_revision': '1aff54a5d8872718236985940b6b049de3fb44a5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -187,7 +187,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': '107e4e989e94e70c594eb91b090d3f4a0108bea5',
+  'angle_revision': '38152bda97f56e98dee42e205835166f1e1b7f93',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -238,7 +238,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': '054d61d117f0cff2a34dc23d0ac6f04c434e181a',
+  'catapult_revision': 'bdb8301ab9c1824fca6baeb3cf7104ec67331d71',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # 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 devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '8c53b6f52b2900fec80a8b245862279ad6a1e57f',
+  'devtools_frontend_revision': '218c5d61fbba8ee52e5a216f9b88490a5af7cdb0',
   # 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.
@@ -302,7 +302,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'b867e14cac14aabe1836e414640f48f7b14daef9',
+  'dawn_revision': '0eff2a2b463fa5bf03b080432d19526221ce6c65',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -521,7 +521,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'a8a560da42e41a8268f4081df0810f9e19d934f7',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '3b7dfcd48c53292284d3759e3cb71e4d45414d9b',
       'condition': 'checkout_ios',
   },
 
@@ -852,7 +852,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'fce26aa1391b60ab470283a642efbb99476050e0',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '516de8ff4c7f48efffd85ba7b60988d2e352c95f',
       'condition': 'checkout_linux',
   },
 
@@ -933,7 +933,7 @@
     Var('chromium_git') + '/codecs/libgav1.git' + '@' + 'fa1c3c4e673cf12ffa22b8fbe4a7c79314571f1b',
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '393c564ae0b4e1b81383f4b9bb863f75e93a7ea8',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '8e26c5f50e8589e42d8c0ceb28abe93f3049fbd5',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1228,7 +1228,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '3685fb977e1554091ab10656b8ac0b0c280a2234',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '4504b6a428422d7595c33561bdff5101c182898a',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1448,7 +1448,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '84ee597cdeae08bb26e578fc66a35bcf35f633f4',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '26d52e1ba0907f6a5a4dd525659ba78c344c4cc1',
+    Var('webrtc_git') + '/src.git' + '@' + 'd19513f3ffbb939fd56b5377b678bb31d3154e14',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1523,7 +1523,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@a65af29054120060ea7efd8d6429543b88866ace',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@d34983e8718f355f46754a65e6f79106d1433899',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/BUILD.gn b/android_webview/browser/BUILD.gn
index df05873..207926d 100644
--- a/android_webview/browser/BUILD.gn
+++ b/android_webview/browser/BUILD.gn
@@ -200,8 +200,8 @@
 
     # Called via JNI in CrashpadMain
     "//components/crash/android:crashpad_main",
-    "//components/crash/content/app",
     "//components/crash/content/browser",
+    "//components/crash/core/app",
     "//components/embedder_support/android:web_contents_delegate",
     "//components/embedder_support/android/metrics",
     "//components/google/core/common",
diff --git a/android_webview/browser/DEPS b/android_webview/browser/DEPS
index bd5acad41..6a02b50 100644
--- a/android_webview/browser/DEPS
+++ b/android_webview/browser/DEPS
@@ -12,7 +12,6 @@
   "+components/autofill/core/browser",
   "+components/autofill/core/common",
   "+components/cdm/browser",
-  "+components/crash/content/app",
   "+components/crash/content/browser",
   "+components/crash/core",
   "+components/download/public/common",
diff --git a/android_webview/browser/aw_autofill_client.cc b/android_webview/browser/aw_autofill_client.cc
index f1912149..8719d31 100644
--- a/android_webview/browser/aw_autofill_client.cc
+++ b/android_webview/browser/aw_autofill_client.cc
@@ -228,7 +228,7 @@
   return base::span<const autofill::Suggestion>();
 }
 
-void AwAutofillClient::PinPopupViewUntilUpdate() {
+void AwAutofillClient::PinPopupView() {
   NOTIMPLEMENTED();
 }
 
diff --git a/android_webview/browser/aw_autofill_client.h b/android_webview/browser/aw_autofill_client.h
index 72288956..b8df192 100644
--- a/android_webview/browser/aw_autofill_client.h
+++ b/android_webview/browser/aw_autofill_client.h
@@ -26,7 +26,7 @@
 class MigratableCreditCard;
 class PersonalDataManager;
 class StrikeDatabase;
-}
+}  // namespace autofill
 
 namespace content {
 class WebContents;
@@ -127,7 +127,7 @@
       const std::vector<base::string16>& values,
       const std::vector<base::string16>& labels) override;
   base::span<const autofill::Suggestion> GetPopupSuggestions() const override;
-  void PinPopupViewUntilUpdate() override;
+  void PinPopupView() override;
   void UpdatePopup(const std::vector<autofill::Suggestion>& suggestions,
                    autofill::PopupType popup_type) override;
   void HideAutofillPopup(autofill::PopupHidingReason reason) override;
diff --git a/android_webview/browser/aw_browser_terminator.cc b/android_webview/browser/aw_browser_terminator.cc
index 76ffa5d3..3cba109 100644
--- a/android_webview/browser/aw_browser_terminator.cc
+++ b/android_webview/browser/aw_browser_terminator.cc
@@ -15,8 +15,8 @@
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/post_task.h"
-#include "components/crash/content/app/crashpad.h"
 #include "components/crash/content/browser/crash_metrics_reporter_android.h"
+#include "components/crash/core/app/crashpad.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/child_process_data.h"
diff --git a/android_webview/browser/aw_debug.cc b/android_webview/browser/aw_debug.cc
index c887cfe..733dc58 100644
--- a/android_webview/browser/aw_debug.cc
+++ b/android_webview/browser/aw_debug.cc
@@ -17,8 +17,8 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/threading/thread_restrictions.h"
-#include "components/crash/content/app/crash_reporter_client.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crash_reporter_client.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/crash/core/common/crash_key.h"
 #include "components/minidump_uploader/rewrite_minidumps_as_mimes.h"
 #include "components/version_info/android/channel_getter.h"
diff --git a/android_webview/common/BUILD.gn b/android_webview/common/BUILD.gn
index a350f48..f7ba133 100644
--- a/android_webview/common/BUILD.gn
+++ b/android_webview/common/BUILD.gn
@@ -67,7 +67,7 @@
     "//android_webview:common_jni_headers",
     "//base",
     "//components/cdm/common",
-    "//components/crash/content/app",
+    "//components/crash/core/app",
     "//components/crash/core/common:crash_key",
     "//components/gwp_asan/common",
     "//components/services/heap_profiling/public/cpp",
diff --git a/android_webview/common/crash_reporter/aw_crash_reporter_client.cc b/android_webview/common/crash_reporter/aw_crash_reporter_client.cc
index 02e18c1..4789f7e 100644
--- a/android_webview/common/crash_reporter/aw_crash_reporter_client.cc
+++ b/android_webview/common/crash_reporter/aw_crash_reporter_client.cc
@@ -23,8 +23,8 @@
 #include "base/path_service.h"
 #include "base/scoped_native_library.h"
 #include "build/build_config.h"
-#include "components/crash/content/app/crash_reporter_client.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crash_reporter_client.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/version_info/android/channel_getter.h"
 #include "components/version_info/version_info.h"
 #include "components/version_info/version_info_values.h"
diff --git a/ash/ambient/ambient_controller.cc b/ash/ambient/ambient_controller.cc
index c62ddb7..048db42f 100644
--- a/ash/ambient/ambient_controller.cc
+++ b/ash/ambient/ambient_controller.cc
@@ -35,6 +35,14 @@
   if (chromeos::features::IsAmbientModeEnabled()) {
     registry->RegisterStringPref(ash::ambient::prefs::kAmbientBackdropClientId,
                                  std::string());
+
+    // Do not sync across devices to allow different usages for different
+    // devices.
+    registry->RegisterBooleanPref(ash::ambient::prefs::kAmbientModeEnabled,
+                                  true);
+    registry->RegisterIntegerPref(
+        ash::ambient::prefs::kAmbientModeTopicSource,
+        static_cast<int>(ash::ambient::prefs::TopicSource::kArtGallery));
   }
 }
 
diff --git a/ash/public/cpp/ambient/ambient_prefs.cc b/ash/public/cpp/ambient/ambient_prefs.cc
index bca483c..a296bf6 100644
--- a/ash/public/cpp/ambient/ambient_prefs.cc
+++ b/ash/public/cpp/ambient/ambient_prefs.cc
@@ -12,6 +12,10 @@
 
 constexpr char kAmbientBackdropClientId[] = "ash.ambient.backdrop.client.id";
 
+constexpr char kAmbientModeEnabled[] = "settings.ambient_mode.enabled";
+
+constexpr char kAmbientModeTopicSource[] = "settings.ambient_mode.topic_source";
+
 }  // namespace prefs
 }  // namespace ambient
 }  // namespace ash
diff --git a/ash/public/cpp/ambient/ambient_prefs.h b/ash/public/cpp/ambient/ambient_prefs.h
index 1501586..68a045a 100644
--- a/ash/public/cpp/ambient/ambient_prefs.h
+++ b/ash/public/cpp/ambient/ambient_prefs.h
@@ -11,9 +11,27 @@
 namespace ambient {
 namespace prefs {
 
+// Enumeration of the topic source, i.e. where the photos come from.
+// Values need to stay in sync with the |topicSource_| in ambient_mode_page.js.
+// Art gallery is a super set of art related topic sources in Backdrop service.
+// These values are registered in prefs.
+// Entries should not be renumbered and numeric values should never be reused.
+// Only append to this enum is allowed if more source will be added.
+enum class TopicSource {
+  kGooglePhotos = 0,
+  kArtGallery = 1,
+  kMaxValue = kArtGallery,
+};
+
 // A GUID for backdrop client.
 ASH_PUBLIC_EXPORT extern const char kAmbientBackdropClientId[];
 
+// Boolean pref for whether ambient mode is enabled.
+ASH_PUBLIC_EXPORT extern const char kAmbientModeEnabled[];
+
+// Integer pref for ambient topic source, value is one of TopicSource.
+ASH_PUBLIC_EXPORT extern const char kAmbientModeTopicSource[];
+
 }  // namespace prefs
 }  // namespace ambient
 }  // namespace ash
diff --git a/base/debug/activity_tracker.cc b/base/debug/activity_tracker.cc
index 18ca4ef..c173893c 100644
--- a/base/debug/activity_tracker.cc
+++ b/base/debug/activity_tracker.cc
@@ -1623,12 +1623,6 @@
   modules_.emplace(info.file, record);
 }
 
-void GlobalActivityTracker::RecordFieldTrial(const std::string& trial_name,
-                                             StringPiece group_name) {
-  const std::string key = std::string("FieldTrial.") + trial_name;
-  process_data_.SetString(key, group_name);
-}
-
 void GlobalActivityTracker::RecordException(const void* pc,
                                             const void* origin,
                                             uint32_t code) {
@@ -1680,12 +1674,6 @@
 
   // Note that this process has launched.
   SetProcessPhase(PROCESS_LAUNCHED);
-
-  // Fetch and record all activated field trials.
-  FieldTrial::ActiveGroups active_groups;
-  FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
-  for (auto& group : active_groups)
-    RecordFieldTrial(group.trial_name, group.group_name);
 }
 
 GlobalActivityTracker::~GlobalActivityTracker() {
diff --git a/base/debug/activity_tracker.h b/base/debug/activity_tracker.h
index c7a5720f..03a86ee 100644
--- a/base/debug/activity_tracker.h
+++ b/base/debug/activity_tracker.h
@@ -1046,17 +1046,6 @@
       tracker->RecordModuleInfo(info);
   }
 
-  // Record field trial information. This call is thread-safe. In addition to
-  // this, construction of a GlobalActivityTracker will cause all existing
-  // active field trials to be fetched and recorded.
-  void RecordFieldTrial(const std::string& trial_name, StringPiece group_name);
-  static void RecordFieldTrialIfEnabled(const std::string& trial_name,
-                                        StringPiece group_name) {
-    GlobalActivityTracker* tracker = Get();
-    if (tracker)
-      tracker->RecordFieldTrial(trial_name, group_name);
-  }
-
   // Record exception information for the current thread.
   ALWAYS_INLINE
   void RecordException(const void* origin, uint32_t code) {
diff --git a/base/files/file_util_win.cc b/base/files/file_util_win.cc
index a8bdf2a..a39fc4c 100644
--- a/base/files/file_util_win.cc
+++ b/base/files/file_util_win.cc
@@ -18,6 +18,7 @@
 #include <limits>
 #include <string>
 
+#include "base/debug/alias.h"
 #include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
 #include "base/files/memory_mapped_file.h"
@@ -38,6 +39,7 @@
 #include "base/threading/scoped_thread_priority.h"
 #include "base/time/time.h"
 #include "base/win/scoped_handle.h"
+#include "base/win/windows_types.h"
 #include "base/win/windows_version.h"
 
 namespace base {
@@ -408,9 +410,18 @@
     return true;
   File::Error move_error = File::OSErrorToFileError(GetLastError());
 
+  // Alias paths for investigation of shutdown hangs. crbug.com/1054164
+  FilePath::CharType from_path_str[MAX_PATH];
+  base::wcslcpy(from_path_str, from_path.value().c_str(),
+                base::size(from_path_str));
+  base::debug::Alias(from_path_str);
+  FilePath::CharType to_path_str[MAX_PATH];
+  base::wcslcpy(to_path_str, to_path.value().c_str(), base::size(to_path_str));
+  base::debug::Alias(to_path_str);
+
   // Try the full-blown replace if the move fails, as ReplaceFile will only
-  // succeed when |to_path| does exist. When writing to a network share, we may
-  // not be able to change the ACLs. Ignore ACL errors then
+  // succeed when |to_path| does exist. When writing to a network share, we
+  // may not be able to change the ACLs. Ignore ACL errors then
   // (REPLACEFILE_IGNORE_MERGE_ERRORS).
   if (::ReplaceFile(to_path.value().c_str(), from_path.value().c_str(), NULL,
                     REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) {
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index beb0048..05470a4 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -986,14 +986,6 @@
     ActivateFieldTrialEntryWhileLocked(field_trial);
   }
 
-  // Recording for stability debugging has to be done inline as a task posted
-  // to an observer may not get executed before a crash.
-  debug::GlobalActivityTracker* tracker = debug::GlobalActivityTracker::Get();
-  if (tracker) {
-    tracker->RecordFieldTrial(field_trial->trial_name(),
-                              field_trial->group_name_internal());
-  }
-
   if (global_->synchronous_observer_) {
     global_->synchronous_observer_->OnFieldTrialGroupFinalized(
         field_trial->trial_name(), field_trial->group_name_internal());
diff --git a/base/win/scoped_hstring.h b/base/win/scoped_hstring.h
index 4635b87..e85ef12 100644
--- a/base/win/scoped_hstring.h
+++ b/base/win/scoped_hstring.h
@@ -7,6 +7,7 @@
 
 #include <hstring.h>
 
+#include "base/compiler_specific.h"
 #include "base/scoped_generic.h"
 #include "base/strings/string_piece_forward.h"
 
@@ -61,7 +62,7 @@
   static ScopedHString Create(StringPiece str);
 
   // Loads all required HSTRING functions, available from Win8 and onwards.
-  static bool ResolveCoreWinRTStringDelayload();
+  static bool ResolveCoreWinRTStringDelayload() WARN_UNUSED_RESULT;
 
   // Returns a view into the memory buffer managed by the instance. The returned
   // StringPiece is only valid during the lifetime of this ScopedHString
diff --git a/base/win/scoped_variant.cc b/base/win/scoped_variant.cc
index 2d87dacb..2b0d740 100644
--- a/base/win/scoped_variant.cc
+++ b/base/win/scoped_variant.cc
@@ -32,12 +32,19 @@
   var_.bstrVal = ::SysAllocStringLen(str, length);
 }
 
-ScopedVariant::ScopedVariant(int value, VARTYPE vt) {
+ScopedVariant::ScopedVariant(long value, VARTYPE vt) {
   var_.vt = vt;
-  if (vt == VT_BOOL)
-    var_.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE;
-  else
-    var_.lVal = value;
+  var_.lVal = value;
+}
+
+ScopedVariant::ScopedVariant(int value) {
+  var_.vt = VT_I4;
+  var_.lVal = value;
+}
+
+ScopedVariant::ScopedVariant(bool value) {
+  var_.vt = VT_BOOL;
+  var_.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE;
 }
 
 ScopedVariant::ScopedVariant(double value, VARTYPE vt) {
diff --git a/base/win/scoped_variant.h b/base/win/scoped_variant.h
index ac14dc2..c734840 100644
--- a/base/win/scoped_variant.h
+++ b/base/win/scoped_variant.h
@@ -43,8 +43,15 @@
 
   // Creates a new integral type variant and assigns the value to
   // VARIANT.lVal (32 bit sized field).
-  // NOTE: VT_BOOL constructs here as VARIANT.boolVal.
-  explicit ScopedVariant(int value, VARTYPE vt = VT_I4);
+  explicit ScopedVariant(long value, VARTYPE vt = VT_I4);
+
+  // Creates a new integral type variant for the int type and assigns the value
+  // to VARIANT.lVal (32 bit sized field).
+  explicit ScopedVariant(int value);
+
+  // Creates a new boolean (VT_BOOL) variant and assigns the value to
+  // VARIANT.boolVal.
+  explicit ScopedVariant(bool value);
 
   // Creates a new double-precision type variant.  |vt| must be either VT_R8
   // or VT_DATE.
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 87e8919..b7b71fb 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200324.3.1
\ No newline at end of file
+0.20200325.2.1
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 87e8919..b7b71fb 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200324.3.1
\ No newline at end of file
+0.20200325.2.1
\ No newline at end of file
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc
index 6fba571..8a0f7b5 100644
--- a/cc/metrics/compositor_frame_reporter.cc
+++ b/cc/metrics/compositor_frame_reporter.cc
@@ -24,8 +24,9 @@
 using BlinkBreakdown = CompositorFrameReporter::BlinkBreakdown;
 using VizBreakdown = CompositorFrameReporter::VizBreakdown;
 
-constexpr int kFrameReportTypeCount =
-    static_cast<int>(CompositorFrameReporter::FrameReportType::kMaxValue) + 1;
+constexpr int kDroppedFrameReportTypeCount =
+    static_cast<int>(CompositorFrameReporter::DroppedFrameReportType::
+                         kDroppedFrameReportTypeCount);
 constexpr int kStageTypeCount = static_cast<int>(StageType::kStageTypeCount);
 constexpr int kAllBreakdownCount =
     static_cast<int>(VizBreakdown::kBreakdownCount) +
@@ -138,19 +139,18 @@
   }
 }
 
-// Names for CompositorFrameReporter::FrameReportType, which should be
+// Names for CompositorFrameReporter::DroppedFrameReportType, which should be
 // updated in case of changes to the enum.
-constexpr const char* kReportTypeNames[]{"", "MissedDeadlineFrame.",
-                                         "DroppedFrame."};
+constexpr const char* kReportTypeNames[]{"", "DroppedFrame."};
 
-static_assert(base::size(kReportTypeNames) == kFrameReportTypeCount,
+static_assert(base::size(kReportTypeNames) == kDroppedFrameReportTypeCount,
               "Compositor latency report types has changed.");
 
 // This value should be recalculated in case of changes to the number of values
 // in CompositorFrameReporter::DroppedFrameReportType or in
 // CompositorFrameReporter::StageType
 constexpr int kMaxCompositorLatencyHistogramIndex =
-    kFrameReportTypeCount * kFrameSequenceTrackerTypeCount *
+    kDroppedFrameReportTypeCount * kFrameSequenceTrackerTypeCount *
     (kStageTypeCount + kAllBreakdownCount);
 constexpr int kCompositorLatencyHistogramMin = 1;
 constexpr int kCompositorLatencyHistogramMax = 350000;
@@ -201,14 +201,12 @@
 CompositorFrameReporter::CompositorFrameReporter(
     const base::flat_set<FrameSequenceTrackerType>* active_trackers,
     const viz::BeginFrameId& id,
-    const base::TimeTicks frame_deadline,
     LatencyUkmReporter* latency_ukm_reporter,
     bool is_single_threaded)
     : frame_id_(id),
       is_single_threaded_(is_single_threaded),
       active_trackers_(active_trackers),
-      latency_ukm_reporter_(latency_ukm_reporter),
-      frame_deadline_(frame_deadline) {}
+      latency_ukm_reporter_(latency_ukm_reporter) {}
 
 CompositorFrameReporter::~CompositorFrameReporter() {
   TerminateReporter();
@@ -313,11 +311,7 @@
 }
 
 void CompositorFrameReporter::DroppedFrame() {
-  report_type_ = FrameReportType::kDroppedFrame;
-}
-
-void CompositorFrameReporter::MissedDeadlineFrame() {
-  report_type_ = FrameReportType::kMissedDeadlineFrame;
+  report_type_ = DroppedFrameReportType::kDroppedFrame;
 }
 
 void CompositorFrameReporter::TerminateReporter() {
@@ -326,15 +320,12 @@
   DCHECK_EQ(current_stage_.start_time, base::TimeTicks());
   bool report_compositor_latency = false;
   bool report_event_latency = false;
-  bool report_missed_deadline_frame = false;
   const char* termination_status_str = nullptr;
   switch (frame_termination_status_) {
     case FrameTerminationStatus::kPresentedFrame:
       report_compositor_latency = true;
       report_event_latency = true;
       termination_status_str = "presented_frame";
-      if (frame_deadline_ < frame_termination_time_)
-        report_missed_deadline_frame = true;
       break;
     case FrameTerminationStatus::kDidNotPresentFrame:
       report_compositor_latency = true;
@@ -358,8 +349,9 @@
   // event, so skip emitting the end event, too.
   if (!stage_history_.empty()) {
     const char* submission_status_str =
-        report_type_ == FrameReportType::kDroppedFrame ? "dropped_frame"
-                                                       : "non_dropped_frame";
+        report_type_ == DroppedFrameReportType::kDroppedFrame
+            ? "dropped_frame"
+            : "non_dropped_frame";
     TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP2(
         "cc,benchmark", "PipelineReporter", TRACE_ID_LOCAL(this),
         frame_termination_time_, "termination_status", termination_status_str,
@@ -374,28 +366,14 @@
     stage_history_.emplace_back(StageType::kTotalLatency,
                                 stage_history_.front().start_time,
                                 stage_history_.back().end_time);
-    ReportLatencyHistograms(report_event_latency, report_missed_deadline_frame);
-  }
-}
-
-void CompositorFrameReporter::ReportLatencyHistograms(
-    bool report_event_latency,
-    bool report_delayed_latency) {
-  ReportCompositorLatencyHistograms();
-
-  if (report_delayed_latency) {
-    // If the frames are delayed also report them under MissedDeadlineFrame.
-    MissedDeadlineFrame();
     ReportCompositorLatencyHistograms();
   }
-
   // Only report event latency histograms if the frame was presented.
   if (report_event_latency)
     ReportEventLatencyHistograms();
 }
 
 void CompositorFrameReporter::ReportCompositorLatencyHistograms() const {
-  UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type", report_type_);
   for (const StageData& stage : stage_history_) {
     ReportStageHistogramWithBreakdown(stage);
 
@@ -519,12 +497,12 @@
   const int histogram_index =
       (stage_type_index * kFrameSequenceTrackerTypeCount +
        frame_sequence_tracker_type_index) *
-          kFrameReportTypeCount +
+          kDroppedFrameReportTypeCount +
       report_type_index;
 
   CHECK_LT(stage_type_index, kStageTypeCount + kAllBreakdownCount);
   CHECK_GE(stage_type_index, 0);
-  CHECK_LT(report_type_index, kFrameReportTypeCount);
+  CHECK_LT(report_type_index, kDroppedFrameReportTypeCount);
   CHECK_GE(report_type_index, 0);
   CHECK_LT(histogram_index, kMaxCompositorLatencyHistogramIndex);
   CHECK_GE(histogram_index, 0);
diff --git a/cc/metrics/compositor_frame_reporter.h b/cc/metrics/compositor_frame_reporter.h
index 8c33215..5b07031 100644
--- a/cc/metrics/compositor_frame_reporter.h
+++ b/cc/metrics/compositor_frame_reporter.h
@@ -62,15 +62,14 @@
     kUnknown
   };
 
-  // These values are used for indexing the UMA histograms.
-  enum class FrameReportType {
+  enum class DroppedFrameReportType {
     kNonDroppedFrame = 0,
-    kMissedDeadlineFrame = 1,
-    kDroppedFrame = 2,
-    kMaxValue = kDroppedFrame
+    kDroppedFrame = 1,
+    kDroppedFrameReportTypeCount
   };
 
-  // These values are used for indexing the UMA histograms.
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
   enum class StageType {
     kBeginImplFrameToSendBeginMainFrame = 0,
     kSendBeginMainFrameToCommit = 1,
@@ -122,7 +121,6 @@
   CompositorFrameReporter(
       const base::flat_set<FrameSequenceTrackerType>* active_trackers,
       const viz::BeginFrameId& id,
-      const base::TimeTicks frame_deadline,
       LatencyUkmReporter* latency_ukm_reporter,
       bool is_single_threaded = false);
   ~CompositorFrameReporter();
@@ -155,13 +153,10 @@
 
  private:
   void DroppedFrame();
-  void MissedDeadlineFrame();
 
   void TerminateReporter();
   void EndCurrentStage(base::TimeTicks end_time);
   void ReportCompositorLatencyHistograms() const;
-  void ReportLatencyHistograms(bool report_event_latency = false,
-                               bool report_delayed_latency = false);
   void ReportStageHistogramWithBreakdown(
       const StageData& stage,
       FrameSequenceTrackerType frame_sequence_tracker_type =
@@ -210,7 +205,8 @@
   std::vector<EventMetrics> events_metrics_;
 
   const bool is_single_threaded_;
-  FrameReportType report_type_ = FrameReportType::kNonDroppedFrame;
+  DroppedFrameReportType report_type_ =
+      DroppedFrameReportType::kNonDroppedFrame;
   base::TimeTicks frame_termination_time_;
   base::TimeTicks begin_main_frame_start_;
   FrameTerminationStatus frame_termination_status_ =
@@ -227,7 +223,6 @@
   // The time that work on Impl frame is finished. It's only valid if the
   // reporter is in a stage other than begin impl frame.
   base::TimeTicks impl_frame_finish_time_;
-  base::TimeTicks frame_deadline_;
 };
 }  // namespace cc
 
diff --git a/cc/metrics/compositor_frame_reporter_unittest.cc b/cc/metrics/compositor_frame_reporter_unittest.cc
index 06004ac..6e00be8 100644
--- a/cc/metrics/compositor_frame_reporter_unittest.cc
+++ b/cc/metrics/compositor_frame_reporter_unittest.cc
@@ -19,8 +19,6 @@
 namespace cc {
 namespace {
 
-base::TimeDelta INTERVAL = base::TimeDelta::FromMicroseconds(16);
-
 MATCHER(IsWhitelisted,
         base::StrCat({negation ? "isn't" : "is", " whitelisted"})) {
   return arg.IsWhitelisted();
@@ -33,7 +31,6 @@
       : pipeline_reporter_(
             std::make_unique<CompositorFrameReporter>(&active_trackers,
                                                       viz::BeginFrameId(),
-                                                      Now() + INTERVAL,
                                                       nullptr)) {
     AdvanceNowByMs(1);
   }
diff --git a/cc/metrics/compositor_frame_reporting_controller.cc b/cc/metrics/compositor_frame_reporting_controller.cc
index e197637..f47bd7a 100644
--- a/cc/metrics/compositor_frame_reporting_controller.cc
+++ b/cc/metrics/compositor_frame_reporting_controller.cc
@@ -54,7 +54,7 @@
 }
 
 void CompositorFrameReportingController::WillBeginImplFrame(
-    const viz::BeginFrameArgs& args) {
+    const viz::BeginFrameId& id) {
   base::TimeTicks begin_time = Now();
   if (reporters_[PipelineStage::kBeginImplFrame]) {
     // If the the reporter is replaced in this stage, it means that Impl frame
@@ -64,24 +64,22 @@
         begin_time);
   }
   std::unique_ptr<CompositorFrameReporter> reporter =
-      std::make_unique<CompositorFrameReporter>(
-          &active_trackers_, args.frame_id,
-          args.frame_time + (args.interval * 1.5), latency_ukm_reporter_.get(),
-          is_single_threaded_);
+      std::make_unique<CompositorFrameReporter>(&active_trackers_, id,
+                                                latency_ukm_reporter_.get(),
+                                                is_single_threaded_);
   reporter->StartStage(StageType::kBeginImplFrameToSendBeginMainFrame,
                        begin_time);
   reporters_[PipelineStage::kBeginImplFrame] = std::move(reporter);
 }
 
 void CompositorFrameReportingController::WillBeginMainFrame(
-    const viz::BeginFrameArgs& args) {
+    const viz::BeginFrameId& id) {
   if (reporters_[PipelineStage::kBeginImplFrame]) {
     // We need to use .get() below because operator<< in std::unique_ptr is a
     // C++20 feature.
     DCHECK_NE(reporters_[PipelineStage::kBeginMainFrame].get(),
               reporters_[PipelineStage::kBeginImplFrame].get());
-    DCHECK_EQ(reporters_[PipelineStage::kBeginImplFrame]->frame_id_,
-              args.frame_id);
+    DCHECK_EQ(reporters_[PipelineStage::kBeginImplFrame]->frame_id_, id);
     reporters_[PipelineStage::kBeginImplFrame]->StartStage(
         StageType::kSendBeginMainFrameToCommit, Now());
     AdvanceReporterStage(PipelineStage::kBeginImplFrame,
@@ -91,10 +89,9 @@
     // beginMain frame before next BeginImplFrame (Not reached the ImplFrame
     // deadline yet). So will start a new reporter at BeginMainFrame.
     std::unique_ptr<CompositorFrameReporter> reporter =
-        std::make_unique<CompositorFrameReporter>(
-            &active_trackers_, args.frame_id,
-            args.frame_time + (args.interval * 1.5),
-            latency_ukm_reporter_.get(), is_single_threaded_);
+        std::make_unique<CompositorFrameReporter>(&active_trackers_, id,
+                                                  latency_ukm_reporter_.get(),
+                                                  is_single_threaded_);
     reporter->StartStage(StageType::kSendBeginMainFrameToCommit, Now());
     reporters_[PipelineStage::kBeginMainFrame] = std::move(reporter);
   }
diff --git a/cc/metrics/compositor_frame_reporting_controller.h b/cc/metrics/compositor_frame_reporting_controller.h
index c019f87..8cab7e1 100644
--- a/cc/metrics/compositor_frame_reporting_controller.h
+++ b/cc/metrics/compositor_frame_reporting_controller.h
@@ -50,8 +50,8 @@
       const CompositorFrameReportingController&) = delete;
 
   // Events to signal Beginning/Ending of phases.
-  virtual void WillBeginImplFrame(const viz::BeginFrameArgs& args);
-  virtual void WillBeginMainFrame(const viz::BeginFrameArgs& args);
+  virtual void WillBeginImplFrame(const viz::BeginFrameId& id);
+  virtual void WillBeginMainFrame(const viz::BeginFrameId& id);
   virtual void BeginMainFrameAborted(const viz::BeginFrameId& id);
   virtual void WillInvalidateOnImplSide();
   virtual void WillCommit();
diff --git a/cc/metrics/compositor_frame_reporting_controller_unittest.cc b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
index 62409c1..53c2dcc 100644
--- a/cc/metrics/compositor_frame_reporting_controller_unittest.cc
+++ b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
@@ -50,14 +50,12 @@
 
 class CompositorFrameReportingControllerTest : public testing::Test {
  public:
-  CompositorFrameReportingControllerTest() : current_id_(1, 1) {
-    args_ = SimulateBeginFrameArgs(current_id_);
-  }
+  CompositorFrameReportingControllerTest() : current_id_(1, 1) {}
 
   // The following functions simulate the actions that would
   // occur for each phase of the reporting controller.
   void SimulateBeginImplFrame() {
-    reporting_controller_.WillBeginImplFrame(args_);
+    reporting_controller_.WillBeginImplFrame(current_id_);
   }
 
   void SimulateBeginMainFrame() {
@@ -67,7 +65,7 @@
     CHECK(
         reporting_controller_.reporters()[CompositorFrameReportingController::
                                               PipelineStage::kBeginImplFrame]);
-    reporting_controller_.WillBeginMainFrame(args_);
+    reporting_controller_.WillBeginMainFrame(current_id_);
   }
 
   void SimulateCommit(std::unique_ptr<BeginMainFrameMetrics> blink_breakdown) {
@@ -116,25 +114,10 @@
     reporting_controller_.DidPresentCompositorFrame(*next_token_, details);
   }
 
-  viz::BeginFrameArgs SimulateBeginFrameArgs(
-      viz::BeginFrameId frame_id,
-      base::TimeTicks frame_time = base::TimeTicks::Now(),
-      base::TimeDelta interval = base::TimeDelta::FromMilliseconds(16)) {
-    args_ = viz::BeginFrameArgs();
-    args_.frame_id = frame_id;
-    args_.frame_time = frame_time;
-    args_.interval = interval;
-    return args_;
-  }
-
-  void IncrementCurrentId() {
-    current_id_.sequence_number++;
-    args_.frame_id = current_id_;
-  }
+  void IncrementCurrentId() { current_id_.sequence_number++; }
 
  protected:
   TestCompositorFrameReportingController reporting_controller_;
-  viz::BeginFrameArgs args_;
   viz::BeginFrameId current_id_;
   viz::BeginFrameId last_activated_id_;
   base::TimeTicks begin_main_start_;
@@ -153,18 +136,18 @@
   // - 4 Simultaneous Reporters
 
   // BF
-  reporting_controller_.WillBeginImplFrame(args_);
+  reporting_controller_.WillBeginImplFrame(current_id_);
   EXPECT_EQ(1, reporting_controller_.ActiveReporters());
 
   // BF -> BF
   // Should replace previous reporter.
-  reporting_controller_.WillBeginImplFrame(args_);
+  reporting_controller_.WillBeginImplFrame(current_id_);
   EXPECT_EQ(1, reporting_controller_.ActiveReporters());
 
   // BF -> BMF -> BF
   // Should add new reporter.
-  reporting_controller_.WillBeginMainFrame(args_);
-  reporting_controller_.WillBeginImplFrame(args_);
+  reporting_controller_.WillBeginMainFrame(current_id_);
+  reporting_controller_.WillBeginImplFrame(current_id_);
   EXPECT_EQ(2, reporting_controller_.ActiveReporters());
 
   // BF -> BMF -> BF -> Commit
@@ -175,7 +158,7 @@
 
   // BF -> BMF -> BF -> Commit -> BMF -> Activate -> Commit -> Activation
   // Having two reporters at Activate phase should delete the older one.
-  reporting_controller_.WillBeginMainFrame(args_);
+  reporting_controller_.WillBeginMainFrame(current_id_);
   reporting_controller_.WillActivate();
   reporting_controller_.DidActivate();
   last_activated_id_ = current_id_;
@@ -278,28 +261,23 @@
 TEST_F(CompositorFrameReportingControllerTest, MainFrameCausedNoDamage) {
   base::HistogramTester histogram_tester;
   viz::BeginFrameId current_id_1_(1, 1);
-  viz::BeginFrameArgs args_1_ = SimulateBeginFrameArgs(current_id_1_);
-
   viz::BeginFrameId current_id_2_(1, 2);
-  viz::BeginFrameArgs args_2_ = SimulateBeginFrameArgs(current_id_2_);
-
   viz::BeginFrameId current_id_3_(1, 3);
-  viz::BeginFrameArgs args_3_ = SimulateBeginFrameArgs(current_id_3_);
 
-  reporting_controller_.WillBeginImplFrame(args_1_);
-  reporting_controller_.WillBeginMainFrame(args_1_);
+  reporting_controller_.WillBeginImplFrame(current_id_1_);
+  reporting_controller_.WillBeginMainFrame(current_id_1_);
   reporting_controller_.BeginMainFrameAborted(current_id_1_);
   reporting_controller_.OnFinishImplFrame(current_id_1_);
   reporting_controller_.DidNotProduceFrame(current_id_1_);
 
-  reporting_controller_.WillBeginImplFrame(args_2_);
-  reporting_controller_.WillBeginMainFrame(args_2_);
+  reporting_controller_.WillBeginImplFrame(current_id_2_);
+  reporting_controller_.WillBeginMainFrame(current_id_2_);
   reporting_controller_.OnFinishImplFrame(current_id_2_);
   reporting_controller_.BeginMainFrameAborted(current_id_2_);
   reporting_controller_.DidNotProduceFrame(current_id_2_);
 
-  reporting_controller_.WillBeginImplFrame(args_3_);
-  reporting_controller_.WillBeginMainFrame(args_3_);
+  reporting_controller_.WillBeginImplFrame(current_id_3_);
+  reporting_controller_.WillBeginMainFrame(current_id_3_);
 
   histogram_tester.ExpectTotalCount(
       "CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0);
@@ -310,8 +288,8 @@
 TEST_F(CompositorFrameReportingControllerTest, MainFrameAborted) {
   base::HistogramTester histogram_tester;
 
-  reporting_controller_.WillBeginImplFrame(args_);
-  reporting_controller_.WillBeginMainFrame(args_);
+  reporting_controller_.WillBeginImplFrame(current_id_);
+  reporting_controller_.WillBeginMainFrame(current_id_);
   reporting_controller_.BeginMainFrameAborted(current_id_);
   reporting_controller_.OnFinishImplFrame(current_id_);
   reporting_controller_.DidSubmitCompositorFrame(
@@ -334,23 +312,17 @@
 TEST_F(CompositorFrameReportingControllerTest, MainFrameAborted2) {
   base::HistogramTester histogram_tester;
   viz::BeginFrameId current_id_1_(1, 1);
-  viz::BeginFrameArgs args_1_ = SimulateBeginFrameArgs(current_id_1_);
-
   viz::BeginFrameId current_id_2_(1, 2);
-  viz::BeginFrameArgs args_2_ = SimulateBeginFrameArgs(current_id_2_);
-
   viz::BeginFrameId current_id_3_(1, 3);
-  viz::BeginFrameArgs args_3_ = SimulateBeginFrameArgs(current_id_3_);
-
-  reporting_controller_.WillBeginImplFrame(args_1_);
+  reporting_controller_.WillBeginImplFrame(current_id_1_);
   reporting_controller_.OnFinishImplFrame(current_id_1_);
-  reporting_controller_.WillBeginMainFrame(args_1_);
+  reporting_controller_.WillBeginMainFrame(current_id_1_);
   reporting_controller_.WillCommit();
   reporting_controller_.DidCommit();
   reporting_controller_.WillActivate();
   reporting_controller_.DidActivate();
-  reporting_controller_.WillBeginImplFrame(args_2_);
-  reporting_controller_.WillBeginMainFrame(args_2_);
+  reporting_controller_.WillBeginImplFrame(current_id_2_);
+  reporting_controller_.WillBeginMainFrame(current_id_2_);
   reporting_controller_.OnFinishImplFrame(current_id_2_);
   reporting_controller_.BeginMainFrameAborted(current_id_2_);
   reporting_controller_.DidSubmitCompositorFrame(
@@ -390,7 +362,7 @@
   histogram_tester.ExpectTotalCount(
       "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame",
       2);
-  reporting_controller_.WillBeginImplFrame(args_3_);
+  reporting_controller_.WillBeginImplFrame(current_id_3_);
   reporting_controller_.OnFinishImplFrame(current_id_3_);
   reporting_controller_.DidSubmitCompositorFrame(
       3, current_id_3_, current_id_1_, std::vector<EventMetrics>());
@@ -468,80 +440,6 @@
       "CompositorLatency.SendBeginMainFrameToCommit.BeginMainSentToStarted", 1);
 }
 
-// If the presentation of the frame happens before deadline.
-TEST_F(CompositorFrameReportingControllerTest, ReportingMissedDeadlineFrame1) {
-  base::HistogramTester histogram_tester;
-
-  reporting_controller_.WillBeginImplFrame(args_);
-  reporting_controller_.OnFinishImplFrame(current_id_);
-  reporting_controller_.WillBeginMainFrame(args_);
-  reporting_controller_.WillCommit();
-  reporting_controller_.DidCommit();
-  reporting_controller_.WillActivate();
-  reporting_controller_.DidActivate();
-  reporting_controller_.DidSubmitCompositorFrame(1, current_id_, current_id_,
-                                                 std::vector<EventMetrics>());
-  viz::FrameTimingDetails details = {};
-  details.presentation_feedback.timestamp =
-      args_.frame_time + args_.interval * 1.5 -
-      base::TimeDelta::FromMicroseconds(100);
-  reporting_controller_.DidPresentCompositorFrame(1, details);
-
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.TotalLatency", 1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.MissedDeadlineFrame."
-      "BeginImplFrameToSendBeginMainFrame",
-      0);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.MissedDeadlineFrame.TotalLatency", 0);
-
-  // Non-dropped cases.
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type", 0, 1);
-  // Missed-deadline cases.
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type", 1, 0);
-  // Dropped cases.
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type", 2, 0);
-}
-
-// If the presentation of the frame happens after deadline.
-TEST_F(CompositorFrameReportingControllerTest, ReportingMissedDeadlineFrame2) {
-  base::HistogramTester histogram_tester;
-
-  reporting_controller_.WillBeginImplFrame(args_);
-  reporting_controller_.OnFinishImplFrame(current_id_);
-  reporting_controller_.WillBeginMainFrame(args_);
-  reporting_controller_.WillCommit();
-  reporting_controller_.DidCommit();
-  reporting_controller_.WillActivate();
-  reporting_controller_.DidActivate();
-  reporting_controller_.DidSubmitCompositorFrame(1, current_id_, current_id_,
-                                                 std::vector<EventMetrics>());
-  viz::FrameTimingDetails details = {};
-  details.presentation_feedback.timestamp =
-      args_.frame_time + args_.interval * 1.5 +
-      base::TimeDelta::FromMicroseconds(100);
-  reporting_controller_.DidPresentCompositorFrame(1, details);
-
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.TotalLatency", 1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.MissedDeadlineFrame."
-      "BeginImplFrameToSendBeginMainFrame",
-      1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.MissedDeadlineFrame.TotalLatency", 1);
-
-  // Non-dropped cases.
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type", 0, 1);
-  // Missed-deadline cases.
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type", 1, 1);
-  // Dropped cases.
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type", 2, 0);
-}
-
 // Tests that EventLatency histograms are reported properly when a frame is
 // presented to the user.
 TEST_F(CompositorFrameReportingControllerTest,
diff --git a/cc/metrics/compositor_timing_history.cc b/cc/metrics/compositor_timing_history.cc
index e218dd10..009ae74 100644
--- a/cc/metrics/compositor_timing_history.cc
+++ b/cc/metrics/compositor_timing_history.cc
@@ -633,7 +633,7 @@
   viz::BeginFrameArgs::BeginFrameArgsType frame_type = args.type;
   base::TimeTicks frame_time = args.frame_time;
 
-  compositor_frame_reporting_controller_->WillBeginImplFrame(args);
+  compositor_frame_reporting_controller_->WillBeginImplFrame(args.frame_id);
 
   // The check for whether a BeginMainFrame was sent anytime between two
   // BeginImplFrames protects us from not detecting a fast main thread that
@@ -668,7 +668,7 @@
     const viz::BeginFrameArgs& args) {
   DCHECK_EQ(base::TimeTicks(), begin_main_frame_sent_time_);
 
-  compositor_frame_reporting_controller_->WillBeginMainFrame(args);
+  compositor_frame_reporting_controller_->WillBeginMainFrame(args.frame_id);
 
   begin_main_frame_on_critical_path_ = args.on_critical_path;
   begin_main_frame_sent_time_ = Now();
diff --git a/cc/metrics/latency_ukm_reporter.cc b/cc/metrics/latency_ukm_reporter.cc
index 9678ede..dc5ea3d 100644
--- a/cc/metrics/latency_ukm_reporter.cc
+++ b/cc/metrics/latency_ukm_reporter.cc
@@ -10,7 +10,7 @@
 namespace cc {
 
 void LatencyUkmReporter::ReportLatencyUkm(
-    CompositorFrameReporter::FrameReportType report_type,
+    CompositorFrameReporter::DroppedFrameReportType report_type,
     const std::vector<CompositorFrameReporter::StageData>& stage_history,
     const base::flat_set<FrameSequenceTrackerType>* active_trackers,
     const viz::FrameTimingDetails& viz_breakdown) {
diff --git a/cc/metrics/latency_ukm_reporter.h b/cc/metrics/latency_ukm_reporter.h
index 9bbc810c..3c171d8b 100644
--- a/cc/metrics/latency_ukm_reporter.h
+++ b/cc/metrics/latency_ukm_reporter.h
@@ -21,7 +21,7 @@
   ~LatencyUkmReporter() = default;
 
   void ReportLatencyUkm(
-      CompositorFrameReporter::FrameReportType report_type,
+      CompositorFrameReporter::DroppedFrameReportType report_type,
       const std::vector<CompositorFrameReporter::StageData>& stage_history,
       const base::flat_set<FrameSequenceTrackerType>* active_trackers,
       const viz::FrameTimingDetails& viz_breakdown);
diff --git a/cc/test/fake_compositor_frame_reporting_controller.cc b/cc/test/fake_compositor_frame_reporting_controller.cc
index 80aa09e2..b1183fe 100644
--- a/cc/test/fake_compositor_frame_reporting_controller.cc
+++ b/cc/test/fake_compositor_frame_reporting_controller.cc
@@ -10,39 +10,27 @@
 #include "components/viz/common/frame_timing_details.h"
 
 namespace cc {
-base::TimeDelta INTERVAL = base::TimeDelta::FromMilliseconds(16);
-
 FakeCompositorFrameReportingController::FakeCompositorFrameReportingController(
     bool is_single_threaded)
     : CompositorFrameReportingController(is_single_threaded) {}
 
 void FakeCompositorFrameReportingController::WillBeginMainFrame(
-    const viz::BeginFrameArgs& args) {
+    const viz::BeginFrameId& id) {
   if (!reporters_[PipelineStage::kBeginImplFrame])
-    CompositorFrameReportingController::WillBeginImplFrame(args);
-  CompositorFrameReportingController::WillBeginMainFrame(args);
+    CompositorFrameReportingController::WillBeginImplFrame(id);
+  CompositorFrameReportingController::WillBeginMainFrame(id);
 }
 
 void FakeCompositorFrameReportingController::BeginMainFrameAborted(
     const viz::BeginFrameId& id) {
-  if (!reporters_[PipelineStage::kBeginMainFrame]) {
-    viz::BeginFrameArgs args = viz::BeginFrameArgs();
-    args.frame_id = id;
-    args.frame_time = Now();
-    args.interval = INTERVAL;
-    WillBeginMainFrame(args);
-  }
+  if (!reporters_[PipelineStage::kBeginMainFrame])
+    WillBeginMainFrame(id);
   CompositorFrameReportingController::BeginMainFrameAborted(id);
 }
 
 void FakeCompositorFrameReportingController::WillCommit() {
-  if (!reporters_[PipelineStage::kBeginMainFrame]) {
-    viz::BeginFrameArgs args = viz::BeginFrameArgs();
-    args.frame_id = viz::BeginFrameId();
-    args.frame_time = Now();
-    args.interval = INTERVAL;
-    WillBeginMainFrame(args);
-  }
+  if (!reporters_[PipelineStage::kBeginMainFrame])
+    WillBeginMainFrame(viz::BeginFrameId());
   CompositorFrameReportingController::WillCommit();
 }
 
diff --git a/cc/test/fake_compositor_frame_reporting_controller.h b/cc/test/fake_compositor_frame_reporting_controller.h
index cbab3fd..b71c857 100644
--- a/cc/test/fake_compositor_frame_reporting_controller.h
+++ b/cc/test/fake_compositor_frame_reporting_controller.h
@@ -29,7 +29,7 @@
   FakeCompositorFrameReportingController& operator=(
       const FakeCompositorFrameReportingController& controller) = delete;
 
-  void WillBeginMainFrame(const viz::BeginFrameArgs& args) override;
+  void WillBeginMainFrame(const viz::BeginFrameId& id) override;
   void BeginMainFrameAborted(const viz::BeginFrameId& id) override;
   void WillCommit() override;
   void DidCommit() override;
diff --git a/cc/trees/ukm_manager.cc b/cc/trees/ukm_manager.cc
index 5a9e68cf..5973c3f 100644
--- a/cc/trees/ukm_manager.cc
+++ b/cc/trees/ukm_manager.cc
@@ -188,13 +188,14 @@
 }
 
 void UkmManager::RecordLatencyUKM(
-    CompositorFrameReporter::FrameReportType report_type,
+    CompositorFrameReporter::DroppedFrameReportType report_type,
     const std::vector<CompositorFrameReporter::StageData>& stage_history,
     const base::flat_set<FrameSequenceTrackerType>* active_trackers,
     const viz::FrameTimingDetails& viz_breakdown) const {
   ukm::builders::Graphics_Smoothness_Latency builder(source_id_);
 
-  if (report_type == CompositorFrameReporter::FrameReportType::kDroppedFrame) {
+  if (report_type ==
+      CompositorFrameReporter::DroppedFrameReportType::kDroppedFrame) {
     builder.SetMissedFrame(true);
   }
 
diff --git a/cc/trees/ukm_manager.h b/cc/trees/ukm_manager.h
index 08cce51e..401b7c43 100644
--- a/cc/trees/ukm_manager.h
+++ b/cc/trees/ukm_manager.h
@@ -49,7 +49,7 @@
   void RecordAggregateThroughput(AggregationType aggregation_type,
                                  int64_t throughput_percent) const;
   void RecordLatencyUKM(
-      CompositorFrameReporter::FrameReportType report_type,
+      CompositorFrameReporter::DroppedFrameReportType report_type,
       const std::vector<CompositorFrameReporter::StageData>& stage_history,
       const base::flat_set<FrameSequenceTrackerType>* active_trackers,
       const viz::FrameTimingDetails& viz_breakdown) const;
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index dc1faa8..49138134 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -186,7 +186,7 @@
         "//chrome/installer/util:constants",
         "//chrome/installer/util:did_run_support",
         "//components/browser_watcher:browser_watcher_client",
-        "//components/crash/content/app:run_as_crashpad_handler",
+        "//components/crash/core/app:run_as_crashpad_handler",
         "//components/crash/core/common",
         "//components/flags_ui:switches",
         "//content:sandbox_helper_win",
@@ -371,7 +371,7 @@
       "//chrome/install_static:install_static_util",
       "//chrome/install_static:secondary_module",
       "//components/browser_watcher:stability_client",
-      "//components/crash/content/app",
+      "//components/crash/core/app",
       "//components/policy:generated",
       "//content/app/resources",
       "//content/public/common:service_names",
@@ -444,7 +444,7 @@
         "//chrome/install_static:install_static_util",
         "//chrome/install_static:secondary_module",
         "//components/browser_watcher:browser_watcher_client",
-        "//components/crash/content/app",
+        "//components/crash/core/app",
         "//content/public/app:child",
         "//content/public/common:service_names",
         "//headless:headless_shell_child_lib",
@@ -764,7 +764,7 @@
 
     public_deps = [
       "//chrome/app_shim:app_mode_loader",
-      "//components/crash/content/app:chrome_crashpad_handler",
+      "//components/crash/core/app:chrome_crashpad_handler",
     ]
 
     foreach(helper_params, content_mac_helpers) {
@@ -1028,7 +1028,7 @@
       "//chrome/app:command_ids",
       "//chrome/common:buildflags",
       "//chrome/common/profiler",
-      "//components/crash/content/app",
+      "//components/crash/core/app",
       "//components/policy:generated",
       "//content/public/app:both",
       "//content/public/common:service_names",
@@ -1194,7 +1194,7 @@
         ":chrome_app",
         ":chrome_framework",
         "//chrome/browser/ui/cocoa/notifications:alert_notification_xpc_service",
-        "//components/crash/content/app:chrome_crashpad_handler",
+        "//components/crash/core/app:chrome_crashpad_handler",
         "//third_party/breakpad:dump_syms",
         "//third_party/swiftshader/src/OpenGL/libEGL:swiftshader_libEGL",
         "//third_party/swiftshader/src/OpenGL/libGLESv2:swiftshader_libGLESv2",
@@ -1225,7 +1225,7 @@
         ":chrome_app",
         ":chrome_framework",
         "//chrome/browser/ui/cocoa/notifications:alert_notification_xpc_service",
-        "//components/crash/content/app:chrome_crashpad_handler",
+        "//components/crash/core/app:chrome_crashpad_handler",
         "//third_party/swiftshader/src/OpenGL/libEGL:swiftshader_libEGL",
         "//third_party/swiftshader/src/OpenGL/libGLESv2:swiftshader_libGLESv2",
       ]
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 906b52b..500b084 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1601,6 +1601,7 @@
   "java/src/org/chromium/chrome/browser/tab/TabWebContentsUserData.java",
   "java/src/org/chromium/chrome/browser/tab/TrustedCdn.java",
   "java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java",
+  "java/src/org/chromium/chrome/browser/tab/state/EncryptedFilePersistedTabDataStorage.java",
   "java/src/org/chromium/chrome/browser/tab/state/FilePersistedTabDataStorage.java",
   "java/src/org/chromium/chrome/browser/tab/state/PersistedTabData.java",
   "java/src/org/chromium/chrome/browser/tab/state/PersistedTabDataConfiguration.java",
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index a270ad9..e4d85ae 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -218,7 +218,7 @@
     if (!_is_monochrome && !_is_trichrome) {
       deps += [
         "//chrome/android:chrome_public_v8_assets",
-        "//components/crash/content/app:chrome_crashpad_handler_named_as_so",
+        "//components/crash/core/app:chrome_crashpad_handler_named_as_so",
         "//third_party/icu:icu_assets",
       ]
       if (!defined(loadable_modules)) {
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 2403f1a4..022cda7 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -120,6 +120,7 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantGenericUiModel.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantGenericUiViewBinder.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantValue.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewEvents.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewFactory.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewInteractions.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java",
@@ -185,6 +186,7 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantGenericUiDelegate.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantGenericUiModel.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantValue.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewEvents.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewFactory.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewInteractions.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderDelegate.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
index 176e589..91f5f24 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
@@ -10,7 +10,6 @@
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
 import org.chromium.chrome.browser.help.HelpAndFeedback;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.ui.TabObscuringHandler;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 
@@ -82,7 +81,8 @@
      * Show the Chrome feedback form.
      */
     public void showFeedback(String debugContext) {
-        Profile profile = ((TabImpl) mActivity.getActivityTabProvider().get()).getProfile();
+        Profile profile =
+                Profile.fromWebContents(mActivity.getActivityTabProvider().get().getWebContents());
 
         HelpAndFeedback.getInstance().showFeedback(mActivity, profile,
                 mActivity.getActivityTab().getUrlString(), FEEDBACK_CATEGORY_TAG,
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewEvents.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewEvents.java
new file mode 100644
index 0000000..15da5b8
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewEvents.java
@@ -0,0 +1,20 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.autofill_assistant.generic_ui;
+
+import android.view.View;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+/** JNI bridge between {@code generic_ui_events_android} and Java. */
+@JNINamespace("autofill_assistant")
+public class AssistantViewEvents {
+    @CalledByNative
+    private static void setOnClickListener(
+            View view, String identifier, AssistantGenericUiDelegate delegate) {
+        view.setOnClickListener(unused -> delegate.onViewClicked(identifier));
+    }
+}
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewInteractions.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewInteractions.java
index cd2411d..e11c191f 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewInteractions.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewInteractions.java
@@ -26,16 +26,10 @@
 import java.util.Collections;
 import java.util.List;
 
-/** JNI bridge between {@code interaction_handler_android} and Java. */
+/** JNI bridge between {@code generic_ui_interactions_android} and Java. */
 @JNINamespace("autofill_assistant")
 public class AssistantViewInteractions {
     @CalledByNative
-    private static void setOnClickListener(
-            View view, String identifier, AssistantGenericUiDelegate delegate) {
-        view.setOnClickListener(unused -> delegate.onViewClicked(identifier));
-    }
-
-    @CalledByNative
     private static void showListPopup(Context context, String[] itemNames,
             @PopupItemType int[] itemTypes, int[] selectedItems, boolean multiple,
             String selectedIndicesIdentifier, @Nullable String selectedNamesIdentifier,
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java
index 7a96182..abed3a6 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java
@@ -124,10 +124,12 @@
     @CalledByNative
     private void addOptionToggleToAccessorySheetData(Object objAccessorySheetData,
             String displayText, boolean enabled, @AccessoryAction int accessoryAction) {
-        // TODO(crbug.com/1044930): Update the callback with a call to native which communicates the
-        // new state of the toggle.
         ((AccessorySheetData) objAccessorySheetData)
-                .setOptionToggle(new OptionToggle(displayText, enabled, e -> {}));
+                .setOptionToggle(new OptionToggle(displayText, enabled, on -> {
+                    assert mNativeView != 0 : "Controller was destroyed but the bridge wasn't!";
+                    ManualFillingComponentBridgeJni.get().onToggleChanged(
+                            mNativeView, ManualFillingComponentBridge.this, accessoryAction, on);
+                }));
     }
 
     @CalledByNative
@@ -199,6 +201,8 @@
                 ManualFillingComponentBridge caller, int tabType, UserInfoField userInfoField);
         void onOptionSelected(long nativeManualFillingViewAndroid,
                 ManualFillingComponentBridge caller, int accessoryAction);
+        void onToggleChanged(long nativeManualFillingViewAndroid,
+                ManualFillingComponentBridge caller, int accessoryAction, boolean enabled);
         void cachePasswordSheetDataForTesting(WebContents webContents, String[] userNames,
                 String[] passwords, boolean originBlacklisted);
         void notifyFocusedFieldTypeForTesting(WebContents webContents, int focusedFieldType);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
index 02d990e6..24a60f9 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
@@ -64,7 +64,7 @@
     public static final String MIN_SDK_PARAM = "zooming-min-sdk-version";
     public static final IntCachedFieldTrialParameter ZOOMING_MIN_SDK =
             new IntCachedFieldTrialParameter(
-                    ChromeFeatureList.TAB_TO_GTS_ANIMATION, MIN_SDK_PARAM, Build.VERSION_CODES.O);
+                    ChromeFeatureList.TAB_TO_GTS_ANIMATION, MIN_SDK_PARAM, Build.VERSION_CODES.M);
     // Field trial parameter for the minimum physical memory size to enable zooming animation.
     public static final String MIN_MEMORY_MB_PARAM = "zooming-min-memory-mb";
     public static final IntCachedFieldTrialParameter ZOOMING_MIN_MEMORY =
diff --git a/chrome/android/features/tab_ui/junit/DEPS b/chrome/android/features/tab_ui/junit/DEPS
index ee28923..fe7bd8a 100644
--- a/chrome/android/features/tab_ui/junit/DEPS
+++ b/chrome/android/features/tab_ui/junit/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
  "+chrome/browser/android/lifecycle",
+ "+chrome/browser/profiles/android/java",
  "+chrome/browser/util",
  "+content/public/android/java/src/org/chromium/content_public/browser",
  "+components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement",
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageServiceUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageServiceUnitTest.java
index 966a02ab..05d6238 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageServiceUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageServiceUnitTest.java
@@ -27,6 +27,9 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.DisableNativeTestRule;
+import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.profiles.ProfileJni;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelFilterProvider;
@@ -69,6 +72,11 @@
 
     private TabSuggestionMessageService mMessageService;
 
+    @Rule
+    public JniMocker mocker = new JniMocker();
+    @Mock
+    public Profile.Natives mMockProfileNatives;
+
     @Mock
     Context mContext;
     @Mock
@@ -89,6 +97,7 @@
     public void setUp() {
         // After setUp there are three tabs in TabModel.
         MockitoAnnotations.initMocks(this);
+        mocker.mock(ProfileJni.TEST_HOOKS, mMockProfileNatives);
 
         // Set up Tabs.
         mTab1 = TabUiUnitTestUtils.prepareTab(TAB1_ID, TAB1_ROOT_ID, null, "");
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUnitTestUtils.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUnitTestUtils.java
index fa71cc1..bcbfacb 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUnitTestUtils.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUnitTestUtils.java
@@ -35,7 +35,6 @@
 
     public static TabImpl prepareTab(int tabId, int rootId, Profile profile, String visibleUrl) {
         TabImpl tab = prepareTab(tabId, rootId);
-        doReturn(null).when(tab).getProfile();
         WebContents webContents = mock(WebContents.class);
         GURL gurl = mock(GURL.class);
         doReturn(visibleUrl).when(gurl).getSpec();
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabContextTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabContextTest.java
index 41d846f..a595b7a 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabContextTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabContextTest.java
@@ -18,6 +18,9 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
+import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.profiles.ProfileJni;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tabmodel.TabModelFilter;
@@ -49,6 +52,12 @@
     @Rule
     public TestRule mProcessor = new Features.JUnitProcessor();
 
+    @Rule
+    public JniMocker mocker = new JniMocker();
+
+    @Mock
+    public Profile.Natives mMockProfileNatives;
+
     @Mock
     private TabModelSelector mTabModelSelector;
 
@@ -70,6 +79,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mocker.mock(ProfileJni.TEST_HOOKS, mMockProfileNatives);
         doReturn(mTabModelFilterProvider).when(mTabModelSelector).getTabModelFilterProvider();
         doReturn(mTabModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
     }
@@ -81,7 +91,6 @@
         doReturn(rootId).when(tab).getRootId();
         doReturn(title).when(tab).getTitle();
         doReturn(url).when(tab).getUrlString();
-        doReturn(null).when(tab).getProfile();
         doReturn(originalUrl).when(tab).getOriginalUrl();
         WebContents webContents = mock(WebContents.class);
         GURL gurl = mock(GURL.class);
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestratorTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestratorTest.java
index 2899478..a03567e5 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestratorTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestratorTest.java
@@ -26,7 +26,10 @@
 import org.chromium.base.Callback;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.InMemorySharedPreferences;
+import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.profiles.ProfileJni;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabCreationState;
 import org.chromium.chrome.browser.tab.TabImpl;
@@ -54,6 +57,12 @@
     @Rule
     public TestRule mProcessor = new Features.JUnitProcessor();
 
+    @Rule
+    public JniMocker mocker = new JniMocker();
+
+    @Mock
+    public Profile.Natives mMockProfileNatives;
+
     @Mock
     private TabModelSelector mTabModelSelector;
 
@@ -74,7 +83,6 @@
     private static Tab mockTab(int id) {
         TabImpl tab = mock(TabImpl.class);
         doReturn(id).when(tab).getId();
-        doReturn(null).when(tab).getProfile();
         WebContents webContents = mock(WebContents.class);
         GURL gurl = mock(GURL.class);
         doReturn("").when(gurl).getSpec();
@@ -86,6 +94,7 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mocker.mock(ProfileJni.TEST_HOOKS, mMockProfileNatives);
         doReturn(mTabModelFilterProvider).when(mTabModelSelector).getTabModelFilterProvider();
         doNothing()
                 .when(mTabModelFilterProvider)
diff --git a/chrome/android/features/vr/BUILD.gn b/chrome/android/features/vr/BUILD.gn
index 0a21a1ea..ac1da8c9 100644
--- a/chrome/android/features/vr/BUILD.gn
+++ b/chrome/android/features/vr/BUILD.gn
@@ -75,6 +75,7 @@
     "//chrome/android:chrome_java",
     "//chrome/browser/flags:java",
     "//chrome/browser/preferences:java",
+    "//chrome/browser/profiles/android:java",
     "//chrome/browser/util:java",
     "//components/browser_ui/modaldialog/android:java",
     "//components/embedder_support/android:content_view_java",
diff --git a/chrome/android/features/vr/DEPS b/chrome/android/features/vr/DEPS
index 96de841..2cbbf59 100644
--- a/chrome/android/features/vr/DEPS
+++ b/chrome/android/features/vr/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+chrome/browser/profiles/android/java",
   "+components/module_installer",
   "+content/public/android/java/src/org/chromium/content_public",
 ]
diff --git a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java
index 82fd068b..a4bce204 100644
--- a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java
+++ b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java
@@ -57,6 +57,7 @@
 import org.chromium.chrome.browser.infobar.SimpleConfirmInfoBarBuilder;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -788,7 +789,7 @@
         // TODO(ymalik): This call will connect to the Google Services api which can be slow. Can we
         // connect to it beforehand when we know that we'll be prompting for feedback?
         HelpAndFeedback.getInstance().showFeedback(((TabImpl) tab).getActivity(),
-                ((TabImpl) tab).getProfile(), tab.getUrlString(),
+                Profile.fromWebContents(tab.getWebContents()), tab.getUrlString(),
                 ContextUtils.getApplicationContext().getPackageName() + "." + FEEDBACK_REPORT_TYPE);
     }
 
diff --git a/chrome/android/feed/DEPS b/chrome/android/feed/DEPS
index f541e4b..62bbe4d6 100644
--- a/chrome/android/feed/DEPS
+++ b/chrome/android/feed/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+chrome/browser/android/lifecycle",
+  "+chrome/browser/profiles/android/java",
   "+chrome/browser/ui/messages/android/java",
   "+components/background_task_scheduler",
   "+components/feature_engagement",
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
index a2a75c7..fc4ba47 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
@@ -29,7 +29,9 @@
 import org.chromium.chrome.browser.ntp.SnapScrollHelper;
 import org.chromium.chrome.browser.ntp.snippets.SectionHeaderView;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.ui.native_page.NativePageHost;
@@ -51,11 +53,11 @@
      * @param tabModelSelector The {@link TabModelSelector} for the containing activity.
      * @param activityTabProvider Allows us to check if we are the current tab.
      * @param activityLifecycleDispatcher Allows us to subscribe to backgrounding events.
-     * @param tab The {@link TabImpl} that contains this new tab page.
+     * @param tab The {@link Tab} that contains this new tab page.
      */
     public FeedNewTabPage(ChromeActivity activity, NativePageHost nativePageHost,
             TabModelSelector tabModelSelector, ActivityTabProvider activityTabProvider,
-            ActivityLifecycleDispatcher activityLifecycleDispatcher, TabImpl tab) {
+            ActivityLifecycleDispatcher activityLifecycleDispatcher, Tab tab) {
         super(activity, nativePageHost, tabModelSelector, activityTabProvider,
                 activityLifecycleDispatcher, tab);
 
@@ -81,12 +83,13 @@
 
     @Override
     protected void initializeMainView(Context context, NativePageHost host) {
+        Profile profile = Profile.fromWebContents(mTab.getWebContents());
         ActionApi actionApi = new FeedActionHandler(mNewTabPageManager.getNavigationDelegate(),
                 FeedProcessScopeFactory.getFeedConsumptionObserver(),
                 FeedProcessScopeFactory.getFeedOfflineIndicator(),
-                OfflinePageBridge.getForProfile(((TabImpl) mTab).getProfile()),
+                OfflinePageBridge.getForProfile(profile),
                 FeedProcessScopeFactory.getFeedLoggingBridge(), ((TabImpl) mTab).getActivity(),
-                ((TabImpl) mTab).getProfile());
+                profile);
         LayoutInflater inflater = LayoutInflater.from(((TabImpl) mTab).getActivity());
         mNewTabPageLayout = (NewTabPageLayout) inflater.inflate(R.layout.new_tab_page_layout, null);
         SectionHeaderView sectionHeaderView = (SectionHeaderView) inflater.inflate(
diff --git a/chrome/android/java/DEPS b/chrome/android/java/DEPS
index 0067268..b373a57 100644
--- a/chrome/android/java/DEPS
+++ b/chrome/android/java/DEPS
@@ -4,6 +4,7 @@
   "+chrome/browser/android/lifecycle",
   "+chrome/browser/android/thin_webview/java",
   "+chrome/browser/flags/android",
+  "+chrome/browser/profiles/android/java",
   "+chrome/browser/share/android",
   "+chrome/browser/thumbnail/generator/android/java",
   "+chrome/browser/ui/android/appmenu",
diff --git a/chrome/android/java/res/layout/four_state_cookie_settings_preference.xml b/chrome/android/java/res/layout/four_state_cookie_settings_preference.xml
index c088ff7..1710cda 100644
--- a/chrome/android/java/res/layout/four_state_cookie_settings_preference.xml
+++ b/chrome/android/java/res/layout/four_state_cookie_settings_preference.xml
@@ -5,7 +5,7 @@
 
 <!-- Layout used by the FourStateCookieSettingsPreference. -->
 
-<FrameLayout
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
@@ -47,4 +47,16 @@
             app:descriptionText="@string/website_settings_category_cookie_block_addition" />
 
     </org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout>
-</FrameLayout>
+
+    <org.chromium.components.browser_ui.widget.text.TextViewWithCompoundDrawables
+        android:id="@+id/managed_view"
+        android:text="@string/managed_by_your_organization"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:gravity="center_vertical"
+        android:padding="16dp"
+        android:drawablePadding="10dp"
+        app:chromeDrawableTint="@color/default_icon_color"
+        android:textAppearance="@style/TextAppearance.TextMedium.Primary"/>
+</LinearLayout>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActionModeCallback.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActionModeCallback.java
index 0035ccc..459c25e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActionModeCallback.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActionModeCallback.java
@@ -20,6 +20,7 @@
 import org.chromium.chrome.browser.firstrun.FirstRunStatus;
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.omnibox.geo.GeolocationHeader;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabImpl;
@@ -151,7 +152,7 @@
                 searchText, ActionModeCallbackHelper.MAX_SEARCH_QUERY_LENGTH);
         if (TextUtils.isEmpty(query)) return;
 
-        TrackerFactory.getTrackerForProfile(((TabImpl) mTab).getProfile())
+        TrackerFactory.getTrackerForProfile(Profile.fromWebContents(mTab.getWebContents()))
                 .notifyEvent(EventConstants.WEB_SEARCH_PERFORMED);
         selector.openNewTab(generateUrlParamsForSearch(query),
                 TabLaunchType.FROM_LONGPRESS_FOREGROUND, mTab, mTab.isIncognito());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 8409b046..89d3918 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -125,7 +125,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabDelegateFactory;
 import org.chromium.chrome.browser.tab.TabHidingType;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tab.TabSelectionType;
 import org.chromium.chrome.browser.tab.TabState;
@@ -1983,8 +1982,8 @@
                     new OfflinePageUtils.TabOfflinePageLoadUrlDelegate(currentTab));
         } else if (id == R.id.translate_id) {
             RecordUserAction.record("MobileMenuTranslate");
-            Tracker tracker =
-                    TrackerFactory.getTrackerForProfile(((TabImpl) getActivityTab()).getProfile());
+            Tracker tracker = TrackerFactory.getTrackerForProfile(
+                    Profile.fromWebContents(getActivityTab().getWebContents()));
             tracker.notifyEvent(EventConstants.TRANSLATE_MENU_BUTTON_CLICKED);
             TranslateBridge.translateTabWhenReady(getActivityTab());
         } else if (id == R.id.print_id) {
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 7c7f565..8fd4f00 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -119,7 +119,6 @@
 import org.chromium.chrome.browser.tab.TabAssociatedApp;
 import org.chromium.chrome.browser.tab.TabCreationState;
 import org.chromium.chrome.browser.tab.TabDelegateFactory;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tab.TabRedirectHandler;
 import org.chromium.chrome.browser.tab.TabSelectionType;
@@ -2129,8 +2128,8 @@
         if (NavigationSheet.isEnabled()) {
             showFullHistoryOnNavigationSheet(tab);
         } else {
-            mNavigationPopup = new NavigationPopup(((TabImpl) tab).getProfile(), this,
-                    tab.getWebContents().getNavigationController(),
+            mNavigationPopup = new NavigationPopup(Profile.fromWebContents(tab.getWebContents()),
+                    this, tab.getWebContents().getNavigationController(),
                     NavigationPopup.Type.ANDROID_SYSTEM_BACK);
             mNavigationPopup.setOnDismissCallback(() -> mNavigationPopup = null);
             mNavigationPopup.show(findViewById(R.id.navigation_popup_anchor_stub));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/background_sync/BackgroundSyncPwaDetector.java b/chrome/android/java/src/org/chromium/chrome/browser/background_sync/BackgroundSyncPwaDetector.java
index aa7ef9f5..540f7cdd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/background_sync/BackgroundSyncPwaDetector.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/background_sync/BackgroundSyncPwaDetector.java
@@ -18,12 +18,20 @@
  */
 public class BackgroundSyncPwaDetector {
     /**
-     * Returns true if there's a PWA installed for the given origin, false otherwise.
+     * Returns true if there's at least one PWA installed for the given origin, false otherwise.
+     * Installed PWAs are associated with a scope, while permissions are attributed to an origin.
+     * Because we're only passed the requesting origin from the permissions infrastructure, we can't
+     * match the scope of installed PWAs to the exact URL of the permission request. We instead
+     * look for any installed PWA for the requesting origin. With this logic, if there's already a
+     * PWA installed for travel.google.com, and a request to register Periodic Background Sync comes
+     * in from maps.google.com, this method will return true and registration will succeed, provided
+     * other required conditions are met.
+     *
      * @param origin The origin for which to look for a PWA.
      */
     @CalledByNative
     public static boolean isPwaInstalled(String origin) {
-        return WebappRegistry.getInstance().hasWebApkForUrl(
+        return WebappRegistry.getInstance().hasAtLeastOneWebApkForOrigin(
                 origin.toLowerCase(Locale.getDefault()));
     }
 
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 07e7ce6..95f66f85 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
@@ -44,7 +44,6 @@
 import org.chromium.chrome.browser.tab.SadTab;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabCreationState;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tab.TabRedirectHandler;
 import org.chromium.chrome.browser.tab.TabSelectionType;
@@ -494,7 +493,8 @@
         mWasActivatedByTap = mSelectionController.getSelectionType() == SelectionType.TAP;
 
         Tab tab = mActivity.getActivityTab();
-        Tracker tracker = TrackerFactory.getTrackerForProfile(((TabImpl) tab).getProfile());
+        Tracker tracker =
+                TrackerFactory.getTrackerForProfile(Profile.fromWebContents(tab.getWebContents()));
         tracker.notifyEvent(mWasActivatedByTap
                         ? EventConstants.CONTEXTUAL_SEARCH_TRIGGERED_BY_TAP
                         : EventConstants.CONTEXTUAL_SEARCH_TRIGGERED_BY_LONGPRESS);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
index 620f702..af319ce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
@@ -201,7 +201,7 @@
         // is initialized.
         if (mNativeHelper == 0) {
             mNativeHelper = ContextualSearchTabHelperJni.get().init(
-                    ContextualSearchTabHelper.this, ((TabImpl) tab).getProfile());
+                    ContextualSearchTabHelper.this, Profile.fromWebContents(tab.getWebContents()));
         }
         if (mTemplateUrlObserver == null) {
             mTemplateUrlObserver = new TemplateUrlServiceObserver() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/cryptids/ProbabilisticCryptidRenderer.java b/chrome/android/java/src/org/chromium/chrome/browser/cryptids/ProbabilisticCryptidRenderer.java
index 89b7ca6..e47f8f1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/cryptids/ProbabilisticCryptidRenderer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/cryptids/ProbabilisticCryptidRenderer.java
@@ -7,6 +7,8 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Log;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 
 /**
  * Allows for cryptids to be displayed on the New Tab Page under certain probabilistic conditions.
@@ -25,15 +27,28 @@
      */
     public boolean shouldUseCryptidRendering() {
         // TODO: This should be disabled unless enabled by variations.
-        return getRandom() < calculateProbability(getLastRenderTimestampMillis(),
-                       System.currentTimeMillis(), getRenderingMoratoriumLengthMillis(),
-                       getRampUpLengthMillis(), getMaxProbability());
+        return getRandom() < calculateProbability();
+    }
+
+    /**
+     * Records the current timestamp as the last render time in the appropriate pref.
+     */
+    public void recordRenderEvent() {
+        recordRenderEvent(System.currentTimeMillis());
     }
 
     // Protected for testing
     protected long getLastRenderTimestampMillis() {
-        // TODO: Store last instance timestamp in a pref and return it.
-        return 0;
+        long lastRenderTimestamp = SharedPreferencesManager.getInstance().readLong(
+                ChromePreferenceKeys.CRYPTID_LAST_RENDER_TIMESTAMP,
+                /* defaultValue = */ 0);
+        if (lastRenderTimestamp == 0) {
+            // If no render has ever been recorded, set the timestamp such that the current time is
+            // the end of the moratorium period.
+            lastRenderTimestamp = System.currentTimeMillis() - getRenderingMoratoriumLengthMillis();
+            recordRenderEvent(lastRenderTimestamp);
+        }
+        return lastRenderTimestamp;
     }
 
     protected long getRenderingMoratoriumLengthMillis() {
@@ -55,6 +70,12 @@
         return (int) (Math.random() * DENOMINATOR);
     }
 
+    @VisibleForTesting
+    void recordRenderEvent(long timestamp) {
+        SharedPreferencesManager.getInstance().writeLong(
+                ChromePreferenceKeys.CRYPTID_LAST_RENDER_TIMESTAMP, timestamp);
+    }
+
     /**
      * Calculates the probability of display at a particular moment, based on various timestamp/
      * length factors. Roughly speaking, the probability starts at 0 after a rendering event,
@@ -90,4 +111,13 @@
         float fractionOfRampUp = (float) (currentTimestamp - windowStartTimestamp) / rampUpLength;
         return (int) Math.round(fractionOfRampUp * maxProbability);
     }
+
+    /**
+     * Convenience method calling the static method above using the appropriate values.
+     */
+    @VisibleForTesting
+    int calculateProbability() {
+        return calculateProbability(getLastRenderTimestampMillis(), System.currentTimeMillis(),
+                getRenderingMoratoriumLengthMillis(), getRampUpLengthMillis(), getMaxProbability());
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTaskDescriptionHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTaskDescriptionHelper.java
index a2cfa3f..f00fc42 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTaskDescriptionHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTaskDescriptionHelper.java
@@ -23,9 +23,9 @@
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.ssl.ChromeSecurityStateModelDelegate;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabThemeColorHelper;
 import org.chromium.chrome.browser.ui.favicon.FaviconHelper;
 import org.chromium.chrome.browser.webapps.WebappExtras;
@@ -269,8 +269,9 @@
         if (currentTab == null) return;
 
         final String currentUrl = currentTab.getUrlString();
-        mFaviconHelper.getLocalFaviconImageForURL(((TabImpl) currentTab).getProfile(),
-                currentTab.getUrlString(), 0, (image, iconUrl) -> {
+        mFaviconHelper.getLocalFaviconImageForURL(
+                Profile.fromWebContents(currentTab.getWebContents()), currentTab.getUrlString(), 0,
+                (image, iconUrl) -> {
                     if (mTabProvider.getTab() == null
                             || !TextUtils.equals(
                                     currentUrl, mTabProvider.getTab().getUrlString())) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
index 535df63..4cde001 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
@@ -210,8 +210,8 @@
         }
 
         if (BrowserStartupController.getInstance().isFullBrowserStarted()) {
-            Profile profile =
-                    (tab == null ? Profile.getLastUsedProfile() : ((TabImpl) tab).getProfile());
+            Profile profile = (tab == null ? Profile.getLastUsedProfile()
+                                           : Profile.fromWebContents(tab.getWebContents()));
             Tracker tracker = TrackerFactory.getTrackerForProfile(profile);
             tracker.notifyEvent(EventConstants.DOWNLOAD_HOME_OPENED);
         }
@@ -294,7 +294,7 @@
             // The download needs to be scheduled to happen at later time due to current network
             // error.
             final OfflinePageBridge bridge =
-                    OfflinePageBridge.getForProfile(((TabImpl) tab).getProfile());
+                    OfflinePageBridge.getForProfile(Profile.fromWebContents(tab.getWebContents()));
             bridge.scheduleDownload(tab.getWebContents(), OfflinePageBridge.ASYNC_NAMESPACE,
                     tab.getUrlString(), DownloadUiActionFlags.PROMPT_DUPLICATE, origin);
         } else {
@@ -303,7 +303,8 @@
             DownloadUtils.recordDownloadPageMetrics(tab);
         }
 
-        Tracker tracker = TrackerFactory.getTrackerForProfile(((TabImpl) tab).getProfile());
+        Tracker tracker =
+                TrackerFactory.getTrackerForProfile(Profile.fromWebContents(tab.getWebContents()));
         tracker.notifyEvent(EventConstants.DOWNLOAD_PAGE_STARTED);
     }
 
@@ -326,7 +327,7 @@
         // Download will only be allowed for the error page if download button is shown in the page.
         if (tab.isShowingErrorPage()) {
             final OfflinePageBridge bridge =
-                    OfflinePageBridge.getForProfile(((TabImpl) tab).getProfile());
+                    OfflinePageBridge.getForProfile(Profile.fromWebContents(tab.getWebContents()));
             return bridge.isShowingDownloadButtonInErrorPage(tab.getWebContents());
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
index 03cc84f..7bcb9395 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
@@ -28,7 +28,6 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.ui.favicon.RoundedIconGenerator;
 import org.chromium.chrome.browser.ui.native_page.BasicNativePage;
@@ -170,7 +169,7 @@
     /**
      * Create a new instance of the explore sites page.
      */
-    public ExploreSitesPage(ChromeActivity activity, NativePageHost host, TabImpl tab) {
+    public ExploreSitesPage(ChromeActivity activity, NativePageHost host, Tab tab) {
         super(host);
 
         mHost = host;
@@ -179,7 +178,7 @@
         mTitle = activity.getString(R.string.explore_sites_title);
         mView = (ViewGroup) activity.getLayoutInflater().inflate(
                 R.layout.explore_sites_page_layout, null);
-        mProfile = ((TabImpl) mTab).getProfile();
+        mProfile = Profile.fromWebContents(mTab.getWebContents());
 
         mDenseVariation = ExploreSitesBridge.getDenseVariation();
         int maxRows;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/IPHBubbleDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/IPHBubbleDelegateImpl.java
index d71cfc5..19b86e5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/IPHBubbleDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/IPHBubbleDelegateImpl.java
@@ -16,8 +16,8 @@
 import org.chromium.chrome.browser.infobar.IPHInfoBarSupport.PopupState;
 import org.chromium.chrome.browser.infobar.IPHInfoBarSupport.TrackerParameters;
 import org.chromium.chrome.browser.permissions.PermissionSettingsBridge;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
 import org.chromium.components.browser_ui.widget.textbubble.TextBubble;
 import org.chromium.components.feature_engagement.FeatureConstants;
@@ -35,7 +35,8 @@
 
     IPHBubbleDelegateImpl(Context context, Tab tab) {
         mContext = context;
-        mTracker = TrackerFactory.getTrackerForProfile(((TabImpl) tab).getProfile());
+        mTracker =
+                TrackerFactory.getTrackerForProfile(Profile.fromWebContents(tab.getWebContents()));
         mTab = tab;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaSessionTabHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaSessionTabHelper.java
index 078ac3a..99f6808 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaSessionTabHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaSessionTabHelper.java
@@ -20,9 +20,9 @@
 import org.chromium.chrome.browser.document.ChromeIntentUtil;
 import org.chromium.chrome.browser.metrics.MediaNotificationUma;
 import org.chromium.chrome.browser.metrics.MediaSessionUMA;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.tab.TabSelectionType;
 import org.chromium.chrome.browser.ui.favicon.LargeIconBridge;
@@ -561,7 +561,7 @@
         String pageUrl = webContents.getLastCommittedUrl();
         int size = MediaNotificationManager.MINIMAL_MEDIA_IMAGE_SIZE_PX;
         if (mLargeIconBridge == null) {
-            mLargeIconBridge = new LargeIconBridge(((TabImpl) mTab).getProfile());
+            mLargeIconBridge = new LargeIconBridge(Profile.fromWebContents(mTab.getWebContents()));
         }
         LargeIconBridge.LargeIconCallback callback = new LargeIconBridge.LargeIconCallback() {
             @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
index 5f38b1b..0c7ff63 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
@@ -26,8 +26,8 @@
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.ntp.RecentTabsManager;
 import org.chromium.chrome.browser.ntp.RecentTabsPage;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.ui.native_page.NativePage;
@@ -60,12 +60,11 @@
             if (ChromeFeatureList.isEnabled(ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS)) {
                 return new FeedNewTabPage(activity,
                         new TabShim(tab, activity.getFullscreenManager()), tabModelSelector,
-                        activityTabProvider, activityLifecycleDispatcher, (TabImpl) tab);
+                        activityTabProvider, activityLifecycleDispatcher, tab);
             }
 
             return new NewTabPage(activity, new TabShim(tab, activity.getFullscreenManager()),
-                    tabModelSelector, activityTabProvider, activityLifecycleDispatcher,
-                    (TabImpl) tab);
+                    tabModelSelector, activityTabProvider, activityLifecycleDispatcher, tab);
         }
 
         protected NativePage buildBookmarksPage(ChromeActivity activity, Tab tab) {
@@ -78,7 +77,7 @@
 
         protected NativePage buildExploreSitesPage(ChromeActivity activity, Tab tab) {
             return new ExploreSitesPage(
-                    activity, new TabShim(tab, activity.getFullscreenManager()), (TabImpl) tab);
+                    activity, new TabShim(tab, activity.getFullscreenManager()), tab);
         }
 
         protected NativePage buildHistoryPage(ChromeActivity activity, Tab tab) {
@@ -86,8 +85,8 @@
         }
 
         protected NativePage buildRecentTabsPage(ChromeActivity activity, Tab tab) {
-            RecentTabsManager recentTabsManager =
-                    new RecentTabsManager(tab, ((TabImpl) tab).getProfile(), activity);
+            RecentTabsManager recentTabsManager = new RecentTabsManager(
+                    tab, Profile.fromWebContents(tab.getWebContents()), activity);
             return new RecentTabsPage(
                     activity, recentTabsManager, new TabShim(tab, activity.getFullscreenManager()));
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index 566fcbf5..832398c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -86,7 +86,7 @@
     private static final String NAVIGATION_ENTRY_SCROLL_POSITION_KEY = "NewTabPageScrollPosition";
     public static final String CONTEXT_MENU_USER_ACTION_PREFIX = "Suggestions";
 
-    protected final TabImpl mTab;
+    protected final Tab mTab;
     private final ActivityTabProvider mActivityTabProvider;
     private final ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
 
@@ -273,7 +273,7 @@
      */
     public NewTabPage(ChromeActivity activity, NativePageHost nativePageHost,
             TabModelSelector tabModelSelector, ActivityTabProvider activityTabProvider,
-            ActivityLifecycleDispatcher activityLifecycleDispatcher, TabImpl tab) {
+            ActivityLifecycleDispatcher activityLifecycleDispatcher, Tab tab) {
         mConstructedTimeNs = System.nanoTime();
         TraceEvent.begin(TAG);
 
@@ -281,7 +281,7 @@
         mActivityLifecycleDispatcher = activityLifecycleDispatcher;
 
         mTab = tab;
-        Profile profile = mTab.getProfile();
+        Profile profile = Profile.fromWebContents(mTab.getWebContents());
 
         SuggestionsDependencyFactory depsFactory = SuggestionsDependencyFactory.getInstance();
         SuggestionsSource suggestionsSource = depsFactory.createSuggestionSource(profile);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java
index 8ae7773..f163ce68 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java
@@ -34,7 +34,6 @@
 import org.chromium.chrome.browser.tab.SadTab;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabCreationState;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
@@ -157,8 +156,8 @@
             WebContents webContents = tab.getWebContents();
             if (webContents == null) return false;
 
-            OfflinePageBridge offlinePageBridge =
-                    getInstance().getOfflinePageBridge(((TabImpl) tab).getProfile());
+            OfflinePageBridge offlinePageBridge = getInstance().getOfflinePageBridge(
+                    Profile.fromWebContents(tab.getWebContents()));
             if (offlinePageBridge == null) return false;
 
             return offlinePageBridge.isOfflinePage(webContents);
@@ -167,7 +166,7 @@
         @Override
         public boolean isShowingOfflinePreview(Tab tab) {
             OfflinePageBridge offlinePageBridge =
-                    getOfflinePageBridge(((TabImpl) tab).getProfile());
+                    getOfflinePageBridge(Profile.fromWebContents(tab.getWebContents()));
             if (offlinePageBridge == null) return false;
             return offlinePageBridge.isShowingOfflinePreview(tab.getWebContents());
         }
@@ -267,7 +266,7 @@
         if (shouldSkipSavingTabOffline(tab)) return;
 
         OfflinePageBridge offlinePageBridge =
-                getInstance().getOfflinePageBridge(((TabImpl) tab).getProfile());
+                getInstance().getOfflinePageBridge(Profile.fromWebContents(tab.getWebContents()));
         if (offlinePageBridge == null) return;
 
         WebContents webContents = tab.getWebContents();
@@ -340,7 +339,7 @@
      */
     public static boolean saveAndSharePage(Tab tab, final Callback<ShareParams> shareCallback) {
         OfflinePageBridge offlinePageBridge =
-                getInstance().getOfflinePageBridge(((TabImpl) tab).getProfile());
+                getInstance().getOfflinePageBridge(Profile.fromWebContents(tab.getWebContents()));
 
         if (offlinePageBridge == null) {
             Log.e(TAG, "Unable to share current tab as an offline page.");
@@ -418,7 +417,7 @@
         }
 
         OfflinePageBridge offlinePageBridge =
-                getInstance().getOfflinePageBridge(((TabImpl) tab).getProfile());
+                getInstance().getOfflinePageBridge(Profile.fromWebContents(tab.getWebContents()));
 
         if (offlinePageBridge == null) {
             Log.e(TAG, "Unable to share current tab as an offline page.");
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/CredentialLeakDialogBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/CredentialLeakDialogBridge.java
index ebcd5acb..2c70e04 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/CredentialLeakDialogBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/CredentialLeakDialogBridge.java
@@ -9,7 +9,6 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.help.HelpAndFeedback;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 
@@ -78,7 +77,8 @@
     private void showHelpArticle() {
         if (mActivity.get() == null) return;
 
-        Profile profile = ((TabImpl) mActivity.get().getActivityTabProvider().get()).getProfile();
+        Profile profile = Profile.fromWebContents(
+                mActivity.get().getActivityTabProvider().get().getWebContents());
         HelpAndFeedback.getInstance().show(mActivity.get(),
                 mActivity.get().getString(R.string.help_context_password_leak_detection), profile,
                 null);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java
index e12cdcd5..5a73d55 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java
@@ -35,7 +35,6 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Comparator;
-import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
 import java.util.PriorityQueue;
@@ -78,6 +77,9 @@
     // A worker task for asynchronously handling video decode requests.
     private DecodeVideoTask mWorkerTask;
 
+    // Keeps track of the last decoding ordinal issued.
+    static int sLastDecodingOrdinal = 0;
+
     // A callback to use for testing to see if decoder is ready.
     static DecoderStatusCallback sStatusCallbackForTesting;
 
@@ -151,8 +153,9 @@
         // ignored for non-videos.
         final boolean mFirstFrame;
 
-        // When the request was added.
-        final Date mCreateTime;
+        // An ordinal used to enforce FIFO decoding, in the case where all other things are equal
+        // (when it comes to determining which record to decode first).
+        private int mRequestOrdinal;
 
         // The callback to use to communicate the results of the decoding.
         final ImagesDecodedCallback mCallback;
@@ -168,7 +171,7 @@
             mFullWidth = fullWidth;
             mFileType = fileType;
             mFirstFrame = firstFrame;
-            mCreateTime = new Date();
+            mRequestOrdinal = sLastDecodingOrdinal++;
             mCallback = callback;
         }
     }
@@ -190,7 +193,7 @@
         // The two requests share the same file type, or are identical video requests (both
         // requesting first frame or both requesting additional frames) so they can be considered
         // equal. Go with first in first out.
-        return r1.mCreateTime.compareTo(r2.mCreateTime);
+        return r1.mRequestOrdinal - r2.mRequestOrdinal;
     };
 
     // A queue of pending requests.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java
index 2e69cc07..63026665 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java
@@ -14,7 +14,6 @@
 import org.chromium.chrome.browser.omnibox.UrlBarData;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
 import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.security_state.ConnectionSecurityLevel;
@@ -62,8 +61,7 @@
 
     @Override
     public Profile getProfile() {
-        if (mTab == null) return null;
-        return ((TabImpl) mTab).getProfile();
+        return mTab != null ? Profile.fromWebContents(mTab.getWebContents()) : null;
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/FourStateCookieSettingsPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/FourStateCookieSettingsPreference.java
index 1e9db30..2d63223 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/FourStateCookieSettingsPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/FourStateCookieSettingsPreference.java
@@ -5,14 +5,23 @@
 package org.chromium.chrome.browser.site_settings;
 
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.RadioGroup;
 
+import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceViewHolder;
 
+import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.settings.ManagedPreferencesUtils;
 import org.chromium.components.browser_ui.widget.RadioButtonWithDescription;
+import org.chromium.components.browser_ui.widget.text.TextViewWithCompoundDrawables;
+import org.chromium.components.content_settings.CookieControlsMode;
+
+import java.util.Arrays;
 
 /**
  * A 4-state radio group Preference used for the Cookies subpage of SiteSettings.
@@ -27,12 +36,22 @@
         BLOCK
     }
 
+    // Signals used to determine the view and button states.
+    private boolean mCookiesContentSettingEnforced;
+    private boolean mThirdPartyBlockingEnforced;
+    private boolean mAllowCookies;
+    private boolean mBlockThirdPartyCookies;
+    // An enum indicating when to block third-party cookies.
+    private @CookieControlsMode int mCookieControlsMode;
     private CookieSettingsState mState = CookieSettingsState.UNINITIALIZED;
-    private RadioButtonWithDescription mAllow;
-    private RadioButtonWithDescription mBlockThirdPartyIncognito;
-    private RadioButtonWithDescription mBlockThirdParty;
-    private RadioButtonWithDescription mBlock;
+
+    // UI Elements.
+    private RadioButtonWithDescription mAllowButton;
+    private RadioButtonWithDescription mBlockThirdPartyIncognitoButton;
+    private RadioButtonWithDescription mBlockThirdPartyButton;
+    private RadioButtonWithDescription mBlockButton;
     private RadioGroup mRadioGroup;
+    private TextViewWithCompoundDrawables mManagedView;
 
     public FourStateCookieSettingsPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -46,14 +65,24 @@
     }
 
     /**
-     * Sets the state of this Preference.
-     * @param state The CookieSettingsState to set the preference to.
+     * Sets the cookie settings state and updates the radio buttons.
+     * @param cookiesContentSettingEnforced Whether the cookies content setting is enforced.
+     * @param thirdPartyBlockingEnforced Whether third-party blocking is enforced.
+     * @param allowCookies Whether the cookies content setting is enabled.
+     * @param blockThirdPartyCookies Whether third-party blocking is enabled.
+     * @param cookieControlsMode The CookieControlsMode enum for the current cookie settings state.
      */
-    public void setState(CookieSettingsState state) {
-        mState = state;
+    public void setState(boolean cookiesContentSettingEnforced, boolean thirdPartyBlockingEnforced,
+            boolean allowCookies, boolean blockThirdPartyCookies,
+            @CookieControlsMode int cookieControlsMode) {
+        mCookiesContentSettingEnforced = cookiesContentSettingEnforced;
+        mThirdPartyBlockingEnforced = thirdPartyBlockingEnforced;
+        mAllowCookies = allowCookies;
+        mBlockThirdPartyCookies = blockThirdPartyCookies;
+        mCookieControlsMode = cookieControlsMode;
 
         if (mRadioGroup != null) {
-            setRadioButton();
+            setRadioButtons();
         }
     }
 
@@ -66,13 +95,13 @@
 
     @Override
     public void onCheckedChanged(RadioGroup group, int checkedId) {
-        if (mAllow.isChecked()) {
+        if (mAllowButton.isChecked()) {
             mState = CookieSettingsState.ALLOW;
-        } else if (mBlockThirdPartyIncognito.isChecked()) {
+        } else if (mBlockThirdPartyIncognitoButton.isChecked()) {
             mState = CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO;
-        } else if (mBlockThirdParty.isChecked()) {
+        } else if (mBlockThirdPartyButton.isChecked()) {
             mState = CookieSettingsState.BLOCK_THIRD_PARTY;
-        } else if (mBlock.isChecked()) {
+        } else if (mBlockButton.isChecked()) {
             mState = CookieSettingsState.BLOCK;
         }
 
@@ -83,39 +112,119 @@
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
 
-        mAllow = (RadioButtonWithDescription) holder.findViewById(R.id.allow);
-        mBlockThirdPartyIncognito =
+        mAllowButton = (RadioButtonWithDescription) holder.findViewById(R.id.allow);
+        mBlockThirdPartyIncognitoButton =
                 (RadioButtonWithDescription) holder.findViewById(R.id.block_third_party_incognito);
-        mBlockThirdParty = (RadioButtonWithDescription) holder.findViewById(R.id.block_third_party);
-        mBlock = (RadioButtonWithDescription) holder.findViewById(R.id.block);
+        mBlockThirdPartyButton =
+                (RadioButtonWithDescription) holder.findViewById(R.id.block_third_party);
+        mBlockButton = (RadioButtonWithDescription) holder.findViewById(R.id.block);
         mRadioGroup = (RadioGroup) holder.findViewById(R.id.radio_button_layout);
         mRadioGroup.setOnCheckedChangeListener(this);
 
-        if (mState != CookieSettingsState.UNINITIALIZED) {
-            setRadioButton();
+        mManagedView = (TextViewWithCompoundDrawables) holder.findViewById(R.id.managed_view);
+        Drawable[] drawables = mManagedView.getCompoundDrawablesRelative();
+        Drawable managedIcon = ApiCompatibilityUtils.getDrawable(getContext().getResources(),
+                ManagedPreferencesUtils.getManagedByEnterpriseIconId());
+        mManagedView.setCompoundDrawablesRelativeWithIntrinsicBounds(
+                managedIcon, drawables[1], drawables[2], drawables[3]);
+
+        setRadioButtons();
+    }
+
+    private RadioButtonWithDescription getActiveRadioButton() {
+        // These conditions only check the preference combinations that deterministically decide
+        // your cookie settings state. In the future we would refactor the backend preferences to
+        // reflect the only possible states you can be in
+        // (Allow/BlockThirdPartyIncognito/BlockThirdParty/Block), instead of using this
+        // combination of multiple signals.
+        if (!mAllowCookies) {
+            mState = CookieSettingsState.BLOCK;
+            return mBlockButton;
+        } else if (mBlockThirdPartyCookies || mCookieControlsMode == CookieControlsMode.ON) {
+            // Having CookieControlsMode.ON is equivalent to having the BLOCK_THIRD_PARTY_COOKIES
+            // pref set to enabled, because it means third party cookie blocking is always on.
+            mState = CookieSettingsState.BLOCK_THIRD_PARTY;
+            return mBlockThirdPartyButton;
+        } else if (mCookieControlsMode == CookieControlsMode.INCOGNITO_ONLY) {
+            mState = CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO;
+            return mBlockThirdPartyIncognitoButton;
+        } else {
+            mState = CookieSettingsState.ALLOW;
+            return mAllowButton;
         }
     }
 
-    private void setRadioButton() {
-        RadioButtonWithDescription button = null;
+    private void setRadioButtons() {
+        mAllowButton.setEnabled(true);
+        mBlockThirdPartyIncognitoButton.setEnabled(true);
+        mBlockThirdPartyButton.setEnabled(true);
+        mBlockButton.setEnabled(true);
+        for (RadioButtonWithDescription button : getEnforcedButtons()) {
+            button.setEnabled(false);
+        }
+        mManagedView.setVisibility((mCookiesContentSettingEnforced || mThirdPartyBlockingEnforced)
+                        ? View.VISIBLE
+                        : View.GONE);
 
-        switch (mState) {
+        RadioButtonWithDescription button = getActiveRadioButton();
+        // Always want to enable the selected option.
+        button.setEnabled(true);
+        button.setChecked(true);
+    }
+
+    /**
+     * A helper function to return a button array from a variable number of arguments.
+     */
+    private RadioButtonWithDescription[] buttons(RadioButtonWithDescription... args) {
+        return args;
+    }
+
+    /**
+     * @return An array of radio buttons that have to be disabled as they can't be selected due to
+     *         policy restrictions.
+     */
+    private RadioButtonWithDescription[] getEnforcedButtons() {
+        if (!mCookiesContentSettingEnforced && !mThirdPartyBlockingEnforced) {
+            return buttons();
+        }
+        if (mCookiesContentSettingEnforced && mThirdPartyBlockingEnforced) {
+            return buttons(mAllowButton, mBlockThirdPartyIncognitoButton, mBlockThirdPartyButton,
+                    mBlockButton);
+        }
+        if (mCookiesContentSettingEnforced) {
+            if (mAllowCookies) {
+                return buttons(mBlockButton);
+            } else {
+                return buttons(mAllowButton, mBlockThirdPartyIncognitoButton,
+                        mBlockThirdPartyButton, mBlockButton);
+            }
+        }
+        if (mBlockThirdPartyCookies) {
+            return buttons(mAllowButton, mBlockThirdPartyIncognitoButton);
+        } else {
+            return buttons(mBlockThirdPartyIncognitoButton, mBlockThirdPartyButton);
+        }
+    }
+
+    @VisibleForTesting
+    public boolean isStateEnforced(CookieSettingsState state) {
+        RadioButtonWithDescription button;
+        switch (state) {
             case ALLOW:
-                button = mAllow;
+                button = mAllowButton;
                 break;
             case BLOCK_THIRD_PARTY_INCOGNITO:
-                button = mBlockThirdPartyIncognito;
+                button = mBlockThirdPartyIncognitoButton;
                 break;
             case BLOCK_THIRD_PARTY:
-                button = mBlockThirdParty;
+                button = mBlockThirdPartyButton;
                 break;
             case BLOCK:
-                button = mBlock;
+                button = mBlockButton;
                 break;
             default:
-                return;
+                return false;
         }
-
-        button.setChecked(true);
+        return Arrays.asList(getEnforcedButtons()).contains(button);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleCategorySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleCategorySettings.java
index 80602c2..dc163ca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleCategorySettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleCategorySettings.java
@@ -583,6 +583,13 @@
         PrefServiceBridge.getInstance().setInteger(Pref.COOKIE_CONTROLS_MODE, mode);
         PrefServiceBridge.getInstance().setBoolean(
                 Pref.BLOCK_THIRD_PARTY_COOKIES, mode == CookieControlsMode.ON);
+
+        // Re-configure the FourStateCookieToggle in order to avoid having stale state variables
+        // within the FourStateCookieSettingsPreference. These variables determine the checked and
+        // enabled states of each of the buttons.
+        configureFourStateCookieToggle(
+                (FourStateCookieSettingsPreference) getPreferenceScreen().findPreference(
+                        FOUR_STATE_COOKIE_TOGGLE_KEY));
     }
 
     private String getAddExceptionDialogMessage() {
@@ -1027,29 +1034,11 @@
     private void configureFourStateCookieToggle(
             FourStateCookieSettingsPreference fourStateCookieToggle) {
         fourStateCookieToggle.setOnPreferenceChangeListener(this);
-        // These conditions only check the preference combinations that deterministically decide
-        // your cookie settings state. In the future we would refactor the backend preferences to
-        // reflect the only possible states you can be in
-        // (Allow/BlockThirdPartyIncognito/BlockThirdParty/Block), instead of using this
-        // combination of multiple signals.
-        CookieSettingsState state;
-        if (!WebsitePreferenceBridge.isCategoryEnabled(ContentSettingsType.COOKIES)) {
-            state = CookieSettingsState.BLOCK;
-        } else if (PrefServiceBridge.getInstance().getBoolean(Pref.BLOCK_THIRD_PARTY_COOKIES)
-                || PrefServiceBridge.getInstance().getInteger(Pref.COOKIE_CONTROLS_MODE)
-                        == CookieControlsMode.ON) {
-            // Having CookieControlsMode.ON is equivalent to having the BLOCK_THIRD_PARTY_COOKIES
-            // pref set to enabled, because it means third party cookie blocking is always on.
-            state = CookieSettingsState.BLOCK_THIRD_PARTY;
-        } else if (PrefServiceBridge.getInstance().getInteger(Pref.COOKIE_CONTROLS_MODE)
-                == CookieControlsMode.INCOGNITO_ONLY) {
-            state = CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO;
-        } else {
-            state = CookieSettingsState.ALLOW;
-        }
-
-        // TODO(crbug.com/1060118): Add managed state for third-party cookie blocking.
-        fourStateCookieToggle.setState(state);
+        fourStateCookieToggle.setState(mCategory.isManaged(),
+                PrefServiceBridge.getInstance().isManagedPreference(Pref.BLOCK_THIRD_PARTY_COOKIES),
+                WebsitePreferenceBridge.isCategoryEnabled(ContentSettingsType.COOKIES),
+                PrefServiceBridge.getInstance().getBoolean(Pref.BLOCK_THIRD_PARTY_COOKIES),
+                PrefServiceBridge.getInstance().getInteger(Pref.COOKIE_CONTROLS_MODE));
     }
 
     private void configureTriStateToggle(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/tab/DEPS
index ba05b42..4172edd2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/DEPS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/DEPS
@@ -19,9 +19,10 @@
   "+components/browser_ui/styles/android",
   "+components/browser_ui/widget/android",
   "+content/public/android/java/src/org/chromium/content_public",
-  "+chrome/android/public/crypto/java/src/org/chromium/chrome/browser/crypto/CipherFactory.java",
+  "+chrome/browser/android/crypto/java/src/org/chromium/chrome/browser/crypto/CipherFactory.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabbedModeTabPersistencePolicy.java",
-  "+chrome/android/public/profiles/java/src/org/chromium/chrome/browser/profiles/Profile.java",
+  "+chrome/android/public/crypto/java/src/org/chromium/chrome/browser/crypto/CipherFactory.java",
+  "+chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/Profile.java",
 ]
 
 specific_include_rules = {
@@ -42,7 +43,6 @@
     "+chrome/android/java/src/org/chromium/chrome/browser/prerender/ExternalPrerenderHandler.java",
     "+chrome/android/java/src/org/chromium/chrome/browser/rlz/RevenueStats.java",
     "+chrome/android/java/src/org/chromium/chrome/browser/night_mode",
-    "+chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/Profile.java",
     "+chrome/browser/util/android/java/src/org/chromium/chrome/browser/util/UrlConstants.java",
 
     "-components",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/SadTab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/SadTab.java
index 63df664..7708f85 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/SadTab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/SadTab.java
@@ -23,6 +23,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.help.HelpAndFeedback;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.ui_metrics.SadTabEvent;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
@@ -90,7 +91,8 @@
                 Activity activity = mTab.getWindowAndroid().getActivity().get();
                 assert activity != null;
                 HelpAndFeedback.getInstance().show(activity,
-                        activity.getString(R.string.help_context_sad_tab), mTab.getProfile(), null);
+                        activity.getString(R.string.help_context_sad_tab),
+                        Profile.fromWebContents(mTab.getWebContents()), null);
             }
         };
 
@@ -98,8 +100,9 @@
             @Override
             public void run() {
                 if (showSendFeedbackView) {
+                    Profile profile = Profile.fromWebContents(mTab.getWebContents());
                     mTab.getActivity().startHelpAndFeedback(
-                            mTab.getUrlString(), "MobileSadTabFeedback", mTab.getProfile());
+                            mTab.getUrlString(), "MobileSadTabFeedback", profile);
                 } else {
                     mTab.reload();
                 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
index 1d446a4..608ed6c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
@@ -35,7 +35,6 @@
 import org.chromium.chrome.browser.night_mode.NightModeUtils;
 import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
 import org.chromium.chrome.browser.prerender.ExternalPrerenderHandler;
-import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.rlz.RevenueStats;
 import org.chromium.chrome.browser.tab.TabState.WebContentsState;
 import org.chromium.chrome.browser.ui.TabObscuringHandler;
@@ -708,13 +707,6 @@
     }
 
     /**
-     * @return The profile associated with this tab.
-     */
-    public Profile getProfile() {
-        return Profile.fromWebContents(getWebContents());
-    }
-
-    /**
      * This is used to change how this tab related to other tabs.
      * @param rootId New relationship id to be set.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
index 7cc067f..199e7d5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
@@ -104,8 +104,8 @@
         // from the {@link Tab}.
         if (tabImpl.isInitialized()) {
             TabState.WebContentsState webContentsState = TabState.getWebContentsState(tabImpl);
-            PersistedTabDataConfiguration config =
-                    PersistedTabDataConfiguration.get(CriticalPersistedTabData.class);
+            PersistedTabDataConfiguration config = PersistedTabDataConfiguration.get(
+                    CriticalPersistedTabData.class, tab.isIncognito());
             CriticalPersistedTabData criticalPersistedTabData = new CriticalPersistedTabData(tab,
                     tab.getParentId(), tabImpl.getRootId(), tab.getTimestampMillis(),
                     webContentsState != null
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/state/EncryptedFilePersistedTabDataStorage.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/state/EncryptedFilePersistedTabDataStorage.java
new file mode 100644
index 0000000..a87aedb
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/state/EncryptedFilePersistedTabDataStorage.java
@@ -0,0 +1,59 @@
+// 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.tab.state;
+
+import org.chromium.base.Callback;
+import org.chromium.base.Log;
+import org.chromium.chrome.browser.crypto.CipherFactory;
+
+import java.util.Locale;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+
+/**
+ * Implements {@link PersistedTabDataStorage} but encrypts and decrypts
+ * as data is stored and retrieved respectively.
+ */
+public class EncryptedFilePersistedTabDataStorage extends FilePersistedTabDataStorage {
+    private static final String TAG = "EFPTDS";
+
+    @Override
+    public void save(int tabId, String dataId, byte[] data) {
+        super.save(tabId, dataId, encrypt(data));
+    }
+
+    @Override
+    public void restore(int tabId, String dataId, Callback<byte[]> callback) {
+        super.restore(tabId, dataId, (data) -> { callback.onResult(decrypt(data)); });
+    }
+
+    private static byte[] decrypt(byte[] data) {
+        try {
+            if (data == null) {
+                return null;
+            }
+            return CipherFactory.getInstance().getCipher(Cipher.DECRYPT_MODE).doFinal(data);
+        } catch (BadPaddingException | IllegalBlockSizeException e) {
+            Log.e(TAG,
+                    String.format(
+                            Locale.ENGLISH, "Error encrypting data. Details: %s", e.getMessage()));
+            return null;
+        }
+    }
+
+    private static byte[] encrypt(byte[] data) {
+        Cipher cipher = CipherFactory.getInstance().getCipher(Cipher.ENCRYPT_MODE);
+        try {
+            return cipher.doFinal(data);
+        } catch (BadPaddingException | IllegalBlockSizeException e) {
+            Log.e(TAG,
+                    String.format(Locale.ENGLISH, "Problem encrypting data. Details: %s",
+                            e.getMessage()));
+            return null;
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/state/FilePersistedTabDataStorage.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/state/FilePersistedTabDataStorage.java
index 9b24393..63c2900 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/state/FilePersistedTabDataStorage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/state/FilePersistedTabDataStorage.java
@@ -11,8 +11,9 @@
 import org.chromium.base.Callback;
 import org.chromium.base.Log;
 import org.chromium.base.StreamUtil;
-import org.chromium.chrome.browser.crypto.CipherFactory;
+import org.chromium.base.task.PostTask;
 import org.chromium.chrome.browser.tabmodel.TabbedModeTabPersistencePolicy;
+import org.chromium.content_public.browser.UiThreadTaskTraits;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -20,10 +21,6 @@
 import java.io.IOException;
 import java.util.Locale;
 
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-
 /**
  * {@link PersistedTabDataStorage} which uses a file for the storage
  */
@@ -31,24 +28,12 @@
     private static final String TAG = "FilePTDS";
 
     @Override
-    public void save(int tabId, boolean isEncrypted, String dataId, byte[] data) {
+    public void save(int tabId, String dataId, byte[] data) {
         // TODO(crbug.com/1059636) these should be queued and executed on a background thread
         // TODO(crbug.com/1059637) we should introduce a retry mechanisms
-        // TODO(crbug.com/1059638) abstract out encrypt/decrypt
-        if (isEncrypted) {
-            Cipher cipher = CipherFactory.getInstance().getCipher(Cipher.ENCRYPT_MODE);
-            try {
-                data = cipher.doFinal(data);
-            } catch (BadPaddingException | IllegalBlockSizeException e) {
-                Log.e(TAG,
-                        String.format(Locale.ENGLISH, "Problem encrypting data. Details: %s",
-                                e.getMessage()));
-                return;
-            }
-        }
         FileOutputStream outputStream = null;
         try {
-            outputStream = new FileOutputStream(getFile(tabId, isEncrypted, dataId));
+            outputStream = new FileOutputStream(getFile(tabId, dataId));
             outputStream.write(data);
         } catch (FileNotFoundException e) {
             Log.e(TAG,
@@ -68,41 +53,32 @@
     }
 
     @Override
-    public void restore(int tabId, boolean isEncrypted, String dataId, Callback<byte[]> callback) {
-        File file = getFile(tabId, isEncrypted, dataId);
+    public void restore(int tabId, String dataId, Callback<byte[]> callback) {
+        File file = getFile(tabId, dataId);
         try {
             AtomicFile atomicFile = new AtomicFile(file);
             byte[] res = atomicFile.readFully();
-            if (isEncrypted) {
-                Cipher cipher = CipherFactory.getInstance().getCipher(Cipher.DECRYPT_MODE);
-                res = cipher.doFinal(res);
-            }
-            callback.onResult(res);
+            PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> { callback.onResult(res); });
         } catch (FileNotFoundException e) {
             Log.e(TAG,
                     String.format(Locale.ENGLISH,
                             "FileNotFoundException while attempting to restore "
                                     + " for Tab %d. Details: %s",
                             tabId, e.getMessage()));
-            callback.onResult(null);
+            PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> { callback.onResult(null); });
         } catch (IOException e) {
             Log.e(TAG,
                     String.format(Locale.ENGLISH,
                             "IOException while attempting to restore for Tab "
                                     + "%d. Details: %s",
                             tabId, e.getMessage()));
-            callback.onResult(null);
-        } catch (BadPaddingException | IllegalBlockSizeException e) {
-            Log.e(TAG,
-                    String.format(
-                            Locale.ENGLISH, "Error encrypting data. Details: %s", e.getMessage()));
-            callback.onResult(null);
+            PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> { callback.onResult(null); });
         }
     }
 
     @Override
-    public void delete(int tabId, boolean isEncrypted, String dataId) {
-        File file = getFile(tabId, isEncrypted, dataId);
+    public void delete(int tabId, String dataId) {
+        File file = getFile(tabId, dataId);
         if (!file.exists()) {
             return;
         }
@@ -113,9 +89,8 @@
     }
 
     @VisibleForTesting
-    protected File getFile(int tabId, boolean isEncrypted, String dataId) {
-        String encryptedId = isEncrypted ? "Encrypted" : "NotEncrypted";
+    protected File getFile(int tabId, String dataId) {
         return new File(TabbedModeTabPersistencePolicy.getOrCreateTabbedModeStateDirectory(),
-                String.format(Locale.ENGLISH, "%d%s%s", tabId, encryptedId, dataId));
+                String.format(Locale.ENGLISH, "%d%s", tabId, dataId));
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/state/PersistedTabData.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/state/PersistedTabData.java
index 3b396f5..0a15901f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/state/PersistedTabData.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/state/PersistedTabData.java
@@ -74,8 +74,9 @@
             callback.onResult(persistedTabDataFromTab);
             return;
         }
-        PersistedTabDataConfiguration config = PersistedTabDataConfiguration.get(clazz);
-        config.storage.restore(tab.getId(), tab.isIncognito(), config.id, (data) -> {
+        PersistedTabDataConfiguration config =
+                PersistedTabDataConfiguration.get(clazz, tab.isIncognito());
+        config.storage.restore(tab.getId(), config.id, (data) -> {
             T persistedTabData;
             if (data == null) {
                 persistedTabData = supplier.get();
@@ -116,8 +117,7 @@
      */
     @VisibleForTesting
     protected void save() {
-        mPersistedTabDataStorage.save(
-                mTab.getId(), mTab.isIncognito(), mPersistedTabDataId, serialize());
+        mPersistedTabDataStorage.save(mTab.getId(), mPersistedTabDataId, serialize());
     }
 
     /**
@@ -138,7 +138,7 @@
      * @param profile profile
      */
     protected void delete() {
-        mPersistedTabDataStorage.delete(mTab.getId(), mTab.isIncognito(), mPersistedTabDataId);
+        mPersistedTabDataStorage.delete(mTab.getId(), mPersistedTabDataId);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/state/PersistedTabDataConfiguration.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/state/PersistedTabDataConfiguration.java
index 09d59730..038d926 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/state/PersistedTabDataConfiguration.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/state/PersistedTabDataConfiguration.java
@@ -14,14 +14,18 @@
 public enum PersistedTabDataConfiguration {
     // TODO(crbug.com/1059650) investigate should this go in the app code?
     // Also investigate if the storage instance should be shared.
-    CRITICAL_PERSISTED_TAB_DATA("CPTD", new FilePersistedTabDataStorage());
+    CRITICAL_PERSISTED_TAB_DATA("CPTD", new FilePersistedTabDataStorage()),
+    ENCRYPTED_CRITICAL_PERSISTED_TAB_DATA("ECPTD", new EncryptedFilePersistedTabDataStorage());
 
     private static final Map<Class<? extends PersistedTabData>, PersistedTabDataConfiguration>
             sLookup = new HashMap<>();
+    private static final Map<Class<? extends PersistedTabData>, PersistedTabDataConfiguration>
+            sEncryptedLookup = new HashMap<>();
 
     static {
         // TODO(crbug.com/1060187) remove static initializer and initialization lazy
         sLookup.put(CriticalPersistedTabData.class, CRITICAL_PERSISTED_TAB_DATA);
+        sEncryptedLookup.put(CriticalPersistedTabData.class, ENCRYPTED_CRITICAL_PERSISTED_TAB_DATA);
     }
 
     public final String id;
@@ -39,7 +43,11 @@
     /**
      * Acquire {@link PersistedTabDataConfiguration} for a given {@link PersistedTabData} class
      */
-    public static PersistedTabDataConfiguration get(Class<? extends PersistedTabData> clazz) {
+    public static PersistedTabDataConfiguration get(
+            Class<? extends PersistedTabData> clazz, boolean isEncrypted) {
+        if (isEncrypted) {
+            return sEncryptedLookup.get(clazz);
+        }
         return sLookup.get(clazz);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/state/PersistedTabDataStorage.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/state/PersistedTabDataStorage.java
index 03167cd..fff6e91 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/state/PersistedTabDataStorage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/state/PersistedTabDataStorage.java
@@ -12,24 +12,21 @@
 public interface PersistedTabDataStorage {
     /**
      * @param tabId identifier for the {@link Tab}
-     * @param isEncrypted true if the data should be encrypted (i.e. for incognito Tabs)
      * @param tabDataId unique identifier representing the type {@link PersistedTabData}
      * @param data serialized {@link PersistedTabData}
      */
-    void save(int tabId, boolean isEncrypted, String tabDataId, byte[] data);
+    void save(int tabId, String tabDataId, byte[] data);
 
     /**
      * @param tabId identifier for the {@link Tab}
-     * @param isEncrypted true if the stored data is encrypted (i.e. for incognito Tabs)
      * @param tabDataId unique identifier representing the type of {@link PersistedTabData}
      * @param callback to pass back the seraizliaed {@link PersistedTabData} in
      */
-    void restore(int tabId, boolean isEncrypted, String tabDataId, Callback<byte[]> callback);
+    void restore(int tabId, String tabDataId, Callback<byte[]> callback);
 
     /**
      * @param tabId identifier for the {@link Tab}
-     * @param isEncrypted true if the stored data is encrypted (i.e. for incognito Tabs)
      * @param tabDataId unique identifier representing the type of {@link PersistedTabData}
      */
-    void delete(int tabId, boolean isEncrypted, String tabDataId);
+    void delete(int tabId, String tabDataId);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java
index 885f6462..726dde2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java
@@ -86,7 +86,8 @@
                 long dataSaved = DataReductionProxySettings.getInstance()
                                          .getContentLengthSavedInHistorySummary()
                         - mDataSavedOnStartPageLoad;
-                Tracker tracker = TrackerFactory.getTrackerForProfile(((TabImpl) tab).getProfile());
+                Tracker tracker = TrackerFactory.getTrackerForProfile(
+                        Profile.fromWebContents(tab.getWebContents()));
                 if (dataSaved > 0L) tracker.notifyEvent(EventConstants.DATA_SAVED_ON_PAGE_LOAD);
                 if (Previews.isPreview(tab)) {
                     tracker.notifyEvent(EventConstants.PREVIEWS_PAGE_LOADED);
@@ -107,14 +108,15 @@
                     return;
                 }
 
-                OfflinePageBridge bridge =
-                        OfflinePageBridge.getForProfile(((TabImpl) tab).getProfile());
+                OfflinePageBridge bridge = OfflinePageBridge.getForProfile(
+                        Profile.fromWebContents(tab.getWebContents()));
                 if (bridge == null
                         || !bridge.isShowingDownloadButtonInErrorPage(tab.getWebContents())) {
                     return;
                 }
 
-                Tracker tracker = TrackerFactory.getTrackerForProfile(((TabImpl) tab).getProfile());
+                Tracker tracker = TrackerFactory.getTrackerForProfile(
+                        Profile.fromWebContents(tab.getWebContents()));
                 tracker.notifyEvent(EventConstants.USER_HAS_SEEN_DINO);
             }
         };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index e7906f0..5049e6ee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -71,7 +71,6 @@
 import org.chromium.chrome.browser.share.ShareDelegate;
 import org.chromium.chrome.browser.tab.SadTab;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.tab.TabSelectionType;
 import org.chromium.chrome.browser.tab.TabThemeColorHelper;
@@ -825,7 +824,8 @@
         // getOriginalProfile or via a Supplier<Profile>.
         Tab tab = mActivityTabProvider.get();
         if (tab != null) {
-            Tracker tracker = TrackerFactory.getTrackerForProfile(((TabImpl) tab).getProfile());
+            Tracker tracker = TrackerFactory.getTrackerForProfile(
+                    Profile.fromWebContents(tab.getWebContents()));
             tracker.notifyEvent(toolbarIPHEvent);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java
index c7531b7..57de48b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java
@@ -24,9 +24,9 @@
 import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior.OverviewModeObserver;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.omnibox.LocationBar;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.share.ShareDelegate;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tasks.ReturnToChromeExperimentsUtil;
 import org.chromium.chrome.browser.toolbar.IncognitoStateProvider;
 import org.chromium.chrome.browser.toolbar.TabCountProvider;
@@ -249,7 +249,8 @@
         Tab tab = mTabProvider.get();
         if (tab == null) return;
 
-        Tracker tracker = TrackerFactory.getTrackerForProfile(((TabImpl) tab).getProfile());
+        Tracker tracker =
+                TrackerFactory.getTrackerForProfile(Profile.fromWebContents(tab.getWebContents()));
         tracker.notifyEvent(EventConstants.CHROME_DUET_USED_BOTTOM_TOOLBAR);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
index c770288..5dbb14f8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
@@ -16,6 +16,7 @@
 import org.chromium.chrome.browser.ThemeColorProvider;
 import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tasks.ReturnToChromeExperimentsUtil;
@@ -145,16 +146,16 @@
             @Override
             public void onActivityTabChanged(Tab tab) {
                 if (tab == null) return;
-                TabImpl tabImpl = (TabImpl) tab;
-                final Tracker tracker = TrackerFactory.getTrackerForProfile(tabImpl.getProfile());
+                Profile profile = Profile.fromWebContents(tab.getWebContents());
+                final Tracker tracker = TrackerFactory.getTrackerForProfile(profile);
                 final Runnable completeRunnable = () -> {
                     if (listener != null) {
                         listener.onClick(anchor);
                     }
                 };
                 tracker.addOnInitializedCallback((ready) -> {
-                    mMediator.showIPH(
-                            feature, tabImpl.getActivity(), anchor, tracker, completeRunnable);
+                    mMediator.showIPH(feature, ((TabImpl) tab).getActivity(), anchor, tracker,
+                            completeRunnable);
                 });
                 mTabProvider.removeObserver(this);
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
index a10e029..1a9e11f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
@@ -35,8 +35,8 @@
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.toolbar.ButtonData;
 import org.chromium.chrome.browser.toolbar.HomeButton;
 import org.chromium.chrome.browser.toolbar.KeyboardNavigationListener;
@@ -305,8 +305,8 @@
     private void displayNavigationPopup(boolean isForward, View anchorView) {
         Tab tab = getToolbarDataProvider().getTab();
         if (tab == null || tab.getWebContents() == null) return;
-        mNavigationPopup = new NavigationPopup(((TabImpl) tab).getProfile(), getContext(),
-                tab.getWebContents().getNavigationController(),
+        mNavigationPopup = new NavigationPopup(Profile.fromWebContents(tab.getWebContents()),
+                getContext(), tab.getWebContents().getNavigationController(),
                 isForward ? NavigationPopup.Type.TABLET_FORWARD : NavigationPopup.Type.TABLET_BACK);
         mNavigationPopup.show(anchorView);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java
index c7b670c..b6eab0e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java
@@ -196,10 +196,10 @@
     }
 
     /**
-     * Returns true if a WebAPK is found whose scope matches the provided URL.
-     * @param url The URL to search a WebAPK for.
+     * Returns true if a WebAPK is found whose scope matches |origin|.
+     * @param origin The origin to search a WebAPK for.
      */
-    public boolean hasWebApkForUrl(String url) {
+    public boolean hasAtLeastOneWebApkForOrigin(String origin) {
         for (HashMap.Entry<String, WebappDataStorage> entry : mStorages.entrySet()) {
             WebappDataStorage storage = entry.getValue();
             if (!storage.getId().startsWith(WebApkConstants.WEBAPK_ID_PREFIX)) continue;
@@ -209,7 +209,7 @@
             // Scope shouldn't be empty.
             assert (!scope.isEmpty());
 
-            if (url.startsWith(scope)) return true;
+            if (scope.startsWith(origin)) return true;
         }
         return false;
     }
diff --git a/chrome/android/javatests/DEPS b/chrome/android/javatests/DEPS
index 6d7182f..63dee6f 100644
--- a/chrome/android/javatests/DEPS
+++ b/chrome/android/javatests/DEPS
@@ -2,6 +2,7 @@
   "+chrome/app",
   "+chrome/browser/android/lifecycle",
   "+chrome/browser/preferences/android/java",
+  "+chrome/browser/profiles/android/java",
   "+chrome/browser/thumbnail/generator/android/java",
   "+chrome/browser/ui/android/appmenu",
   "-chrome/browser/ui/android/appmenu/internal",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoDismissTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoDismissTest.java
index e59f2089..86b225d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoDismissTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoDismissTest.java
@@ -28,10 +28,10 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.SigninPromoController;
 import org.chromium.chrome.browser.sync.SyncTestRule;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.BookmarkTestUtil;
 import org.chromium.chrome.test.util.ChromeTabUtils;
@@ -57,8 +57,8 @@
         mSyncTestRule.startMainActivityForSyncTest();
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            BookmarkModel bookmarkModel = new BookmarkModel(
-                    ((TabImpl) mSyncTestRule.getActivity().getActivityTab()).getProfile());
+            BookmarkModel bookmarkModel = new BookmarkModel(Profile.fromWebContents(
+                    mSyncTestRule.getActivity().getActivityTab().getWebContents()));
             bookmarkModel.loadFakePartnerBookmarkShimForTesting();
             BookmarkTestUtil.waitForBookmarkModelLoaded();
         });
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
index 3aca8f0..ce6a5162 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
@@ -42,7 +42,7 @@
 import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmarksShim;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.browser.tab.TabImpl;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.util.ActivityUtils;
@@ -119,8 +119,8 @@
     public void setUp() {
         mActivityTestRule.startMainActivityOnBlankPage();
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mBookmarkModel = new BookmarkModel(
-                    ((TabImpl) mActivityTestRule.getActivity().getActivityTab()).getProfile());
+            mBookmarkModel = new BookmarkModel(Profile.fromWebContents(
+                    mActivityTestRule.getActivity().getActivityTab().getWebContents()));
         });
         mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
         mTestPage = mTestServer.getURL(TEST_PAGE_URL_GOOGLE);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java
index b17444e..401ea1c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java
@@ -17,7 +17,6 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 
@@ -42,8 +41,9 @@
             @Override
             public void run() {
                 final String url = "https://www.example.com";
-                SiteEngagementService service = SiteEngagementService.getForProfile(
-                        ((TabImpl) mActivityTestRule.getActivity().getActivityTab()).getProfile());
+                SiteEngagementService service =
+                        SiteEngagementService.getForProfile(Profile.fromWebContents(
+                                mActivityTestRule.getActivity().getActivityTab().getWebContents()));
 
                 Assert.assertEquals(0.0, service.getScore(url), 0);
                 service.resetBaseScoreForUrl(url, 5.0);
@@ -66,8 +66,8 @@
             @Override
             public void run() {
                 final String url = "https://www.example.com";
-                Profile profile =
-                        ((TabImpl) mActivityTestRule.getActivity().getActivityTab()).getProfile();
+                Profile profile = Profile.fromWebContents(
+                        mActivityTestRule.getActivity().getActivityTab().getWebContents());
 
                 Assert.assertEquals(
                         0.0, SiteEngagementService.getForProfile(profile).getScore(url), 0);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageAutoFetchTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageAutoFetchTest.java
index 1b97e202..8d8ef8c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageAutoFetchTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageAutoFetchTest.java
@@ -29,7 +29,6 @@
 import org.chromium.chrome.browser.offlinepages.AutoFetchNotifier.NotificationAction;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
@@ -155,7 +154,7 @@
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mInitialHistograms = histogramSnapshot();
-            mProfile = ((TabImpl) activityTab()).getProfile();
+            mProfile = Profile.fromWebContents(activityTab().getWebContents());
             mOfflinePageBridge = OfflinePageBridge.getForProfile(mProfile);
 
             if (!NetworkChangeNotifier.isInitialized()) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java
index 894c24b0..509efb4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java
@@ -35,7 +35,6 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.share.ShareParams;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager.SnackbarController;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -318,8 +317,9 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             OfflinePageItem privateOfflinePageItem = new OfflinePageItem(uriPath, OFFLINE_ID,
                     namespace, PAGE_ID, TITLE, filePath, FILE_SIZE, 0, 0, 0, REQUEST_ORIGIN);
-            OfflinePageBridge offlinePageBridge = OfflinePageBridge.getForProfile(
-                    ((TabImpl) mActivityTestRule.getActivity().getActivityTab()).getProfile());
+            OfflinePageBridge offlinePageBridge =
+                    OfflinePageBridge.getForProfile(Profile.fromWebContents(
+                            mActivityTestRule.getActivity().getActivityTab().getWebContents()));
 
             boolean isSharable = OfflinePageUtils.isOfflinePageShareable(
                     offlinePageBridge, privateOfflinePageItem, Uri.parse(uriPath));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchFlowTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchFlowTest.java
index 5e8e9e5..ca6ddabb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchFlowTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchFlowTest.java
@@ -30,7 +30,6 @@
 import org.chromium.chrome.browser.offlinepages.OfflinePageItem;
 import org.chromium.chrome.browser.offlinepages.OfflineTestUtil;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.components.background_task_scheduler.TaskIds;
@@ -126,7 +125,8 @@
 
         // Register Offline Page observer and enable limitless prefetching.
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mProfile = ((TabImpl) mActivityTestRule.getActivity().getActivityTab()).getProfile();
+            mProfile = Profile.fromWebContents(
+                    mActivityTestRule.getActivity().getActivityTab().getWebContents());
             OfflinePageBridge.getForProfile(mProfile).addObserver(
                     new OfflinePageBridge.OfflinePageModelObserver() {
                         @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/prerender/PrerenderTestHelper.java b/chrome/android/javatests/src/org/chromium/chrome/browser/prerender/PrerenderTestHelper.java
index cf55404d..25f2e2e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/prerender/PrerenderTestHelper.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/prerender/PrerenderTestHelper.java
@@ -9,8 +9,8 @@
 import org.junit.Assert;
 
 import org.chromium.chrome.browser.TabLoadStatus;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.test.util.Coordinates;
 import org.chromium.content_public.browser.test.util.Criteria;
@@ -77,7 +77,8 @@
                                 Rect bounds = new Rect(0, 0, coord.getContentWidthPixInt(),
                                         coord.getContentHeightPixInt());
                                 boolean didPrerender = prerenderHandler.addPrerender(
-                                                               ((TabImpl) currentTab).getProfile(),
+                                                               Profile.fromWebContents(
+                                                                       currentTab.getWebContents()),
                                                                currentTab.getWebContents(), testUrl,
                                                                null, bounds, true)
                                         != null;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninSignoutIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninSignoutIntegrationTest.java
index eabdf0e..d84bc34 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninSignoutIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninSignoutIntegrationTest.java
@@ -35,9 +35,9 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.bookmarks.BookmarkModel;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileAccountManagementMetrics;
 import org.chromium.chrome.browser.sync.settings.AccountManagementFragment;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ActivityUtils;
@@ -195,8 +195,8 @@
     private void addOneTestBookmark() {
         Assert.assertNull("This method should be called only once!", mBookmarkModel);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mBookmarkModel = new BookmarkModel(
-                    ((TabImpl) mActivityTestRule.getActivity().getActivityTab()).getProfile());
+            mBookmarkModel = new BookmarkModel(Profile.fromWebContents(
+                    mActivityTestRule.getActivity().getActivityTab().getWebContents()));
             mBookmarkModel.loadFakePartnerBookmarkShimForTesting();
             BookmarkTestUtil.waitForBookmarkModelLoaded();
             Assert.assertEquals(0, mBookmarkModel.getChildCount(mBookmarkModel.getDefaultFolder()));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
index d65d37d..1301ded 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
@@ -53,6 +53,7 @@
 import org.chromium.content_public.common.ContentSwitches;
 import org.chromium.net.test.EmbeddedTestServer;
 import org.chromium.net.test.ServerCertificate;
+import org.chromium.policy.test.annotations.Policies;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -256,6 +257,41 @@
         });
     }
 
+    /**
+     * Checks if the button representing the given state matches the managed expectation.
+     */
+    private void checkFourStateCookieToggleButtonUnmanaged(final SettingsActivity settingsActivity,
+            final CookieSettingsState state, final boolean expected) {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            SingleCategorySettings preferences =
+                    (SingleCategorySettings) settingsActivity.getMainFragment();
+            FourStateCookieSettingsPreference fourStateCookieToggle =
+                    (FourStateCookieSettingsPreference) preferences.findPreference(
+                            SingleCategorySettings.FOUR_STATE_COOKIE_TOGGLE_KEY);
+            Assert.assertNotEquals("Button should be " + (expected ? "unmanaged" : "managed"),
+                    fourStateCookieToggle.isStateEnforced(state), expected);
+        });
+    }
+
+    private void checkDefaultCookiesSettingManaged(boolean expected) {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertEquals(
+                    "Default Cookie Setting should be " + (expected ? "managed" : "unmanaged"),
+                    WebsitePreferenceBridge.isContentSettingManaged(ContentSettingsType.COOKIES),
+                    expected);
+        });
+    }
+
+    private void checkThirdPartyCookieBlockingManaged(boolean expected) {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertEquals(
+                    "Third Party Cookie Blocking should be " + (expected ? "managed" : "unmanaged"),
+                    PrefServiceBridge.getInstance().isManagedPreference(
+                            Pref.BLOCK_THIRD_PARTY_COOKIES),
+                    expected);
+        });
+    }
+
     private void setGlobalToggleForCategory(
             final @SiteSettingsCategory.Type int type, final boolean enabled) {
         final SettingsActivity settingsActivity =
@@ -505,6 +541,175 @@
         Assert.assertEquals("\"\"", mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
     }
 
+    /**
+     * Set the cookie content setting to managed and ensure the correct radio buttons are enforced.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Preferences"})
+    @Policies.Add({ @Policies.Item(key = "DefaultCookiesSetting", string = "1") })
+    public void testDefaultCookiesSettingManagedTrue() throws Exception {
+        checkDefaultCookiesSettingManaged(true);
+        checkThirdPartyCookieBlockingManaged(false);
+        // The ContentSetting is managed (and set to true) while ThirdPartyCookieBlocking is not
+        // managed. This means that every button other than BLOCKED is unmanaged.
+        SettingsActivity settingsActivity =
+                SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.COOKIES);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.ALLOW, true);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, true);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY, true);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK, false);
+        settingsActivity.finish();
+    }
+
+    /**
+     * Set the cookie content setting to managed and ensure the correct radio buttons are enforced.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Preferences"})
+    @Policies.Add({ @Policies.Item(key = "DefaultCookiesSetting", string = "2") })
+    public void testDefaultCookiesSettingManagedFalse() throws Exception {
+        checkDefaultCookiesSettingManaged(true);
+        checkThirdPartyCookieBlockingManaged(false);
+        // The ContentSetting is managed (and set to false) while ThirdPartyCookieBlocking is not
+        // managed. This means cookies should always be blocked, so the user cannot choose any other
+        // options and all buttons should be managed.
+        SettingsActivity settingsActivity =
+                SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.COOKIES);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.ALLOW, false);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, false);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY, false);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK, false);
+        settingsActivity.finish();
+    }
+
+    /**
+     * Set third-party cookie blocking to managed and ensure the correct radio buttons are enforced.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Preferences"})
+    @Policies.Add({ @Policies.Item(key = "BlockThirdPartyCookies", string = "true") })
+    @DisabledTest(
+            message =
+                    "TODO(eokoyomon): See if it possible to support BlockThirdPartyCookies policy on android.")
+    public void
+    testBlockThirdPartyCookiesManagedTrue() throws Exception {
+        checkDefaultCookiesSettingManaged(false);
+        checkThirdPartyCookieBlockingManaged(true);
+        // ThirdPartyCookieBlocking is managed (and set to true) while the ContentSetting is not
+        // managed. This means a user can choose only between BLOCK_THIRD_PARTY and BLOCK, so only
+        // these should be unmanaged.
+        SettingsActivity settingsActivity =
+                SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.COOKIES);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.ALLOW, false);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, false);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY, true);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK, true);
+        settingsActivity.finish();
+    }
+
+    /**
+     * Set third-party cookie blocking to managed and ensure the correct radio buttons are enforced.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Preferences"})
+    @Policies.Add({ @Policies.Item(key = "BlockThirdPartyCookies", string = "false") })
+    @DisabledTest(
+            message =
+                    "TODO(eokoyomon): See if it possible to support BlockThirdPartyCookies policy on android.")
+    public void
+    testBlockThirdPartyCookiesManagedFalse() throws Exception {
+        checkDefaultCookiesSettingManaged(false);
+        checkThirdPartyCookieBlockingManaged(true);
+        // ThirdPartyCookieBlocking is managed (and set to false) while the ContentSetting is not
+        // managed. This means a user can only choose to ALLOW all cookies or BLOCK all cookies, so
+        // only these should be unmanaged.
+        SettingsActivity settingsActivity =
+                SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.COOKIES);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.ALLOW, true);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, false);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY, false);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK, true);
+        settingsActivity.finish();
+    }
+
+    /**
+     * Set both the cookie content setting and third-party cookie blocking to managed and ensure the
+     * correct radio buttons are enforced.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Preferences"})
+    @Policies.Add({
+        @Policies.Item(key = "DefaultCookiesSetting", string = "1")
+        , @Policies.Item(key = "BlockThirdPartyCookies", string = "false")
+    })
+    @DisabledTest(
+            message =
+                    "TODO(eokoyomon): See if it possible to support BlockThirdPartyCookies policy on android.")
+    public void
+    testAllCookieSettingsManaged() throws Exception {
+        checkDefaultCookiesSettingManaged(true);
+        checkThirdPartyCookieBlockingManaged(true);
+        // The ContentSetting and ThirdPartyCookieBlocking are managed. This means a user has a
+        // fixed setting for cookies that they cannot change. Therefore, all buttons should be
+        // managed.
+        SettingsActivity settingsActivity =
+                SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.COOKIES);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.ALLOW, false);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, false);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY, false);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK, false);
+        settingsActivity.finish();
+    }
+
+    /**
+     * Ensure no radio buttons are enforced when cookie settings are unmanaged.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Preferences"})
+    public void testNoCookieSettingsManaged() throws Exception {
+        checkDefaultCookiesSettingManaged(false);
+        checkThirdPartyCookieBlockingManaged(false);
+        // The ContentSetting and ThirdPartyCookieBlocking are unmanaged. This means all buttons
+        // should be unmanaged.
+        SettingsActivity settingsActivity =
+                SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.COOKIES);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.ALLOW, true);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, true);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY, true);
+        checkFourStateCookieToggleButtonUnmanaged(
+                settingsActivity, CookieSettingsState.BLOCK, true);
+        settingsActivity.finish();
+    }
+
     private void resetSite(WebsiteAddress address) {
         Website website = new Website(address, address);
         final SettingsActivity settingsActivity =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabDataTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabDataTest.java
index 6353fa4e..51208b7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabDataTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabDataTest.java
@@ -4,9 +4,6 @@
 
 package org.chromium.chrome.browser.tab.state;
 
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
 import android.support.test.filters.SmallTest;
 
 import org.junit.Assert;
@@ -14,16 +11,17 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.Callback;
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.chrome.browser.tab.MockTab;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 
+import java.util.concurrent.Semaphore;
+
 /**
  * Test relating to {@link CriticalPersistedTabData}
  */
@@ -43,14 +41,7 @@
     private static final int THEME_COLOR = 5;
     private static final int LAUNCH_TYPE_AT_CREATION = 3;
 
-    @Mock
-    private Callback<Boolean> mBooleanCallback;
-
-    @Mock
-    private Callback<CriticalPersistedTabData> mRestoreCallback;
-
-    @Mock
-    private Callback<CriticalPersistedTabData> mDeleteCallback;
+    private CriticalPersistedTabData mCriticalPersistedTabData;
 
     private static Tab mockTab(int id, boolean isEncrypted) {
         return new MockTab(id, isEncrypted);
@@ -63,44 +54,54 @@
 
     @SmallTest
     @Test
-    public void testNonEncryptedSaveRestore() {
+    public void testNonEncryptedSaveRestore() throws InterruptedException {
         testSaveRestoreDelete(false);
     }
 
     @SmallTest
     @Test
-    public void testEncryptedSaveRestoreDelete() {
+    public void testEncryptedSaveRestoreDelete() throws InterruptedException {
         testSaveRestoreDelete(true);
     }
 
-    private void testSaveRestoreDelete(boolean isEncrypted) {
-        PersistedTabDataConfiguration config =
-                PersistedTabDataConfiguration.get(CriticalPersistedTabData.class);
-        CriticalPersistedTabData criticalPersistedTabData =
-                new CriticalPersistedTabData(mockTab(TAB_ID, isEncrypted), PARENT_ID, ROOT_ID,
-                        TIMESTAMP, CONTENT_STATE, CONTENT_STATE_VERSION, OPENER_APP_ID, THEME_COLOR,
-                        LAUNCH_TYPE_AT_CREATION, config.storage, config.id);
-        criticalPersistedTabData.save();
-        CriticalPersistedTabData.from(mockTab(TAB_ID, isEncrypted), mRestoreCallback);
-        ArgumentCaptor<CriticalPersistedTabData> cptdCaptor =
-                ArgumentCaptor.forClass(CriticalPersistedTabData.class);
-        verify(mRestoreCallback, times(1)).onResult(cptdCaptor.capture());
-        CriticalPersistedTabData restored = cptdCaptor.getValue();
-        Assert.assertNotNull(restored);
-        Assert.assertEquals(restored.getParentId(), PARENT_ID);
-        Assert.assertEquals(restored.getRootId(), ROOT_ID);
-        Assert.assertEquals(restored.getTimestampMillis(), TIMESTAMP);
-        Assert.assertEquals(restored.getContentStateVersion(), CONTENT_STATE_VERSION);
-        Assert.assertEquals(restored.getOpenerAppId(), OPENER_APP_ID);
-        Assert.assertEquals(restored.getThemeColor(), THEME_COLOR);
-        Assert.assertEquals(restored.getTabLaunchTypeAtCreation(), LAUNCH_TYPE_AT_CREATION);
-        Assert.assertArrayEquals(restored.getContentStateBytes(), CONTENT_STATE);
-        restored.delete();
-        CriticalPersistedTabData.from(mockTab(TAB_ID, isEncrypted), mDeleteCallback);
-        ArgumentCaptor<CriticalPersistedTabData> cptdDeletedCaptor =
-                ArgumentCaptor.forClass(CriticalPersistedTabData.class);
-        verify(mDeleteCallback, times(1)).onResult(cptdDeletedCaptor.capture());
-        Assert.assertNull(cptdDeletedCaptor.getValue());
+    private void testSaveRestoreDelete(boolean isEncrypted) throws InterruptedException {
+        final Semaphore semaphore = new Semaphore(0);
+        Callback<CriticalPersistedTabData> callback = new Callback<CriticalPersistedTabData>() {
+            @Override
+            public void onResult(CriticalPersistedTabData res) {
+                mCriticalPersistedTabData = res;
+                semaphore.release();
+            }
+        };
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            PersistedTabDataConfiguration config =
+                    PersistedTabDataConfiguration.get(CriticalPersistedTabData.class, isEncrypted);
+            CriticalPersistedTabData criticalPersistedTabData =
+                    new CriticalPersistedTabData(mockTab(TAB_ID, isEncrypted), PARENT_ID, ROOT_ID,
+                            TIMESTAMP, CONTENT_STATE, CONTENT_STATE_VERSION, OPENER_APP_ID,
+                            THEME_COLOR, LAUNCH_TYPE_AT_CREATION, config.storage, config.id);
+            criticalPersistedTabData.save();
+            CriticalPersistedTabData.from(mockTab(TAB_ID, isEncrypted), callback);
+        });
+        semaphore.acquire();
+        Assert.assertNotNull(mCriticalPersistedTabData);
+        Assert.assertEquals(mCriticalPersistedTabData.getParentId(), PARENT_ID);
+        Assert.assertEquals(mCriticalPersistedTabData.getRootId(), ROOT_ID);
+        Assert.assertEquals(mCriticalPersistedTabData.getTimestampMillis(), TIMESTAMP);
+        Assert.assertEquals(
+                mCriticalPersistedTabData.getContentStateVersion(), CONTENT_STATE_VERSION);
+        Assert.assertEquals(mCriticalPersistedTabData.getOpenerAppId(), OPENER_APP_ID);
+        Assert.assertEquals(mCriticalPersistedTabData.getThemeColor(), THEME_COLOR);
+        Assert.assertEquals(
+                mCriticalPersistedTabData.getTabLaunchTypeAtCreation(), LAUNCH_TYPE_AT_CREATION);
+        Assert.assertArrayEquals(mCriticalPersistedTabData.getContentStateBytes(), CONTENT_STATE);
+
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mCriticalPersistedTabData.delete();
+            CriticalPersistedTabData.from(mockTab(TAB_ID, isEncrypted), callback);
+        });
+        semaphore.acquire();
+        Assert.assertNull(mCriticalPersistedTabData);
         // TODO(crbug.com/1060232) test restored.save() after restored.delete()
         // Also cover
         // - Multiple (different) TAB_IDs being stored in the same storage.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/FilePersistedTabDataStorageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/FilePersistedTabDataStorageTest.java
index 4b1ef2b7..6d75ee20 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/FilePersistedTabDataStorageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/FilePersistedTabDataStorageTest.java
@@ -4,9 +4,6 @@
 
 package org.chromium.chrome.browser.tab.state;
 
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
 import android.support.test.filters.SmallTest;
 
 import org.junit.Assert;
@@ -14,15 +11,16 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.Callback;
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 
 import java.io.File;
+import java.util.concurrent.Semaphore;
 
 /**
  * Tests relating to  {@link FilePersistedTabDataStorage}
@@ -39,6 +37,8 @@
     private static final byte[] DATA = {13, 14};
     private static final String DATA_ID = "DataId";
 
+    private byte[] mResult;
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -46,27 +46,41 @@
 
     @SmallTest
     @Test
-    public void testFilePersistedDataStorageNonEncrypted() {
-        testFilePersistedDataStorage(false);
+    public void testFilePersistedDataStorageNonEncrypted() throws InterruptedException {
+        testFilePersistedDataStorage(new FilePersistedTabDataStorage());
     }
 
     @SmallTest
     @Test
-    public void testFilePersistedDataStorageEncrypted() {
-        testFilePersistedDataStorage(true);
+    public void testFilePersistedDataStorageEncrypted() throws InterruptedException {
+        testFilePersistedDataStorage(new EncryptedFilePersistedTabDataStorage());
     }
 
-    private void testFilePersistedDataStorage(boolean isEncrypted) {
-        FilePersistedTabDataStorage filePersistedTabDataStorage = new FilePersistedTabDataStorage();
-        filePersistedTabDataStorage.save(TAB_ID, isEncrypted, DATA_ID, DATA);
-        ArgumentCaptor<byte[]> byteArrayCaptor = ArgumentCaptor.forClass(byte[].class);
-        filePersistedTabDataStorage.restore(TAB_ID, isEncrypted, DATA_ID, mByteArrayCallback);
-        verify(mByteArrayCallback, times(1)).onResult(byteArrayCaptor.capture());
-        Assert.assertEquals(byteArrayCaptor.getValue().length, 2);
-        Assert.assertArrayEquals(byteArrayCaptor.getValue(), DATA);
-        File file = filePersistedTabDataStorage.getFile(TAB_ID, isEncrypted, DATA_ID);
+    private void testFilePersistedDataStorage(FilePersistedTabDataStorage persistedTabDataStorage)
+            throws InterruptedException {
+        final Semaphore semaphore = new Semaphore(0);
+        Callback<byte[]> callback = new Callback<byte[]>() {
+            @Override
+            public void onResult(byte[] res) {
+                mResult = res;
+                semaphore.release();
+            }
+        };
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            persistedTabDataStorage.save(TAB_ID, DATA_ID, DATA);
+            persistedTabDataStorage.restore(TAB_ID, DATA_ID, callback);
+        });
+        semaphore.acquire();
+        Assert.assertEquals(mResult.length, 2);
+        Assert.assertArrayEquals(mResult, DATA);
+        File file = persistedTabDataStorage.getFile(TAB_ID, DATA_ID);
         Assert.assertTrue(file.exists());
-        filePersistedTabDataStorage.delete(TAB_ID, isEncrypted, DATA_ID);
+
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            persistedTabDataStorage.delete(TAB_ID, DATA_ID);
+            semaphore.release();
+        });
+        semaphore.acquire();
         Assert.assertFalse(file.exists());
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/cryptids/ProbabilisticCryptidRendererUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/cryptids/ProbabilisticCryptidRendererUnitTest.java
index 9ca637fc..27da3a6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/cryptids/ProbabilisticCryptidRendererUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/cryptids/ProbabilisticCryptidRendererUnitTest.java
@@ -68,10 +68,40 @@
         Assert.assertEquals(MAX_PROBABILITY, probPostRampup);
     }
 
+    @Test
+    @SmallTest
+    public void testPrefStorageDefaultValue() {
+        ProbabilisticCryptidRenderer render = new ProbabilisticCryptidRenderer();
+        long defaultRenderTimestamp = render.getLastRenderTimestampMillis();
+
+        long howLongSinceDefaultTime = System.currentTimeMillis() - defaultRenderTimestamp;
+        // We expect |howLongSinceDefaultTime| to be one moratorium length, because the default
+        // case should be rigged up to start users at the end of the moratorium.
+        long delta = howLongSinceDefaultTime - render.getRenderingMoratoriumLengthMillis();
+        Assert.assertTrue(String.format("Delta %d was larger than 10 seconds (10000)", delta),
+                delta < 10000); // Allow a 10 second grace period in case the test is slow.
+    }
+
+    @Test
+    @SmallTest
+    public void testPrefStorage() {
+        ProbabilisticCryptidRenderer render = new ProbabilisticCryptidRenderer();
+        // Set the last render event to be a crazy long time ago, ensuring max probability.
+        render.recordRenderEvent(1);
+        Assert.assertEquals(render.getMaxProbability(), render.calculateProbability());
+
+        // Immediately after an event, the probability should have dropped to 0.
+        render.recordRenderEvent();
+        Assert.assertEquals(0, render.calculateProbability());
+    }
+
     // Aggregator tests: these tests run |NUM_RUNS| invocations of shouldUseCryptidRendering to
     // verify that the number of trues returned is consistent with our assumptions about how often
     // this should happen, within |TOLERANCE|.
 
+    /**
+     * This fake bypasses any calls to pref logic, and also uses a seeded RNG.
+     */
     private static class FakeProbabilisticCrpytidRenderer extends ProbabilisticCryptidRenderer {
         public FakeProbabilisticCrpytidRenderer(long lastRenderDeltaFromNow) {
             mLastRenderTimestamp = System.currentTimeMillis() + lastRenderDeltaFromNow;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsUnitTest.java
index 20d2dec..6e2e2ea 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsUnitTest.java
@@ -30,7 +30,9 @@
 import org.chromium.base.UserDataHost;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.profiles.ProfileJni;
 import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.test.util.SadTabRule;
@@ -47,6 +49,11 @@
 @Config(manifest = Config.NONE,
         shadows = {OfflinePageUtilsUnitTest.WrappedEnvironment.class, ShadowMultiDex.class})
 public class OfflinePageUtilsUnitTest {
+    @Rule
+    public JniMocker mocker = new JniMocker();
+    @Mock
+    public Profile.Natives mMockProfileNatives;
+
     @Mock
     private File mMockDataDirectory;
     @Mock
@@ -64,6 +71,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mocker.mock(ProfileJni.TEST_HOOKS, mMockProfileNatives);
         WrappedEnvironment.setDataDirectoryForTest(mMockDataDirectory);
 
         // Setting up a mock tab. These are the values common to most tests, but individual
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java
index 3b427668..0482ec1 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java
@@ -27,7 +27,8 @@
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.tab.TabImpl;
+import org.chromium.chrome.browser.profiles.ProfileJni;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.content_public.browser.NavigationController;
@@ -40,11 +41,13 @@
 public class SendTabToSelfShareActivityTest {
     @Rule
     public JniMocker mocker = new JniMocker();
+    @Mock
+    public Profile.Natives mMockProfileNatives;
 
     @Mock
     SendTabToSelfAndroidBridge.Natives mNativeMock;
     @Mock
-    private TabImpl mTab;
+    private Tab mTab;
     @Mock
     private ChromeActivity mChromeActivity;
     @Mock
@@ -66,6 +69,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mocker.mock(SendTabToSelfAndroidBridgeJni.TEST_HOOKS, mNativeMock);
+        mocker.mock(ProfileJni.TEST_HOOKS, mMockProfileNatives);
         RecordHistogram.setDisabledForTests(true);
     }
 
@@ -91,7 +95,7 @@
         // Setup the mocked object chain to get to the profile.
         when(mChromeActivity.getActivityTabProvider()).thenReturn(mActivityTabProvider);
         when(mActivityTabProvider.get()).thenReturn(mTab);
-        when((mTab).getProfile()).thenReturn(mProfile);
+        when(mMockProfileNatives.fromWebContents(eq(mWebContents))).thenReturn(mProfile);
 
         // Setup the mocked object chain to get to the url, title and timestamp.
         when(mTab.getWebContents()).thenReturn(mWebContents);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java
index 65e673a..b4f13fb 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java
@@ -666,19 +666,19 @@
 
     @Test
     @Feature({"WebApk"})
-    public void testHasWebApkForUrl() throws Exception {
-        final String startUrl = START_URL;
-        final String testUrl = START_URL + "/index.html";
+    public void testHasWebApkForOrigin() throws Exception {
+        final String startUrl = START_URL + "/test_page.html";
+        final String testOrigin = START_URL;
 
-        assertFalse(WebappRegistry.getInstance().hasWebApkForUrl(testUrl));
+        assertFalse(WebappRegistry.getInstance().hasAtLeastOneWebApkForOrigin(testOrigin));
 
         String webappId = "webapp";
         registerWebapp(webappId, createShortcutWebappInfo(startUrl));
-        assertFalse(WebappRegistry.getInstance().hasWebApkForUrl(testUrl));
+        assertFalse(WebappRegistry.getInstance().hasAtLeastOneWebApkForOrigin(testOrigin));
 
         WebApkInfo webApkInfo = new WebApkInfoBuilder("org.chromium.webapk", startUrl).build();
         registerWebapp(webApkInfo.id(), webApkInfo);
-        assertTrue(WebappRegistry.getInstance().hasWebApkForUrl(testUrl));
+        assertTrue(WebappRegistry.getInstance().hasAtLeastOneWebApkForOrigin(testOrigin));
     }
 
     private Set<String> addWebappsToRegistry(String... webapps) {
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index 71b39d4..1f0d35c7 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -141,8 +141,8 @@
   if (!is_fuchsia) {
     # TODO(crbug.com/753619): Enable crash reporting on Fuchsia.
     deps += [
-      "//components/crash/content/app",
-      "//components/crash/content/app:test_support",
+      "//components/crash/core/app",
+      "//components/crash/core/app:test_support",
     ]
   }
 
diff --git a/chrome/app/chrome_crash_reporter_client.h b/chrome/app/chrome_crash_reporter_client.h
index 848719b..4086051a 100644
--- a/chrome/app/chrome_crash_reporter_client.h
+++ b/chrome/app/chrome_crash_reporter_client.h
@@ -13,7 +13,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "build/build_config.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 
 class ChromeCrashReporterClient : public crash_reporter::CrashReporterClient {
  public:
diff --git a/chrome/app/chrome_crash_reporter_client_win.cc b/chrome/app/chrome_crash_reporter_client_win.cc
index 4625e3d..1e6ae0d 100644
--- a/chrome/app/chrome_crash_reporter_client_win.cc
+++ b/chrome/app/chrome_crash_reporter_client_win.cc
@@ -24,7 +24,7 @@
 #include "chrome/common/chrome_result_codes.h"
 #include "chrome/install_static/install_util.h"
 #include "chrome/install_static/user_data_dir.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/version_info/channel.h"
 
 ChromeCrashReporterClient::ChromeCrashReporterClient() {}
diff --git a/chrome/app/chrome_crash_reporter_client_win.h b/chrome/app/chrome_crash_reporter_client_win.h
index fe37ae0..48012e38 100644
--- a/chrome/app/chrome_crash_reporter_client_win.h
+++ b/chrome/app/chrome_crash_reporter_client_win.h
@@ -6,7 +6,7 @@
 #define CHROME_APP_CHROME_CRASH_REPORTER_CLIENT_WIN_H_
 
 #include "base/macros.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 
 class ChromeCrashReporterClient : public crash_reporter::CrashReporterClient {
  public:
diff --git a/chrome/app/chrome_exe_main_win.cc b/chrome/app/chrome_exe_main_win.cc
index bdf74a4..03ee82c 100644
--- a/chrome/app/chrome_exe_main_win.cc
+++ b/chrome/app/chrome_exe_main_win.cc
@@ -37,10 +37,10 @@
 #include "chrome/install_static/install_util.h"
 #include "chrome/install_static/user_data_dir.h"
 #include "components/browser_watcher/exit_code_watcher_win.h"
-#include "components/crash/content/app/crash_switches.h"
-#include "components/crash/content/app/crashpad.h"
-#include "components/crash/content/app/fallback_crash_handling_win.h"
-#include "components/crash/content/app/run_as_crashpad_handler_win.h"
+#include "components/crash/core/app/crash_switches.h"
+#include "components/crash/core/app/crashpad.h"
+#include "components/crash/core/app/fallback_crash_handling_win.h"
+#include "components/crash/core/app/run_as_crashpad_handler_win.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/result_codes.h"
 #include "third_party/crashpad/crashpad/util/win/initial_client_data.h"
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index d6f0e7d..946f878 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -44,7 +44,7 @@
 #include "chrome/utility/chrome_content_utility_client.h"
 #include "components/component_updater/component_updater_paths.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 #include "components/crash/core/common/crash_key.h"
 #include "components/crash/core/common/crash_keys.h"
 #include "components/gwp_asan/buildflags/buildflags.h"
@@ -139,7 +139,7 @@
 #endif
 
 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
-#include "components/crash/content/app/breakpad_linux.h"
+#include "components/crash/core/app/breakpad_linux.h"
 #include "v8/include/v8-wasm-trap-handler-posix.h"
 #include "v8/include/v8.h"
 #endif
@@ -151,7 +151,7 @@
 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_ANDROID) || \
     defined(OS_LINUX)
 #include "chrome/browser/policy/policy_path_parser.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #endif
 
 #if BUILDFLAG(ENABLE_NACL)
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 57db5ba..1f0d8d8 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -7000,16 +7000,16 @@
 
       <!-- Sharing features. -->
       <message name="IDS_SHARING_REMOTE_COPY_NOTIFICATION_TITLE_TEXT_CONTENT_UNKNOWN_DEVICE" desc="Title text displayed in a Remote Copy (for text content) notification when the source device name is unknown.">
-        Text shared from other device
+        Text copied from other device
       </message>
       <message name="IDS_SHARING_REMOTE_COPY_NOTIFICATION_TITLE_TEXT_CONTENT" desc="Title text displayed in a Remote Copy (for text content) notification when the source device name is known.">
-        Text shared from <ph name="DEVICE_NAME">$1<ex>Jimmy's Pixel</ex></ph>
+        Text copied from <ph name="DEVICE_NAME">$1<ex>Jimmy's Pixel</ex></ph>
       </message>
       <message name="IDS_SHARING_REMOTE_COPY_NOTIFICATION_TITLE_IMAGE_CONTENT_UNKNOWN_DEVICE" desc="Title text displayed in a Remote Copy (for image content) notification when the source device name is unknown.">
-        Image shared from other device
+        Image copied from other device
       </message>
       <message name="IDS_SHARING_REMOTE_COPY_NOTIFICATION_TITLE_IMAGE_CONTENT" desc="Title text displayed in a Remote Copy (for image content) notification when the source device name is known.">
-        Image shared from <ph name="DEVICE_NAME">$1<ex>Jimmy's Pixel</ex></ph>
+        Image copied from <ph name="DEVICE_NAME">$1<ex>Jimmy's Pixel</ex></ph>
       </message>
       <message name="IDS_SHARING_REMOTE_COPY_NOTIFICATION_DESCRIPTION" desc="Second line of text displayed in a Remote Copy notification.">
         Press <ph name="MODIFIER_KEY_DESCRIPTION">$1<ex>Ctrl+V</ex></ph> to paste
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 9bbfd0e..ba24139 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -315,6 +315,30 @@
   <message name="IDS_OS_SETTINGS_CHANGE_PICTURE_TITLE" desc="Label for row in settings page that shows the user image picker.">
     Change device account image
   </message>
+  <message name="IDS_OS_SETTINGS_AMBIENT_MODE_TITLE" desc="Label for row in settings page that opens the ambient mode settings page to customize what you see on your screen when the device is idle.">
+    Ambient mode
+  </message>
+  <message name="IDS_OS_SETTINGS_AMBIENT_MODE_ENABLED" desc="Sub label for the ambient mode row in settings page when it is enabled.">
+    Enabled
+  </message>
+  <message name="IDS_OS_SETTINGS_AMBIENT_MODE_DISABLED" desc="Sub label for the ambient mode row in settings page when it is disabled.">
+    Disabled
+  </message>
+  <message name="IDS_OS_SETTINGS_AMBIENT_MODE_ON" desc="Label for the toggle row in ambient mode settings page when it is enabled.">
+    On
+  </message>
+  <message name="IDS_OS_SETTINGS_AMBIENT_MODE_OFF" desc="Label for the toggle row in ambient mode settings page when it is disabled.">
+    Off
+  </message>
+  <message name="IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_TITLE" desc="Label for the radio button group of topic source, where the screen contents come from.">
+    Choose what you see on your screen
+  </message>
+  <message name="IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS" desc="Label for the radio button to choose the topic source from Google Photos.">
+    Google Photos
+  </message>
+  <message name="IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY" desc="Label for the radio button to choose the topic source from art gallery.">
+    Art gallery
+  </message>
 
   <!-- Search and Assistant section. -->
   <message name="IDS_OS_SETTINGS_SEARCH_ENGINE_LABEL" desc="Label in OS settings describing search engine behavior.">
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_DISABLED.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_DISABLED.png.sha1
new file mode 100644
index 0000000..c5c76b2
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_DISABLED.png.sha1
@@ -0,0 +1 @@
+c857f2eab5600f993642e58139a1d09d1648c64f
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ENABLED.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ENABLED.png.sha1
new file mode 100644
index 0000000..44d660f2
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ENABLED.png.sha1
@@ -0,0 +1 @@
+6463a86de2a4f69cc1ec7014099fea42e887be13
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_OFF.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_OFF.png.sha1
new file mode 100644
index 0000000..d8fbb5c
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_OFF.png.sha1
@@ -0,0 +1 @@
+53edd126c4273f366fdc0d0fb2667959d75fc780
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ON.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ON.png.sha1
new file mode 100644
index 0000000..c269ccd
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ON.png.sha1
@@ -0,0 +1 @@
+d694d74276c3d458f4ff7a6e1814fc56ba0a5367
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TITLE.png.sha1
new file mode 100644
index 0000000..e691a93
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TITLE.png.sha1
@@ -0,0 +1 @@
+22d883ef436173ef138214b2731e4371af96dcce
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY.png.sha1
new file mode 100644
index 0000000..7bd499e
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY.png.sha1
@@ -0,0 +1 @@
+14f5b65f17900e406f6bc5e73ae278ddb980693d
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS.png.sha1
new file mode 100644
index 0000000..f5956ad
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS.png.sha1
@@ -0,0 +1 @@
+24473868c185ef36c5f8975a29709c25844cd6db
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_TITLE.png.sha1
new file mode 100644
index 0000000..a36d11b
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_TITLE.png.sha1
@@ -0,0 +1 @@
+ed13f271527f62a8b0bad446be8d7140e02a5351
\ No newline at end of file
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 831439c..c67ec4d 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -509,6 +509,14 @@
   <message name="IDS_SETTINGS_PASSWORDS_MANAGE_PASSWORDS" desc="Shown in the passwords section of settings. Descriptive text to inform that passwords can be accessed online. Has a link.">
     View and manage saved passwords in your <ph name="BEGIN_LINK">&lt;a is="action-link" href="$1" target="_blank"&gt;</ph>Google Account<ph name="END_LINK">&lt;/a&gt;</ph>
   </message>
+  <!-- TODO(crbug.com/1062344): Make it translateable and use definitive label once final mocks are available. -->
+  <message translateable="false" name="IDS_SETTINGS_PASSWORDS_OPT_IN_ACCOUNT_STORAGE_LABEL" desc="Label for a button in the passwords section of settings triggering opt in to passwords account storage.">
+    Opt-in to account storage
+  </message>
+  <!-- TODO(crbug.com/1062344): Make it translateable and use definitive label once final mocks are available. -->
+  <message translateable="false" name="IDS_SETTINGS_PASSWORDS_OPT_OUT_ACCOUNT_STORAGE_LABEL" desc="Label for a button in the passwords section of settings triggering opt out of passwords account storage.">
+    Opt-out of account storage
+  </message>
   <message name="IDS_SETTINGS_PASSWORDS_EXPORT_MENU_ITEM" desc="A menu item in the More Actions menu above the password list in Chrome's settings. Selecting this action will open a dialog, through which the user can export their passwords outside of Chrome.">
     Export passwords...
   </message>
diff --git a/chrome/app_shim/BUILD.gn b/chrome/app_shim/BUILD.gn
index ad45e38d..ee7e1ce 100644
--- a/chrome/app_shim/BUILD.gn
+++ b/chrome/app_shim/BUILD.gn
@@ -25,7 +25,7 @@
     "//chrome/browser/ui",
     "//chrome/common",
     "//chrome/common:mojo_bindings",
-    "//components/crash/content/app",
+    "//components/crash/core/app",
     "//components/remote_cocoa/app_shim",
     "//content/public/browser",
     "//ipc",
diff --git a/chrome/app_shim/DEPS b/chrome/app_shim/DEPS
index be81e88c..4f587ed 100644
--- a/chrome/app_shim/DEPS
+++ b/chrome/app_shim/DEPS
@@ -4,7 +4,7 @@
   "+chrome/browser/ui/cocoa",
   "+chrome/grit/generated_resources.h",
   "+chrome/installer/launcher_support",
-  "+components/crash/content/app",
+  "+components/crash/core/app",
   "+components/remote_cocoa",
   "+content/public/browser",
   "+mojo/core/embedder",
diff --git a/chrome/app_shim/chrome_main_app_mode_mac.mm b/chrome/app_shim/chrome_main_app_mode_mac.mm
index ae074fb..db1b9a4e 100644
--- a/chrome/app_shim/chrome_main_app_mode_mac.mm
+++ b/chrome/app_shim/chrome_main_app_mode_mac.mm
@@ -32,7 +32,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_paths_internal.h"
 #include "chrome/common/mac/app_mode_common.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/core/embedder/scoped_ipc_support.h"
 #include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 4a722b9..2ec3cee 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -690,6 +690,8 @@
     "media/feeds/media_feeds_service.h",
     "media/feeds/media_feeds_service_factory.cc",
     "media/feeds/media_feeds_service_factory.h",
+    "media/feeds/media_feeds_utils.cc",
+    "media/feeds/media_feeds_utils.h",
     "media/history/media_history_contents_observer.cc",
     "media/history/media_history_contents_observer.h",
     "media/history/media_history_feed_items_table.cc",
@@ -2332,6 +2334,10 @@
       "android/autofill_assistant/client_android.h",
       "android/autofill_assistant/generic_ui_controller_android.cc",
       "android/autofill_assistant/generic_ui_controller_android.h",
+      "android/autofill_assistant/generic_ui_events_android.cc",
+      "android/autofill_assistant/generic_ui_events_android.h",
+      "android/autofill_assistant/generic_ui_interactions_android.cc",
+      "android/autofill_assistant/generic_ui_interactions_android.h",
       "android/autofill_assistant/interaction_handler_android.cc",
       "android/autofill_assistant/interaction_handler_android.h",
       "android/autofill_assistant/ui_controller_android.cc",
@@ -4396,8 +4402,8 @@
       "//chrome/app/chrome_crash_reporter_client.h",
     ]
     deps += [
-      "//components/crash/content/app",
       "//components/crash/content/browser",
+      "//components/crash/core/app",
       "//components/version_info:generate_version_info",
     ]
   }
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 09d75976..2c7e6e6 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -69,6 +69,7 @@
   "+components/cookie_config",
   "+components/crash/content/app",
   "+components/crash/content/browser",
+  "+components/crash/core/app",
   "+components/crash/core/browser",
   "+components/crash/core/common",
   "+components/crx_file",
diff --git a/chrome/browser/android/autofill_assistant/client_android.cc b/chrome/browser/android/autofill_assistant/client_android.cc
index a7983ff..6e27427 100644
--- a/chrome/browser/android/autofill_assistant/client_android.cc
+++ b/chrome/browser/android/autofill_assistant/client_android.cc
@@ -199,6 +199,11 @@
   // From this point on, the UIController, in ui_ptr, is either transferred or
   // deleted.
 
+  GetPasswordManagerClient()
+      ->GetPasswordManager()
+      ->set_autofill_assistance_mode(
+          password_manager::AutofillAssistantMode::kNotRunning);
+
   if (!jother_web_contents)
     return;
 
@@ -433,6 +438,11 @@
 
     ui_controller_android_->Attach(web_contents_, this, controller_.get());
   }
+
+  GetPasswordManagerClient()
+      ->GetPasswordManager()
+      ->set_autofill_assistance_mode(
+          password_manager::AutofillAssistantMode::kManuallyCuratedScript);
 }
 
 void ClientAndroid::DestroyUI() {
@@ -532,6 +542,11 @@
   if (!controller_)
     return;
 
+  GetPasswordManagerClient()
+      ->GetPasswordManager()
+      ->set_autofill_assistance_mode(
+          password_manager::AutofillAssistantMode::kNotRunning);
+
   if (ui_controller_android_ && ui_controller_android_->IsAttached())
     DestroyUI();
 
diff --git a/chrome/browser/android/autofill_assistant/generic_ui_events_android.cc b/chrome/browser/android/autofill_assistant/generic_ui_events_android.cc
new file mode 100644
index 0000000..6e13689e
--- /dev/null
+++ b/chrome/browser/android/autofill_assistant/generic_ui_events_android.cc
@@ -0,0 +1,23 @@
+// 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 "chrome/browser/android/autofill_assistant/generic_ui_events_android.h"
+#include "base/android/jni_string.h"
+#include "chrome/android/features/autofill_assistant/jni_headers/AssistantViewEvents_jni.h"
+
+namespace autofill_assistant {
+namespace android_events {
+
+void SetOnClickListener(JNIEnv* env,
+                        base::android::ScopedJavaGlobalRef<jobject> jview,
+                        base::android::ScopedJavaGlobalRef<jobject> jdelegate,
+                        const OnViewClickedEventProto& proto) {
+  Java_AssistantViewEvents_setOnClickListener(
+      env, jview,
+      base::android::ConvertUTF8ToJavaString(env, proto.view_identifier()),
+      jdelegate);
+}
+
+}  // namespace android_events
+}  // namespace autofill_assistant
diff --git a/chrome/browser/android/autofill_assistant/generic_ui_events_android.h b/chrome/browser/android/autofill_assistant/generic_ui_events_android.h
new file mode 100644
index 0000000..d9529c1
--- /dev/null
+++ b/chrome/browser/android/autofill_assistant/generic_ui_events_android.h
@@ -0,0 +1,26 @@
+// 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 CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_GENERIC_UI_EVENTS_ANDROID_H_
+#define CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_GENERIC_UI_EVENTS_ANDROID_H_
+
+#include <string>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "components/autofill_assistant/browser/interactions.pb.h"
+
+namespace autofill_assistant {
+namespace android_events {
+
+// Sets a click listener for |jview|.
+void SetOnClickListener(JNIEnv* env,
+                        base::android::ScopedJavaGlobalRef<jobject> jview,
+                        base::android::ScopedJavaGlobalRef<jobject> jdelegate,
+                        const OnViewClickedEventProto& proto);
+
+}  // namespace android_events
+}  // namespace autofill_assistant
+
+#endif  // CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_GENERIC_UI_EVENTS_ANDROID_H_
diff --git a/chrome/browser/android/autofill_assistant/generic_ui_interactions_android.cc b/chrome/browser/android/autofill_assistant/generic_ui_interactions_android.cc
new file mode 100644
index 0000000..ee32a76
--- /dev/null
+++ b/chrome/browser/android/autofill_assistant/generic_ui_interactions_android.cc
@@ -0,0 +1,275 @@
+// 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 "chrome/browser/android/autofill_assistant/generic_ui_interactions_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/optional.h"
+#include "chrome/android/features/autofill_assistant/jni_headers/AssistantViewInteractions_jni.h"
+#include "chrome/browser/android/autofill_assistant/ui_controller_android_utils.h"
+#include "components/autofill_assistant/browser/user_model.h"
+
+namespace autofill_assistant {
+namespace android_interactions {
+
+void SetValue(base::WeakPtr<BasicInteractions> basic_interactions,
+              const SetModelValueProto& proto) {
+  if (!basic_interactions) {
+    return;
+  }
+  basic_interactions->SetValue(proto);
+}
+
+void ComputeValue(base::WeakPtr<BasicInteractions> basic_interactions,
+                  const ComputeValueProto& proto) {
+  if (!basic_interactions) {
+    return;
+  }
+  basic_interactions->ComputeValue(proto);
+}
+
+void SetUserActions(base::WeakPtr<BasicInteractions> basic_interactions,
+                    const SetUserActionsProto& proto) {
+  if (!basic_interactions) {
+    return;
+  }
+  basic_interactions->SetUserActions(proto);
+}
+
+void EndAction(base::WeakPtr<BasicInteractions> basic_interactions,
+               const EndActionProto& proto) {
+  if (!basic_interactions) {
+    return;
+  }
+  basic_interactions->EndAction(proto);
+}
+
+void ToggleUserAction(base::WeakPtr<BasicInteractions> basic_interactions,
+                      const ToggleUserActionProto& proto) {
+  if (!basic_interactions) {
+    return;
+  }
+  basic_interactions->ToggleUserAction(proto);
+}
+
+void ShowInfoPopup(const InfoPopupProto& proto,
+                   base::android::ScopedJavaGlobalRef<jobject> jcontext) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  auto jcontext_local = base::android::ScopedJavaLocalRef<jobject>(jcontext);
+  ui_controller_android_utils::ShowJavaInfoPopup(
+      env, ui_controller_android_utils::CreateJavaInfoPopup(env, proto),
+      jcontext_local);
+}
+
+void ShowListPopup(base::WeakPtr<UserModel> user_model,
+                   const ShowListPopupProto& proto,
+                   base::android::ScopedJavaGlobalRef<jobject> jcontext,
+                   base::android::ScopedJavaGlobalRef<jobject> jdelegate) {
+  if (!user_model) {
+    return;
+  }
+
+  auto item_names = user_model->GetValue(proto.item_names_model_identifier());
+  if (!item_names.has_value()) {
+    DVLOG(2) << "Failed to show list popup: '"
+             << proto.item_names_model_identifier() << "' not found in model.";
+    return;
+  }
+  if (item_names->strings().values().size() == 0) {
+    DVLOG(2) << "Failed to show list popup: the list of item names in '"
+             << proto.item_names_model_identifier() << "' was empty.";
+    return;
+  }
+
+  base::Optional<ValueProto> item_types;
+  if (proto.has_item_types_model_identifier()) {
+    item_types = user_model->GetValue(proto.item_types_model_identifier());
+    if (!item_types.has_value()) {
+      DVLOG(2) << "Failed to show list popup: '"
+               << proto.item_types_model_identifier()
+               << "' not found in the model.";
+      return;
+    }
+    if (item_types->ints().values().size() !=
+        item_names->strings().values().size()) {
+      DVLOG(2) << "Failed to show list popup: Expected item_types to contain "
+               << item_names->strings().values().size() << " integers, but got "
+               << item_types->ints().values().size();
+      return;
+    }
+  } else {
+    item_types = ValueProto();
+    for (int i = 0; i < item_names->strings().values().size(); ++i) {
+      item_types->mutable_ints()->add_values(
+          static_cast<int>(ShowListPopupProto::ENABLED));
+    }
+  }
+
+  auto selected_indices =
+      user_model->GetValue(proto.selected_item_indices_model_identifier());
+  if (!selected_indices.has_value()) {
+    DVLOG(2) << "Failed to show list popup: '"
+             << proto.selected_item_indices_model_identifier()
+             << "' not found in model.";
+    return;
+  }
+  if (!(*selected_indices == ValueProto()) &&
+      selected_indices->kind_case() != ValueProto::kInts) {
+    DVLOG(2) << "Failed to show list popup: expected '"
+             << proto.selected_item_indices_model_identifier()
+             << "' to be int[], but was of type "
+             << selected_indices->kind_case();
+    return;
+  }
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  std::vector<std::string> item_names_vec;
+  std::copy(item_names->strings().values().begin(),
+            item_names->strings().values().end(),
+            std::back_inserter(item_names_vec));
+
+  std::vector<int> item_types_vec;
+  std::copy(item_types->ints().values().begin(),
+            item_types->ints().values().end(),
+            std::back_inserter(item_types_vec));
+
+  std::vector<int> selected_indices_vec;
+  std::copy(selected_indices->ints().values().begin(),
+            selected_indices->ints().values().end(),
+            std::back_inserter(selected_indices_vec));
+
+  Java_AssistantViewInteractions_showListPopup(
+      env, jcontext, base::android::ToJavaArrayOfStrings(env, item_names_vec),
+      base::android::ToJavaIntArray(env, item_types_vec),
+      base::android::ToJavaIntArray(env, selected_indices_vec),
+      proto.allow_multiselect(),
+      base::android::ConvertUTF8ToJavaString(
+          env, proto.selected_item_indices_model_identifier()),
+      proto.selected_item_names_model_identifier().empty()
+          ? nullptr
+          : base::android::ConvertUTF8ToJavaString(
+                env, proto.selected_item_names_model_identifier()),
+      jdelegate);
+}
+
+void ShowCalendarPopup(base::WeakPtr<UserModel> user_model,
+                       const ShowCalendarPopupProto& proto,
+                       base::android::ScopedJavaGlobalRef<jobject> jcontext,
+                       base::android::ScopedJavaGlobalRef<jobject> jdelegate) {
+  if (!user_model) {
+    return;
+  }
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  auto initial_date = user_model->GetValue(proto.date_model_identifier());
+  if (!initial_date.has_value()) {
+    DVLOG(2) << "Failed to show calendar popup: "
+             << proto.date_model_identifier() << " not found in model";
+    return;
+  }
+  if (*initial_date != ValueProto() &&
+      initial_date->dates().values().size() != 1) {
+    DVLOG(2) << "Failed to show calendar popup: date_model_identifier must be "
+                "empty or contain single date, but was "
+             << *initial_date;
+    return;
+  }
+
+  auto min_date = user_model->GetValue(proto.min_date_model_identifier());
+  if (!min_date.has_value() || min_date->dates().values().size() != 1) {
+    DVLOG(2) << "Failed to show calendar popup: min_date not found or invalid "
+                "in user model at "
+             << proto.min_date_model_identifier();
+    return;
+  }
+
+  auto max_date = user_model->GetValue(proto.max_date_model_identifier());
+  if (!max_date.has_value() || max_date->dates().values().size() != 1) {
+    DVLOG(2) << "Failed to show calendar popup: max_date not found or invalid "
+                "in user model at "
+             << proto.max_date_model_identifier();
+    return;
+  }
+
+  jboolean jsuccess = Java_AssistantViewInteractions_showCalendarPopup(
+      env, jcontext,
+      *initial_date != ValueProto()
+          ? ui_controller_android_utils::ToJavaValue(env, *initial_date)
+          : nullptr,
+      ui_controller_android_utils::ToJavaValue(env, *min_date),
+      ui_controller_android_utils::ToJavaValue(env, *max_date),
+      base::android::ConvertUTF8ToJavaString(env,
+                                             proto.date_model_identifier()),
+      jdelegate);
+  if (!jsuccess) {
+    DVLOG(2) << "Failed to show calendar popup: JNI call failed";
+  }
+}
+
+void SetViewText(
+    base::WeakPtr<UserModel> user_model,
+    const SetTextProto& proto,
+    std::map<std::string, base::android::ScopedJavaGlobalRef<jobject>>* views) {
+  if (!user_model) {
+    return;
+  }
+
+  auto text = user_model->GetValue(proto.model_identifier());
+  if (!text.has_value()) {
+    DVLOG(2) << "Failed to set text for " << proto.view_identifier() << ": "
+             << proto.model_identifier() << " not found in model";
+    return;
+  }
+  if (text->strings().values_size() != 1) {
+    DVLOG(2) << "Failed to set text for " << proto.view_identifier()
+             << ": expected " << proto.model_identifier()
+             << " to contain single string, but was instead " << *text;
+    return;
+  }
+
+  auto jview = views->find(proto.view_identifier());
+  if (jview == views->end()) {
+    DVLOG(2) << "Failed to set text for " << proto.view_identifier() << ": "
+             << " view not found";
+    return;
+  }
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_AssistantViewInteractions_setViewText(
+      env, jview->second,
+      base::android::ConvertUTF8ToJavaString(env, text->strings().values(0)));
+}
+
+void SetViewVisibility(
+    base::WeakPtr<UserModel> user_model,
+    const SetViewVisibilityProto& proto,
+    std::map<std::string, base::android::ScopedJavaGlobalRef<jobject>>* views) {
+  if (!user_model) {
+    return;
+  }
+
+  auto jview = views->find(proto.view_identifier());
+  if (jview == views->end()) {
+    DVLOG(2) << "Failed to set view visibility for " << proto.view_identifier()
+             << ": view not found";
+    return;
+  }
+
+  auto visible_value = user_model->GetValue(proto.model_identifier());
+  if (!visible_value.has_value() ||
+      visible_value->booleans().values_size() != 1) {
+    DVLOG(2) << "Failed to set view visibility for " << proto.view_identifier()
+             << ": " << proto.model_identifier()
+             << " did not contain single boolean";
+    return;
+  }
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_AssistantViewInteractions_setViewVisibility(
+      env, jview->second,
+      ui_controller_android_utils::ToJavaValue(env, *visible_value));
+}
+
+}  // namespace android_interactions
+}  // namespace autofill_assistant
diff --git a/chrome/browser/android/autofill_assistant/generic_ui_interactions_android.h b/chrome/browser/android/autofill_assistant/generic_ui_interactions_android.h
new file mode 100644
index 0000000..e15f0f6
--- /dev/null
+++ b/chrome/browser/android/autofill_assistant/generic_ui_interactions_android.h
@@ -0,0 +1,70 @@
+// 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 CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_GENERIC_UI_INTERACTIONS_ANDROID_H_
+#define CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_GENERIC_UI_INTERACTIONS_ANDROID_H_
+
+#include <string>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/memory/weak_ptr.h"
+#include "components/autofill_assistant/browser/basic_interactions.h"
+#include "components/autofill_assistant/browser/interactions.pb.h"
+
+namespace autofill_assistant {
+namespace android_interactions {
+
+// Writes a value to the model.
+void SetValue(base::WeakPtr<BasicInteractions> basic_interactions,
+              const SetModelValueProto& proto);
+
+// Computes a value and writes it to the model.
+void ComputeValue(base::WeakPtr<BasicInteractions> basic_interactions,
+                  const ComputeValueProto& proto);
+
+// Sets the list of available user actions (i.e., chips and direct actions).
+void SetUserActions(base::WeakPtr<BasicInteractions> basic_interactions,
+                    const SetUserActionsProto& proto);
+
+// Ends the current ShowGenericUi action.
+void EndAction(base::WeakPtr<BasicInteractions> basic_interactions,
+               const EndActionProto& proto);
+
+// Enables or disables a particular user action.
+void ToggleUserAction(base::WeakPtr<BasicInteractions> basic_interactions,
+                      const ToggleUserActionProto& proto);
+
+// Displays an info popup on the screen.
+void ShowInfoPopup(const InfoPopupProto& proto,
+                   base::android::ScopedJavaGlobalRef<jobject> jcontext);
+
+// Displays a list popup on the screen.
+void ShowListPopup(base::WeakPtr<UserModel> user_model,
+                   const ShowListPopupProto& proto,
+                   base::android::ScopedJavaGlobalRef<jobject> jcontext,
+                   base::android::ScopedJavaGlobalRef<jobject> jdelegate);
+
+// Displays a calendar popup on the screen.
+void ShowCalendarPopup(base::WeakPtr<UserModel> user_model,
+                       const ShowCalendarPopupProto& proto,
+                       base::android::ScopedJavaGlobalRef<jobject> jcontext,
+                       base::android::ScopedJavaGlobalRef<jobject> jdelegate);
+
+// Sets the text of a view.
+void SetViewText(
+    base::WeakPtr<UserModel> user_model,
+    const SetTextProto& proto,
+    std::map<std::string, base::android::ScopedJavaGlobalRef<jobject>>* views);
+
+// Sets the visibility of a view.
+void SetViewVisibility(
+    base::WeakPtr<UserModel> user_model,
+    const SetViewVisibilityProto& proto,
+    std::map<std::string, base::android::ScopedJavaGlobalRef<jobject>>* views);
+
+}  // namespace android_interactions
+}  // namespace autofill_assistant
+
+#endif  // CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_GENERIC_UI_INTERACTIONS_ANDROID_H_
diff --git a/chrome/browser/android/autofill_assistant/interaction_handler_android.cc b/chrome/browser/android/autofill_assistant/interaction_handler_android.cc
index d23ecf8..071c124 100644
--- a/chrome/browser/android/autofill_assistant/interaction_handler_android.cc
+++ b/chrome/browser/android/autofill_assistant/interaction_handler_android.cc
@@ -5,14 +5,13 @@
 #include "chrome/browser/android/autofill_assistant/interaction_handler_android.h"
 #include <algorithm>
 #include <vector>
-#include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/callback_helpers.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
-#include "chrome/android/features/autofill_assistant/jni_headers/AssistantViewInteractions_jni.h"
 #include "chrome/browser/android/autofill_assistant/generic_ui_controller_android.h"
-#include "chrome/browser/android/autofill_assistant/ui_controller_android_utils.h"
+#include "chrome/browser/android/autofill_assistant/generic_ui_events_android.h"
+#include "chrome/browser/android/autofill_assistant/generic_ui_interactions_android.h"
 #include "components/autofill_assistant/browser/basic_interactions.h"
 #include "components/autofill_assistant/browser/interactions.pb.h"
 #include "components/autofill_assistant/browser/ui_delegate.h"
@@ -23,50 +22,8 @@
 
 namespace {
 
-// Note regarding Try* methods: these are simple wrappers around basic
-// interactions. They are needed because it is impossible to directly bind to
-// non-void return methods with a weak ptr.
-void TrySetValue(base::WeakPtr<BasicInteractions> basic_interactions,
-                 const SetModelValueProto& proto) {
-  if (!basic_interactions) {
-    return;
-  }
-  basic_interactions->SetValue(proto);
-}
-
-void TryComputeValue(base::WeakPtr<BasicInteractions> basic_interactions,
-                     const ComputeValueProto& proto) {
-  if (!basic_interactions) {
-    return;
-  }
-  basic_interactions->ComputeValue(proto);
-}
-
-void SetUserActionsIgnoreReturn(
-    base::WeakPtr<BasicInteractions> basic_interactions,
-    const SetUserActionsProto& proto) {
-  if (!basic_interactions) {
-    return;
-  }
-  basic_interactions->SetUserActions(proto);
-}
-
-void TryEndAction(base::WeakPtr<BasicInteractions> basic_interactions,
-                  const EndActionProto& proto) {
-  if (!basic_interactions) {
-    return;
-  }
-  basic_interactions->EndAction(proto);
-}
-
-void TryToggleUserAction(base::WeakPtr<BasicInteractions> basic_interactions,
-                         const ToggleUserActionProto& proto) {
-  if (!basic_interactions) {
-    return;
-  }
-  basic_interactions->ToggleUserAction(proto);
-}
-
+// A simple wrapper around a basic interaction, needed because we can't directly
+// bind a repeating callback to a method with non-void return value.
 void TryRunConditionalCallback(
     base::WeakPtr<BasicInteractions> basic_interactions,
     const std::string& condition_identifier,
@@ -77,224 +34,6 @@
   basic_interactions->RunConditionalCallback(condition_identifier, callback);
 }
 
-void ShowInfoPopup(const InfoPopupProto& proto,
-                   base::android::ScopedJavaGlobalRef<jobject> jcontext) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  auto jcontext_local = base::android::ScopedJavaLocalRef<jobject>(jcontext);
-  ui_controller_android_utils::ShowJavaInfoPopup(
-      env, ui_controller_android_utils::CreateJavaInfoPopup(env, proto),
-      jcontext_local);
-}
-
-void ShowListPopup(base::WeakPtr<UserModel> user_model,
-                   const ShowListPopupProto& proto,
-                   base::android::ScopedJavaGlobalRef<jobject> jcontext,
-                   base::android::ScopedJavaGlobalRef<jobject> jdelegate) {
-  if (!user_model) {
-    return;
-  }
-
-  auto item_names = user_model->GetValue(proto.item_names_model_identifier());
-  if (!item_names.has_value()) {
-    DVLOG(2) << "Failed to show list popup: '"
-             << proto.item_names_model_identifier() << "' not found in model.";
-    return;
-  }
-  if (item_names->strings().values().size() == 0) {
-    DVLOG(2) << "Failed to show list popup: the list of item names in '"
-             << proto.item_names_model_identifier() << "' was empty.";
-    return;
-  }
-
-  base::Optional<ValueProto> item_types;
-  if (proto.has_item_types_model_identifier()) {
-    item_types = user_model->GetValue(proto.item_types_model_identifier());
-    if (!item_types.has_value()) {
-      DVLOG(2) << "Failed to show list popup: '"
-               << proto.item_types_model_identifier()
-               << "' not found in the model.";
-      return;
-    }
-    if (item_types->ints().values().size() !=
-        item_names->strings().values().size()) {
-      DVLOG(2) << "Failed to show list popup: Expected item_types to contain "
-               << item_names->strings().values().size() << " integers, but got "
-               << item_types->ints().values().size();
-      return;
-    }
-  } else {
-    item_types = ValueProto();
-    for (int i = 0; i < item_names->strings().values().size(); ++i) {
-      item_types->mutable_ints()->add_values(
-          static_cast<int>(ShowListPopupProto::ENABLED));
-    }
-  }
-
-  auto selected_indices =
-      user_model->GetValue(proto.selected_item_indices_model_identifier());
-  if (!selected_indices.has_value()) {
-    DVLOG(2) << "Failed to show list popup: '"
-             << proto.selected_item_indices_model_identifier()
-             << "' not found in model.";
-    return;
-  }
-  if (!(*selected_indices == ValueProto()) &&
-      selected_indices->kind_case() != ValueProto::kInts) {
-    DVLOG(2) << "Failed to show list popup: expected '"
-             << proto.selected_item_indices_model_identifier()
-             << "' to be int[], but was of type "
-             << selected_indices->kind_case();
-    return;
-  }
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  std::vector<std::string> item_names_vec;
-  std::copy(item_names->strings().values().begin(),
-            item_names->strings().values().end(),
-            std::back_inserter(item_names_vec));
-
-  std::vector<int> item_types_vec;
-  std::copy(item_types->ints().values().begin(),
-            item_types->ints().values().end(),
-            std::back_inserter(item_types_vec));
-
-  std::vector<int> selected_indices_vec;
-  std::copy(selected_indices->ints().values().begin(),
-            selected_indices->ints().values().end(),
-            std::back_inserter(selected_indices_vec));
-
-  Java_AssistantViewInteractions_showListPopup(
-      env, jcontext, base::android::ToJavaArrayOfStrings(env, item_names_vec),
-      base::android::ToJavaIntArray(env, item_types_vec),
-      base::android::ToJavaIntArray(env, selected_indices_vec),
-      proto.allow_multiselect(),
-      base::android::ConvertUTF8ToJavaString(
-          env, proto.selected_item_indices_model_identifier()),
-      proto.selected_item_names_model_identifier().empty()
-          ? nullptr
-          : base::android::ConvertUTF8ToJavaString(
-                env, proto.selected_item_names_model_identifier()),
-      jdelegate);
-}
-
-void ShowCalendarPopup(base::WeakPtr<UserModel> user_model,
-                       const ShowCalendarPopupProto& proto,
-                       base::android::ScopedJavaGlobalRef<jobject> jcontext,
-                       base::android::ScopedJavaGlobalRef<jobject> jdelegate) {
-  if (!user_model) {
-    return;
-  }
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  auto initial_date = user_model->GetValue(proto.date_model_identifier());
-  if (!initial_date.has_value()) {
-    DVLOG(2) << "Failed to show calendar popup: "
-             << proto.date_model_identifier() << " not found in model";
-    return;
-  }
-  if (*initial_date != ValueProto() &&
-      initial_date->dates().values().size() != 1) {
-    DVLOG(2) << "Failed to show calendar popup: date_model_identifier must be "
-                "empty or contain single date, but was "
-             << *initial_date;
-    return;
-  }
-
-  auto min_date = user_model->GetValue(proto.min_date_model_identifier());
-  if (!min_date.has_value() || min_date->dates().values().size() != 1) {
-    DVLOG(2) << "Failed to show calendar popup: min_date not found or invalid "
-                "in user model at "
-             << proto.min_date_model_identifier();
-    return;
-  }
-
-  auto max_date = user_model->GetValue(proto.max_date_model_identifier());
-  if (!max_date.has_value() || max_date->dates().values().size() != 1) {
-    DVLOG(2) << "Failed to show calendar popup: max_date not found or invalid "
-                "in user model at "
-             << proto.max_date_model_identifier();
-    return;
-  }
-
-  jboolean jsuccess = Java_AssistantViewInteractions_showCalendarPopup(
-      env, jcontext,
-      *initial_date != ValueProto()
-          ? ui_controller_android_utils::ToJavaValue(env, *initial_date)
-          : nullptr,
-      ui_controller_android_utils::ToJavaValue(env, *min_date),
-      ui_controller_android_utils::ToJavaValue(env, *max_date),
-      base::android::ConvertUTF8ToJavaString(env,
-                                             proto.date_model_identifier()),
-      jdelegate);
-  if (!jsuccess) {
-    DVLOG(2) << "Failed to show calendar popup: JNI call failed";
-  }
-}
-
-void SetViewText(
-    base::WeakPtr<UserModel> user_model,
-    const SetTextProto& proto,
-    std::map<std::string, base::android::ScopedJavaGlobalRef<jobject>>* views) {
-  if (!user_model) {
-    return;
-  }
-
-  auto text = user_model->GetValue(proto.model_identifier());
-  if (!text.has_value()) {
-    DVLOG(2) << "Failed to set text for " << proto.view_identifier() << ": "
-             << proto.model_identifier() << " not found in model";
-    return;
-  }
-  if (text->strings().values_size() != 1) {
-    DVLOG(2) << "Failed to set text for " << proto.view_identifier()
-             << ": expected " << proto.model_identifier()
-             << " to contain single string, but was instead " << *text;
-    return;
-  }
-
-  auto jview = views->find(proto.view_identifier());
-  if (jview == views->end()) {
-    DVLOG(2) << "Failed to set text for " << proto.view_identifier() << ": "
-             << " view not found";
-    return;
-  }
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_AssistantViewInteractions_setViewText(
-      env, jview->second,
-      base::android::ConvertUTF8ToJavaString(env, text->strings().values(0)));
-}
-
-void SetViewVisibility(
-    base::WeakPtr<UserModel> user_model,
-    const SetViewVisibilityProto& proto,
-    std::map<std::string, base::android::ScopedJavaGlobalRef<jobject>>* views) {
-  if (!user_model) {
-    return;
-  }
-
-  auto jview = views->find(proto.view_identifier());
-  if (jview == views->end()) {
-    DVLOG(2) << "Failed to set view visibility for " << proto.view_identifier()
-             << ": view not found";
-    return;
-  }
-
-  auto visible_value = user_model->GetValue(proto.model_identifier());
-  if (!visible_value.has_value() ||
-      visible_value->booleans().values_size() != 1) {
-    DVLOG(2) << "Failed to set view visibility for " << proto.view_identifier()
-             << ": " << proto.model_identifier()
-             << " did not contain single boolean";
-    return;
-  }
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_AssistantViewInteractions_setViewVisibility(
-      env, jview->second,
-      ui_controller_android_utils::ToJavaValue(env, *visible_value));
-}
-
 base::Optional<EventHandler::EventKey> CreateEventKeyFromProto(
     const EventProto& proto,
     JNIEnv* env,
@@ -311,11 +50,8 @@
                 << proto.on_view_clicked().view_identifier() << "' found";
         return base::nullopt;
       }
-      Java_AssistantViewInteractions_setOnClickListener(
-          env, jview->second,
-          base::android::ConvertUTF8ToJavaString(
-              env, proto.on_view_clicked().view_identifier()),
-          jdelegate);
+      android_events::SetOnClickListener(env, jview->second, jdelegate,
+                                         proto.on_view_clicked());
       return base::Optional<EventHandler::EventKey>(
           {proto.kind_case(), proto.on_view_clicked().view_identifier()});
     }
@@ -346,11 +82,12 @@
         return base::nullopt;
       }
       return base::Optional<InteractionHandlerAndroid::InteractionCallback>(
-          base::BindRepeating(&TrySetValue, basic_interactions->GetWeakPtr(),
+          base::BindRepeating(&android_interactions::SetValue,
+                              basic_interactions->GetWeakPtr(),
                               proto.set_value()));
     case CallbackProto::kShowInfoPopup: {
       return base::Optional<InteractionHandlerAndroid::InteractionCallback>(
-          base::BindRepeating(&ShowInfoPopup,
+          base::BindRepeating(&android_interactions::ShowInfoPopup,
                               proto.show_info_popup().info_popup(), jcontext));
     }
     case CallbackProto::kShowListPopup:
@@ -367,8 +104,9 @@
         return base::nullopt;
       }
       return base::Optional<InteractionHandlerAndroid::InteractionCallback>(
-          base::BindRepeating(&ShowListPopup, user_model->GetWeakPtr(),
-                              proto.show_list_popup(), jcontext, jdelegate));
+          base::BindRepeating(&android_interactions::ShowListPopup,
+                              user_model->GetWeakPtr(), proto.show_list_popup(),
+                              jcontext, jdelegate));
     case CallbackProto::kComputeValue:
       if (proto.compute_value().result_model_identifier().empty()) {
         VLOG(1) << "Error creating ComputeValue interaction: "
@@ -376,7 +114,7 @@
         return base::nullopt;
       }
       return base::Optional<InteractionHandlerAndroid::InteractionCallback>(
-          base::BindRepeating(&TryComputeValue,
+          base::BindRepeating(&android_interactions::ComputeValue,
                               basic_interactions->GetWeakPtr(),
                               proto.compute_value()));
     case CallbackProto::kSetUserActions:
@@ -386,12 +124,13 @@
         return base::nullopt;
       }
       return base::Optional<InteractionHandlerAndroid::InteractionCallback>(
-          base::BindRepeating(&SetUserActionsIgnoreReturn,
+          base::BindRepeating(&android_interactions::SetUserActions,
                               basic_interactions->GetWeakPtr(),
                               proto.set_user_actions()));
     case CallbackProto::kEndAction:
       return base::Optional<InteractionHandlerAndroid::InteractionCallback>(
-          base::BindRepeating(&TryEndAction, basic_interactions->GetWeakPtr(),
+          base::BindRepeating(&android_interactions::EndAction,
+                              basic_interactions->GetWeakPtr(),
                               proto.end_action()));
     case CallbackProto::kShowCalendarPopup:
       if (proto.show_calendar_popup().date_model_identifier().empty()) {
@@ -400,7 +139,8 @@
         return base::nullopt;
       }
       return base::Optional<InteractionHandlerAndroid::InteractionCallback>(
-          base::BindRepeating(&ShowCalendarPopup, user_model->GetWeakPtr(),
+          base::BindRepeating(&android_interactions::ShowCalendarPopup,
+                              user_model->GetWeakPtr(),
                               proto.show_calendar_popup(), jcontext,
                               jdelegate));
     case CallbackProto::kSetText:
@@ -415,8 +155,9 @@
         return base::nullopt;
       }
       return base::Optional<InteractionHandlerAndroid::InteractionCallback>(
-          base::BindRepeating(&SetViewText, user_model->GetWeakPtr(),
-                              proto.set_text(), views));
+          base::BindRepeating(&android_interactions::SetViewText,
+                              user_model->GetWeakPtr(), proto.set_text(),
+                              views));
     case CallbackProto::kToggleUserAction:
       if (proto.toggle_user_action().user_actions_model_identifier().empty()) {
         VLOG(1) << "Error creating ToggleUserAction interaction: "
@@ -434,7 +175,7 @@
         return base::nullopt;
       }
       return base::Optional<InteractionHandlerAndroid::InteractionCallback>(
-          base::BindRepeating(&TryToggleUserAction,
+          base::BindRepeating(&android_interactions::ToggleUserAction,
                               basic_interactions->GetWeakPtr(),
                               proto.toggle_user_action()));
     case CallbackProto::kSetViewVisibility:
@@ -449,7 +190,8 @@
         return base::nullopt;
       }
       return base::Optional<InteractionHandlerAndroid::InteractionCallback>(
-          base::BindRepeating(&SetViewVisibility, user_model->GetWeakPtr(),
+          base::BindRepeating(&android_interactions::SetViewVisibility,
+                              user_model->GetWeakPtr(),
                               proto.set_view_visibility(), views));
     case CallbackProto::KIND_NOT_SET:
       VLOG(1) << "Error creating interaction: kind not set";
diff --git a/chrome/browser/autofill/accessory_controller.h b/chrome/browser/autofill/accessory_controller.h
index 75d3d3a..2bfaa5f 100644
--- a/chrome/browser/autofill/accessory_controller.h
+++ b/chrome/browser/autofill/accessory_controller.h
@@ -24,6 +24,10 @@
 
   // Triggered when a user selects an option.
   virtual void OnOptionSelected(autofill::AccessoryAction selected_action) = 0;
+
+  // Triggered when a user changes a toggle.
+  virtual void OnToggleChanged(autofill::AccessoryAction toggled_action,
+                               bool enabled) = 0;
 };
 
 #endif  // CHROME_BROWSER_AUTOFILL_ACCESSORY_CONTROLLER_H_
diff --git a/chrome/browser/autofill/address_accessory_controller_impl.cc b/chrome/browser/autofill/address_accessory_controller_impl.cc
index 89e31ffa..403054d 100644
--- a/chrome/browser/autofill/address_accessory_controller_impl.cc
+++ b/chrome/browser/autofill/address_accessory_controller_impl.cc
@@ -128,6 +128,13 @@
                << static_cast<int>(selected_action);
 }
 
+void AddressAccessoryControllerImpl::OnToggleChanged(
+    AccessoryAction toggled_action,
+    bool enabled) {
+  NOTREACHED() << "Unhandled toggled action: "
+               << static_cast<int>(toggled_action);
+}
+
 void AddressAccessoryControllerImpl::RefreshSuggestions() {
   std::vector<AutofillProfile*> profiles = GetProfiles();
   base::string16 title_or_empty_message;
diff --git a/chrome/browser/autofill/address_accessory_controller_impl.h b/chrome/browser/autofill/address_accessory_controller_impl.h
index c152cb9..943aba23 100644
--- a/chrome/browser/autofill/address_accessory_controller_impl.h
+++ b/chrome/browser/autofill/address_accessory_controller_impl.h
@@ -35,6 +35,7 @@
   // AccessoryController:
   void OnFillingTriggered(const autofill::UserInfo::Field& selection) override;
   void OnOptionSelected(AccessoryAction selected_action) override;
+  void OnToggleChanged(AccessoryAction toggled_action, bool enabled) override;
 
   // AddressAccessoryController:
   void RefreshSuggestions() override;
diff --git a/chrome/browser/autofill/autofill_keyboard_accessory_adapter.cc b/chrome/browser/autofill/autofill_keyboard_accessory_adapter.cc
index dc15d74..aa5071c 100644
--- a/chrome/browser/autofill/autofill_keyboard_accessory_adapter.cc
+++ b/chrome/browser/autofill/autofill_keyboard_accessory_adapter.cc
@@ -197,11 +197,11 @@
   return controller_ && controller_->IsRTL();
 }
 
-const std::vector<autofill::Suggestion>
-AutofillKeyboardAccessoryAdapter::GetSuggestions() {
+std::vector<Suggestion> AutofillKeyboardAccessoryAdapter::GetSuggestions()
+    const {
   if (!controller_)
-    return std::vector<autofill::Suggestion>();
-  std::vector<autofill::Suggestion> suggestions = controller_->GetSuggestions();
+    return std::vector<Suggestion>();
+  std::vector<Suggestion> suggestions = controller_->GetSuggestions();
   if (front_element_.has_value()) {
     std::rotate(suggestions.begin(),
                 suggestions.begin() + front_element_.value(),
diff --git a/chrome/browser/autofill/autofill_keyboard_accessory_adapter.h b/chrome/browser/autofill/autofill_keyboard_accessory_adapter.h
index 9d12ec0..aa4358a1 100644
--- a/chrome/browser/autofill/autofill_keyboard_accessory_adapter.h
+++ b/chrome/browser/autofill/autofill_keyboard_accessory_adapter.h
@@ -84,7 +84,7 @@
   gfx::NativeView container_view() const override;
   const gfx::RectF& element_bounds() const override;
   bool IsRTL() const override;
-  const std::vector<autofill::Suggestion> GetSuggestions() override;
+  std::vector<Suggestion> GetSuggestions() const override;
 
   void OnDeletionConfirmed(int index);
 
diff --git a/chrome/browser/autofill/credit_card_accessory_controller_impl.cc b/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
index 8c6cd3c..133931a 100644
--- a/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
+++ b/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
@@ -120,6 +120,13 @@
                << static_cast<int>(selected_action);
 }
 
+void CreditCardAccessoryControllerImpl::OnToggleChanged(
+    AccessoryAction toggled_action,
+    bool enabled) {
+  NOTREACHED() << "Unhandled toggled action: "
+               << static_cast<int>(toggled_action);
+}
+
 // static
 bool CreditCardAccessoryController::AllowedForWebContents(
     content::WebContents* web_contents) {
diff --git a/chrome/browser/autofill/credit_card_accessory_controller_impl.h b/chrome/browser/autofill/credit_card_accessory_controller_impl.h
index f226b2c..e5984d1 100644
--- a/chrome/browser/autofill/credit_card_accessory_controller_impl.h
+++ b/chrome/browser/autofill/credit_card_accessory_controller_impl.h
@@ -27,6 +27,7 @@
   // AccessoryController:
   void OnFillingTriggered(const UserInfo::Field& selection) override;
   void OnOptionSelected(AccessoryAction selected_action) override;
+  void OnToggleChanged(AccessoryAction toggled_action, bool enabled) override;
 
   // CreditCardAccessoryController:
   void RefreshSuggestions() override;
diff --git a/chrome/browser/autofill/manual_filling_controller.h b/chrome/browser/autofill/manual_filling_controller.h
index eab1f05e..b6b4e87 100644
--- a/chrome/browser/autofill/manual_filling_controller.h
+++ b/chrome/browser/autofill/manual_filling_controller.h
@@ -111,6 +111,11 @@
   virtual void OnOptionSelected(
       autofill::AccessoryAction selected_action) const = 0;
 
+  // Called by the UI code because a user toggled the |toggled_action|,
+  // such as "Save passwords for this site".
+  virtual void OnToggleChanged(autofill::AccessoryAction toggled_action,
+                               bool enabled) const = 0;
+
   // -----------------
   // Member accessors:
   // -----------------
diff --git a/chrome/browser/autofill/manual_filling_controller_impl.cc b/chrome/browser/autofill/manual_filling_controller_impl.cc
index d556867..f334a97 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl.cc
+++ b/chrome/browser/autofill/manual_filling_controller_impl.cc
@@ -177,6 +177,15 @@
   controller->OnOptionSelected(selected_action);
 }
 
+void ManualFillingControllerImpl::OnToggleChanged(
+    AccessoryAction toggled_action,
+    bool enabled) const {
+  AccessoryController* controller = GetControllerForAction(toggled_action);
+  if (!controller)
+    return;  // Controller not available anymore.
+  controller->OnToggleChanged(toggled_action, enabled);
+}
+
 gfx::NativeView ManualFillingControllerImpl::container_view() const {
   return web_contents_->GetNativeView();
 }
@@ -289,12 +298,12 @@
     case AccessoryAction::GENERATE_PASSWORD_MANUAL:
     case AccessoryAction::MANAGE_PASSWORDS:
     case AccessoryAction::GENERATE_PASSWORD_AUTOMATIC:
+    case AccessoryAction::TOGGLE_SAVE_PASSWORDS:
       return GetPasswordController();
     case AccessoryAction::MANAGE_ADDRESSES:
       return address_controller_.get();
     case AccessoryAction::MANAGE_CREDIT_CARDS:
       return cc_controller_.get();
-    case AccessoryAction::TOGGLE_SAVE_PASSWORDS:
     case AccessoryAction::AUTOFILL_SUGGESTION:
     case AccessoryAction::COUNT:
       break;  // Intentional failure;
diff --git a/chrome/browser/autofill/manual_filling_controller_impl.h b/chrome/browser/autofill/manual_filling_controller_impl.h
index 58d693a..0d86d887 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl.h
+++ b/chrome/browser/autofill/manual_filling_controller_impl.h
@@ -45,6 +45,8 @@
                           const autofill::UserInfo::Field& selection) override;
   void OnOptionSelected(
       autofill::AccessoryAction selected_action) const override;
+  void OnToggleChanged(autofill::AccessoryAction toggled_action,
+                       bool enabled) const override;
   gfx::NativeView container_view() const override;
 
   // Returns a weak pointer for this object.
diff --git a/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc b/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
index b08d3485..55a15adc 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
+++ b/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/autofill/mock_password_accessory_controller.h"
 #include "chrome/browser/password_manager/password_accessory_controller.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/autofill/core/browser/ui/accessory_sheet_enums.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "content/public/test/browser_task_environment.h"
@@ -309,3 +310,15 @@
               OnOptionSelected(AccessoryAction::GENERATE_PASSWORD_MANUAL));
   controller()->OnOptionSelected(AccessoryAction::GENERATE_PASSWORD_MANUAL);
 }
+
+TEST_F(ManualFillingControllerTest, OnSavePasswordsToggledTrue) {
+  EXPECT_CALL(mock_pwd_controller_,
+              OnToggleChanged(AccessoryAction::TOGGLE_SAVE_PASSWORDS, true));
+  controller()->OnToggleChanged(AccessoryAction::TOGGLE_SAVE_PASSWORDS, true);
+}
+
+TEST_F(ManualFillingControllerTest, OnSavePasswordsToggledFalse) {
+  EXPECT_CALL(mock_pwd_controller_,
+              OnToggleChanged(AccessoryAction::TOGGLE_SAVE_PASSWORDS, false));
+  controller()->OnToggleChanged(AccessoryAction::TOGGLE_SAVE_PASSWORDS, false);
+}
diff --git a/chrome/browser/autofill/mock_address_accessory_controller.h b/chrome/browser/autofill/mock_address_accessory_controller.h
index 3b37062..b8d70de 100644
--- a/chrome/browser/autofill/mock_address_accessory_controller.h
+++ b/chrome/browser/autofill/mock_address_accessory_controller.h
@@ -20,6 +20,8 @@
   MOCK_METHOD1(OnFillingTriggered, void(const autofill::UserInfo::Field&));
   MOCK_METHOD1(OnOptionSelected,
                void(autofill::AccessoryAction selected_action));
+  MOCK_METHOD2(OnToggleChanged,
+               void(autofill::AccessoryAction toggled_action, bool enabled));
   MOCK_METHOD0(RefreshSuggestions, void());
 
  private:
diff --git a/chrome/browser/autofill/mock_autofill_popup_controller.h b/chrome/browser/autofill/mock_autofill_popup_controller.h
index 3913437..dd35a57 100644
--- a/chrome/browser/autofill/mock_autofill_popup_controller.h
+++ b/chrome/browser/autofill/mock_autofill_popup_controller.h
@@ -43,7 +43,7 @@
   // AutofillPopupController
   MOCK_METHOD0(OnSuggestionsChanged, void());
   MOCK_METHOD1(AcceptSuggestion, void(int index));
-  const std::vector<autofill::Suggestion> GetSuggestions() override {
+  std::vector<Suggestion> GetSuggestions() const override {
     return suggestions_;
   }
 
diff --git a/chrome/browser/autofill/mock_credit_card_accessory_controller.h b/chrome/browser/autofill/mock_credit_card_accessory_controller.h
index 0451f5b4..4433317a 100644
--- a/chrome/browser/autofill/mock_credit_card_accessory_controller.h
+++ b/chrome/browser/autofill/mock_credit_card_accessory_controller.h
@@ -23,6 +23,7 @@
 
   MOCK_METHOD1(OnFillingTriggered, void(const autofill::UserInfo::Field&));
   MOCK_METHOD1(OnOptionSelected, void(autofill::AccessoryAction));
+  MOCK_METHOD2(OnToggleChanged, void(autofill::AccessoryAction, bool));
   MOCK_METHOD0(RefreshSuggestions, void());
   MOCK_METHOD0(OnPersonalDataChanged, void());
   MOCK_METHOD3(OnCreditCardFetched,
diff --git a/chrome/browser/autofill/mock_manual_filling_controller.h b/chrome/browser/autofill/mock_manual_filling_controller.h
index 7e7cd3e..b55208e 100644
--- a/chrome/browser/autofill/mock_manual_filling_controller.h
+++ b/chrome/browser/autofill/mock_manual_filling_controller.h
@@ -28,6 +28,9 @@
                     const autofill::UserInfo::Field&));
   MOCK_CONST_METHOD1(OnOptionSelected,
                      void(autofill::AccessoryAction selected_action));
+  MOCK_CONST_METHOD2(OnToggleChanged,
+                     void(autofill::AccessoryAction toggled_action,
+                          bool enabled));
   MOCK_CONST_METHOD0(container_view, gfx::NativeView());
 
  private:
diff --git a/chrome/browser/autofill/mock_password_accessory_controller.h b/chrome/browser/autofill/mock_password_accessory_controller.h
index 579d440..242c73431 100644
--- a/chrome/browser/autofill/mock_password_accessory_controller.h
+++ b/chrome/browser/autofill/mock_password_accessory_controller.h
@@ -32,6 +32,8 @@
   MOCK_METHOD1(OnFillingTriggered, void(const autofill::UserInfo::Field&));
   MOCK_METHOD1(OnOptionSelected,
                void(autofill::AccessoryAction selected_action));
+  MOCK_METHOD2(OnToggleChanged,
+               void(autofill::AccessoryAction toggled_action, bool enabled));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockPasswordAccessoryController);
diff --git a/chrome/browser/browser_switcher/browser_switcher_service.cc b/chrome/browser/browser_switcher/browser_switcher_service.cc
index a66ed685..7ccfc09 100644
--- a/chrome/browser/browser_switcher/browser_switcher_service.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_service.cc
@@ -140,7 +140,7 @@
     auto request = std::make_unique<network::ResourceRequest>();
     request->url = source.url;
     request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
-    request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+    request->credentials_mode = network::mojom::CredentialsMode::kInclude;
     source.url_loader = network::SimpleURLLoader::Create(std::move(request),
                                                          traffic_annotation);
     source.url_loader->SetRetryOptions(
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index 28ab0fb..8b97feb 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -81,7 +81,7 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index 5e7f241..69ef37f 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -134,7 +134,7 @@
 
 #if defined(OS_LINUX)
 #include "chrome/common/chrome_paths.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/upload_list/crash_upload_list.h"
 #endif  // defined(OS_LINUX)
 
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index e9af992..b947c27a 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -248,7 +248,9 @@
       distiller_ui_handle)
       ->set_render_frame_host(frame_host);
 #endif
-  CreateDistillerJavaScriptService(distiller_ui_handle, std::move(receiver));
+  auto* distilled_page_prefs = dom_distiller_service->GetDistilledPagePrefs();
+  CreateDistillerJavaScriptService(distiller_ui_handle, distilled_page_prefs,
+                                   std::move(receiver));
 }
 
 void BindPrerenderCanceler(
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 5257b84d..25ae3211 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -214,8 +214,8 @@
 #endif  // defined(OS_LINUX) && !defined(OS_CHROMEOS)
 
 #if defined(OS_LINUX)
-#include "components/crash/content/app/breakpad_linux.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/breakpad_linux.h"
+#include "components/crash/core/app/crashpad.h"
 #endif
 
 #if defined(OS_MACOSX)
diff --git a/chrome/browser/chrome_browser_main_linux.cc b/chrome/browser/chrome_browser_main_linux.cc
index 9378c7b..a5b3aa18b 100644
--- a/chrome/browser/chrome_browser_main_linux.cc
+++ b/chrome/browser/chrome_browser_main_linux.cc
@@ -16,8 +16,8 @@
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/grit/chromium_strings.h"
-#include "components/crash/content/app/breakpad_linux.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/breakpad_linux.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/metrics/metrics_service.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
diff --git a/chrome/browser/chrome_browser_main_mac.mm b/chrome/browser/chrome_browser_main_mac.mm
index 4dfb852..ffbed05 100644
--- a/chrome/browser/chrome_browser_main_mac.mm
+++ b/chrome/browser/chrome_browser_main_mac.mm
@@ -33,7 +33,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/chromium_strings.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/metrics/metrics_service.h"
 #include "components/os_crypt/os_crypt.h"
 #include "content/public/common/main_function_params.h"
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index 259af4f..74ad624 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -80,8 +80,8 @@
 #include "chrome/installer/util/installer_util_strings.h"
 #include "chrome/installer/util/l10n_string_util.h"
 #include "chrome/installer/util/shell_util.h"
-#include "components/crash/content/app/crash_export_thunks.h"
-#include "components/crash/content/app/dump_hung_process_with_ptype.h"
+#include "components/crash/core/app/crash_export_thunks.h"
+#include "components/crash/core/app/dump_hung_process_with_ptype.h"
 #include "components/crash/core/common/crash_key.h"
 #include "components/os_crypt/os_crypt.h"
 #include "components/version_info/channel.h"
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 3dc2f3c..07a6dfd 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -392,7 +392,7 @@
 #include "chromeos/constants/chromeos_constants.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/constants/chromeos_switches.h"
-#include "components/crash/content/app/breakpad_linux.h"
+#include "components/crash/core/app/breakpad_linux.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "services/service_manager/public/mojom/interface_provider_spec.mojom.h"
@@ -456,14 +456,14 @@
 #endif
 
 #if defined(OS_LINUX)
-#include "components/crash/content/app/crash_switches.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crash_switches.h"
+#include "components/crash/core/app/crashpad.h"
 #endif
 
 #if defined(OS_POSIX) && !defined(OS_MACOSX)
 #if !defined(OS_ANDROID)
 #include "base/debug/leak_annotations.h"
-#include "components/crash/content/app/breakpad_linux.h"
+#include "components/crash/core/app/breakpad_linux.h"
 #endif  // !defined(OS_ANDROID)
 #include "components/crash/content/browser/crash_handler_host_linux.h"
 #endif
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index ac9a62d..b77c6fff 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -186,7 +186,7 @@
     "//components/consent_auditor:consent_auditor",
     "//components/constrained_window",
     "//components/content_settings/core/browser",
-    "//components/crash/content/app",
+    "//components/crash/core/app",
     "//components/crx_file",
     "//components/device_event_log",
     "//components/download/content/public",
@@ -1977,6 +1977,8 @@
     "policy/status_collector/status_collector_state.h",
     "policy/status_uploader.cc",
     "policy/status_uploader.h",
+    "policy/system_features_disable_list_policy_handler.cc",
+    "policy/system_features_disable_list_policy_handler.h",
     "policy/system_log_uploader.cc",
     "policy/system_log_uploader.h",
     "policy/system_proxy_settings_policy_handler.cc",
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index 8de5c14..5047242 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -155,8 +155,8 @@
 #include "components/arc/arc_prefs.h"
 #include "components/arc/arc_util.h"
 #include "components/arc/session/arc_bridge_service.h"
-#include "components/crash/content/app/breakpad_linux.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/breakpad_linux.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/session_manager/core/session_manager.h"
diff --git a/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.cc b/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.cc
new file mode 100644
index 0000000..961e7c9
--- /dev/null
+++ b/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.cc
@@ -0,0 +1,33 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "system_features_disable_list_policy_handler.h"
+
+#include "base/values.h"
+#include "components/policy/core/common/policy_pref_names.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_registry_simple.h"
+
+namespace policy {
+
+SystemFeaturesDisableListPolicyHandler::SystemFeaturesDisableListPolicyHandler()
+    : policy::ListPolicyHandler(key::kSystemFeaturesDisableList,
+                                base::Value::Type::STRING) {}
+
+SystemFeaturesDisableListPolicyHandler::
+    ~SystemFeaturesDisableListPolicyHandler() = default;
+
+void SystemFeaturesDisableListPolicyHandler::RegisterPrefs(
+    PrefRegistrySimple* registry) {
+  registry->RegisterListPref(policy_prefs::kSystemFeaturesDisableList);
+}
+
+void SystemFeaturesDisableListPolicyHandler::ApplyList(
+    std::unique_ptr<base::ListValue> filtered_list,
+    PrefValueMap* prefs) {
+  prefs->SetValue(policy_prefs::kSystemFeaturesDisableList,
+                  base::Value::FromUniquePtrValue(std::move(filtered_list)));
+}
+
+}  // namespace policy
diff --git a/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h b/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h
new file mode 100644
index 0000000..cf854170
--- /dev/null
+++ b/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h
@@ -0,0 +1,36 @@
+// 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 CHROME_BROWSER_CHROMEOS_POLICY_SYSTEM_FEATURES_DISABLE_LIST_POLICY_HANDLER_H_
+#define CHROME_BROWSER_CHROMEOS_POLICY_SYSTEM_FEATURES_DISABLE_LIST_POLICY_HANDLER_H_
+
+#include <memory>
+
+#include "components/policy/core/browser/configuration_policy_handler.h"
+
+namespace base {
+class ListValue;
+}
+class PrefValueMap;
+class PrefRegistrySimple;
+
+namespace policy {
+
+class SystemFeaturesDisableListPolicyHandler
+    : public policy::ListPolicyHandler {
+ public:
+  SystemFeaturesDisableListPolicyHandler();
+  ~SystemFeaturesDisableListPolicyHandler() override;
+
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
+ protected:
+  // ListPolicyHandler:
+  void ApplyList(std::unique_ptr<base::ListValue> filtered_list,
+                 PrefValueMap* prefs) override;
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_CHROMEOS_POLICY_SYSTEM_FEATURES_DISABLE_LIST_POLICY_HANDLER_H_
diff --git a/chrome/browser/crash_upload_list/crash_upload_list.cc b/chrome/browser/crash_upload_list/crash_upload_list.cc
index 9643397..f0f96bd 100644
--- a/chrome/browser/crash_upload_list/crash_upload_list.cc
+++ b/chrome/browser/crash_upload_list/crash_upload_list.cc
@@ -13,7 +13,7 @@
 #include "base/path_service.h"
 #include "chrome/browser/crash_upload_list/crash_upload_list_crashpad.h"
 #include "chrome/common/chrome_paths.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/upload_list/crash_upload_list.h"
 #include "components/upload_list/text_log_upload_list.h"
 #endif
diff --git a/chrome/browser/crash_upload_list/crash_upload_list_crashpad.cc b/chrome/browser/crash_upload_list/crash_upload_list_crashpad.cc
index dc44b99d..7601a6a 100644
--- a/chrome/browser/crash_upload_list/crash_upload_list_crashpad.cc
+++ b/chrome/browser/crash_upload_list/crash_upload_list_crashpad.cc
@@ -11,7 +11,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/common/chrome_constants.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 
 namespace {
 
diff --git a/chrome/browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc b/chrome/browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc
index 64271eb..35b65cf 100644
--- a/chrome/browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc
+++ b/chrome/browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc
@@ -601,4 +601,61 @@
   ASSERT_FLOAT_EQ(kScale, fontSize / oldFontSize);
 }
 
+IN_PROC_BROWSER_TEST_F(DomDistillerViewerSourceBrowserTest, UISetsPrefs) {
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // Load the distilled page
+  GURL view_url;
+  expect_distillation_ = true;
+  expect_distiller_page_ = true;
+  GURL original_url("http://www.example.com/1");
+  view_url =
+      url_utils::GetDistillerViewUrlFromUrl(kDomDistillerScheme, original_url);
+  ViewSingleDistilledPage(view_url, "text/html");
+  content::WaitForLoadStop(contents);
+
+  DistilledPagePrefs* distilled_page_prefs =
+      DomDistillerServiceFactory::GetForBrowserContext(browser()->profile())
+          ->GetDistilledPagePrefs();
+
+  // Verify that the initial preferences aren't the same as those set below.
+  ExpectBodyHasThemeAndFont(contents, "light", "sans-serif");
+  EXPECT_NE(mojom::Theme::kDark, distilled_page_prefs->GetTheme());
+  EXPECT_NE(mojom::FontFamily::kMonospace,
+            distilled_page_prefs->GetFontFamily());
+
+  // 'Click' the associated UI elements for changing each preference.
+  const std::string script = R"(
+      (() => {
+        const observer = new MutationObserver((mutationsList, observer) => {
+          const classes = document.body.classList;
+          if (classes.contains('dark') && classes.contains('monospace')) {
+            observer.disconnect();
+            window.domAutomationController.send(document.body.className);
+          }
+        });
+
+        observer.observe(document.body, {
+          attributes: true,
+          attributeFilter: [ 'class' ]
+        });
+
+        document.querySelector('.theme-option .dark')
+          .dispatchEvent(new MouseEvent('click'));
+        document.querySelector(
+            '#font-family-selection option[value="monospace"]')
+          .dispatchEvent(new Event("change", { bubbles: true }));
+      })();)";
+  content::DOMMessageQueue queue(contents);
+  std::string result;
+  content::ExecuteScriptAsync(contents, script);
+  EXPECT_TRUE(queue.WaitForMessage(&result));
+
+  // Verify that the preferences changed in the browser-side DistilledPagePrefs.
+  EXPECT_EQ(mojom::Theme::kDark, distilled_page_prefs->GetTheme());
+  EXPECT_EQ(mojom::FontFamily::kMonospace,
+            distilled_page_prefs->GetFontFamily());
+}
+
 }  // namespace dom_distiller
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
index b2ba05c..a00fc1e 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
@@ -344,7 +344,6 @@
     entry.username = base::UTF16ToUTF8(form->username_value);
     entry.id = password_id_generator_.GenerateId(
         password_manager::CreateSortKey(*form));
-    entry.num_characters_in_password = form->password_value.length();
 
     if (!form->federation_origin.opaque()) {
       entry.federation_text.reset(new std::string(l10n_util::GetStringFUTF8(
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc
index 64de82d..4fe5609 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc
@@ -268,8 +268,6 @@
         ASSERT_EQ(1u, password_list.size());
         EXPECT_EQ(sample_form.username_value,
                   base::UTF8ToUTF16(password_list[0].username));
-        EXPECT_EQ(sample_form.password_value.size(),
-                  size_t{password_list[0].num_characters_in_password});
       }));
   EXPECT_TRUE(got_passwords);
 
@@ -290,8 +288,6 @@
         ASSERT_EQ(1u, password_list.size());
         EXPECT_EQ(base::ASCIIToUTF16("new_user"),
                   base::UTF8ToUTF16(password_list[0].username));
-        EXPECT_EQ(base::ASCIIToUTF16("new_pass").size(),
-                  size_t{password_list[0].num_characters_in_password});
       }));
   EXPECT_TRUE(got_passwords);
 }
diff --git a/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc b/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc
index 9484ded..6f0005f 100644
--- a/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc
+++ b/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc
@@ -18,7 +18,6 @@
 using ui::TimeFormat;
 
 constexpr size_t kNumMocks = 3;
-constexpr int kNumCharactersInPassword = 10;
 
 api::passwords_private::PasswordUiEntry CreateEntry(int id) {
   api::passwords_private::PasswordUiEntry entry;
@@ -26,7 +25,6 @@
   entry.urls.origin = "http://" + entry.urls.shown + "/login";
   entry.urls.link = entry.urls.origin;
   entry.username = "testName" + base::NumberToString(id);
-  entry.num_characters_in_password = kNumCharactersInPassword;
   entry.id = id;
   return entry;
 }
@@ -71,8 +69,6 @@
   // PasswordUiEntry does not contain a password. Thus we are only updating
   // the username and the length of the password.
   current_entries_[id].username = base::UTF16ToUTF8(username);
-  if (password)
-    current_entries_[id].num_characters_in_password = password->size();
   SendSavedPasswordsList();
 }
 
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 9c9ef61..5ad68bb 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -44,6 +44,7 @@
 #include "extensions/common/extension.h"
 
 #if defined(OS_CHROMEOS)
+#include "ash/public/cpp/ambient/ambient_prefs.h"
 #include "ash/public/cpp/ash_pref_names.h"  // nogncheck
 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
 #include "chrome/browser/chromeos/guest_os/guest_os_pref_names.h"
@@ -473,6 +474,12 @@
   (*s_whitelist)[arc::prefs::kArcEnabled] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
 
+  // Ambient Mode.
+  (*s_whitelist)[ash::ambient::prefs::kAmbientModeEnabled] =
+      settings_api::PrefType::PREF_TYPE_BOOLEAN;
+  (*s_whitelist)[ash::ambient::prefs::kAmbientModeTopicSource] =
+      settings_api::PrefType::PREF_TYPE_NUMBER;
+
   // Google Assistant.
   (*s_whitelist)[chromeos::assistant::prefs::kAssistantConsentStatus] =
       settings_api::PrefType::PREF_TYPE_NUMBER;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 8ed2b67..5442800 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2809,6 +2809,11 @@
     "expiry_milestone": 86
   },
   {
+    "name": "new-signin-architecture",
+    "owners": ["chrome-ios-signin@google.com", "fernandex"],
+    "expiry_milestone": 86
+  },
+  {
     "name": "new-tabstrip-animation",
     "owners": [ "tbergquist" ],
     "expiry_milestone": 81
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index ebe00be9..cd9a05a 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -567,7 +567,7 @@
                                          base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kTabToGTSAnimation{"TabToGTSAnimation",
-                                       base::FEATURE_DISABLED_BY_DEFAULT};
+                                       base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kTestDefaultDisabled{"TestDefaultDisabled",
                                          base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/google/google_update_settings_posix.cc b/chrome/browser/google/google_update_settings_posix.cc
index b6a05ed7..f2e4ed05 100644
--- a/chrome/browser/google/google_update_settings_posix.cc
+++ b/chrome/browser/google/google_update_settings_posix.cc
@@ -13,7 +13,7 @@
 #include "base/task/lazy_thread_pool_task_runner.h"
 #include "build/build_config.h"
 #include "chrome/common/chrome_paths.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 
 namespace {
 
diff --git a/chrome/browser/hang_monitor/hang_crash_dump_mac.cc b/chrome/browser/hang_monitor/hang_crash_dump_mac.cc
index 754cbb8..86a272d 100644
--- a/chrome/browser/hang_monitor/hang_crash_dump_mac.cc
+++ b/chrome/browser/hang_monitor/hang_crash_dump_mac.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/hang_monitor/hang_crash_dump.h"
 
 #include "base/process/process.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #include "content/public/browser/browser_child_process_host.h"
 #include "content/public/common/result_codes.h"
 
diff --git a/chrome/browser/hang_monitor/hang_crash_dump_win.cc b/chrome/browser/hang_monitor/hang_crash_dump_win.cc
index 8a4cd17c..30b04eb 100644
--- a/chrome/browser/hang_monitor/hang_crash_dump_win.cc
+++ b/chrome/browser/hang_monitor/hang_crash_dump_win.cc
@@ -7,7 +7,7 @@
 #include <windows.h>
 
 #include "base/logging.h"
-#include "components/crash/content/app/crash_export_thunks.h"
+#include "components/crash/core/app/crash_export_thunks.h"
 
 namespace {
 
diff --git a/chrome/browser/media/feeds/BUILD.gn b/chrome/browser/media/feeds/BUILD.gn
index a5d72ae..92d3cee 100644
--- a/chrome/browser/media/feeds/BUILD.gn
+++ b/chrome/browser/media/feeds/BUILD.gn
@@ -8,7 +8,10 @@
 mojom("mojo_bindings") {
   sources = [ "media_feeds_store.mojom" ]
 
-  public_deps = [ "//url/mojom:url_mojom_origin" ]
+  public_deps = [
+    "//services/media_session/public/mojom",
+    "//url/mojom:url_mojom_origin",
+  ]
 }
 
 proto_library("proto") {
diff --git a/chrome/browser/media/feeds/media_feeds.proto b/chrome/browser/media/feeds/media_feeds.proto
index c7fbefb..600ce19 100644
--- a/chrome/browser/media/feeds/media_feeds.proto
+++ b/chrome/browser/media/feeds/media_feeds.proto
@@ -115,3 +115,19 @@
   // The identifiers for the episode. We will store up to one of each type.
   repeated Identifier identifier = 4;
 }
+
+// A reference to an image along with the width and height in px. Only stores
+// the URL and not the image data.
+// https://wicg.github.io/media-feeds/index.html#media-image
+message Image {
+  string url = 1;
+  int64 width = 2;
+  int64 height = 3;
+}
+
+// Stores up to 5 images of different sizes.
+// https://wicg.github.io/media-feeds/index.html#media-feed-document
+// https://wicg.github.io/media-feeds/index.html#media-feed-item
+message ImageSet {
+  repeated Image image = 1;
+}
diff --git a/chrome/browser/media/feeds/media_feeds_store.mojom b/chrome/browser/media/feeds/media_feeds_store.mojom
index 7eaa7fa..c1021136 100644
--- a/chrome/browser/media/feeds/media_feeds_store.mojom
+++ b/chrome/browser/media/feeds/media_feeds_store.mojom
@@ -4,6 +4,7 @@
 
 module media_feeds.mojom;
 
+import "services/media_session/public/mojom/media_session.mojom";
 import "mojo/public/mojom/base/string16.mojom";
 import "mojo/public/mojom/base/time.mojom";
 import "url/mojom/url.mojom";
@@ -17,19 +18,75 @@
 
   // The last time this feed was discovered.
   mojo_base.mojom.Time last_discovery_time;
+
+  // The last time this feed was fetched.
+  mojo_base.mojom.Time? last_fetch_time;
+
+  // Whether the user has forcibly opted out of the feed.
+  FeedUserStatus user_status;
+
+  // The result of the last fetch of this feed.
+  FetchResult last_fetch_result;
+
+  // The number of times in a row the feed has failed to be fetched.
+  int64 fetch_failed_count;
+
+  // The time the previously fetched feed should expire in the cache.
+  mojo_base.mojom.Time? cache_expiry_time;
+
+  // The number of items in the last feed fetch.
+  int64 last_fetch_item_count;
+
+  // The number of items in the last feed fetch that are eligible for play next.
+  int64 last_fetch_play_next_count;
+
+  // A field mask of the content types in the last feed fetch.
+  int64 last_fetch_content_types;
+
+  // The logos for the feed.
+  array<media_session.mojom.MediaImage> logos;
+
+  // A display name for the feed.
+  string display_name;
+};
+
+// The result of fetching the feed. This enum is committed to storage so do not
+// change the numbering.
+enum FetchResult {
+  // The feed has never been fetched.
+  kNone = 0,
+
+  // The feed was fetched successfully.
+  kSuccess = 1,
+
+  // The feed failed to fetch because of a backend server error.
+  kFailedBackendError = 2,
+
+  // The feed failed to fetch because of a network error.
+  kFailedNetworkError = 3,
+};
+
+// Whether the user has forcibly opted out of the feed. This enum is committed
+// to storage so do not change the numbering.
+enum FeedUserStatus {
+  // The browser should determine whether to fetch the feed based on metrics.
+  kAuto = 0,
+
+  // The user has opted out of seeing the feed.
+  kDisabled = 1,
 };
 
 // The type of the feed item. This enum is committed to storage so do not
 // change the numbering.
 enum MediaFeedItemType {
-  // Any video. https://schema.org/VideoObject
-  kVideo = 0,
+  // 1 << 0. Any video. https://schema.org/VideoObject
+  kVideo = 1,
 
-  // A TV series. https://schema.org/TVSeries
-  kTVSeries = 1,
+  // 1 << 1. A TV series. https://schema.org/TVSeries
+  kTVSeries = 2,
 
-  // A movie. https://schema.org/Movie
-  kMovie = 2,
+  // 1 << 2. A movie. https://schema.org/Movie
+  kMovie = 4,
 };
 
 // The action status for the feed item. This enum is committed to storage so
@@ -146,7 +203,7 @@
 
 // The Media Feed Item is an individual item stored in a Media Feed. It
 // represents a single recommendation such as a video or TV show.
-// https://wicg.github.io/media-feeds-api/index.html#media-feed-item
+// https://wicg.github.io/media-feeds/index.html#media-feed-item
 struct MediaFeedItem {
   // The type of this feed item such as a video or TV show.
   MediaFeedItemType type;
@@ -198,6 +255,9 @@
 
   // Whether the user has previously clicked on this feed item.
   bool clicked;
+
+  // The images for the feed item.
+  array<media_session.mojom.MediaImage> images;
 };
 
 // MediaFeedStore allows the Media Feeds WebUI to access data from the Media
diff --git a/chrome/browser/media/feeds/media_feeds_utils.cc b/chrome/browser/media/feeds/media_feeds_utils.cc
new file mode 100644
index 0000000..cb4b976
--- /dev/null
+++ b/chrome/browser/media/feeds/media_feeds_utils.cc
@@ -0,0 +1,64 @@
+// 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 "chrome/browser/media/feeds/media_feeds_utils.h"
+
+#include "chrome/browser/media/feeds/media_feeds.pb.h"
+#include "services/media_session/public/cpp/media_image.h"
+
+namespace media_feeds {
+
+void MediaImageToProto(Image* proto, const media_session::MediaImage& image) {
+  proto->set_url(image.src.spec());
+
+  if (image.sizes.empty())
+    return;
+
+  DCHECK_EQ(1u, image.sizes.size());
+
+  proto->set_width(image.sizes[0].width());
+  proto->set_height(image.sizes[0].height());
+}
+
+ImageSet MediaImagesToProto(
+    const std::vector<media_session::MediaImage>& images,
+    int max_number) {
+  ImageSet image_set;
+
+  for (auto& image : images) {
+    MediaImageToProto(image_set.add_image(), image);
+
+    if (image_set.image().size() >= max_number)
+      break;
+  }
+
+  return image_set;
+}
+
+media_session::MediaImage ProtoToMediaImage(const Image& proto) {
+  media_session::MediaImage image;
+  image.src = GURL(proto.url());
+
+  if (proto.width() > 0 && proto.height() > 0)
+    image.sizes.push_back(gfx::Size(proto.width(), proto.height()));
+
+  return image;
+}
+
+std::vector<media_session::MediaImage> ProtoToMediaImages(
+    const ImageSet& image_set,
+    unsigned max_number) {
+  std::vector<media_session::MediaImage> images;
+
+  for (auto& proto : image_set.image()) {
+    images.push_back(ProtoToMediaImage(proto));
+
+    if (images.size() >= max_number)
+      break;
+  }
+
+  return images;
+}
+
+}  // namespace media_feeds
diff --git a/chrome/browser/media/feeds/media_feeds_utils.h b/chrome/browser/media/feeds/media_feeds_utils.h
new file mode 100644
index 0000000..955d88a
--- /dev/null
+++ b/chrome/browser/media/feeds/media_feeds_utils.h
@@ -0,0 +1,33 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEDIA_FEEDS_MEDIA_FEEDS_UTILS_H_
+#define CHROME_BROWSER_MEDIA_FEEDS_MEDIA_FEEDS_UTILS_H_
+
+#include <vector>
+
+namespace media_session {
+struct MediaImage;
+}  // namespace media_session
+
+namespace media_feeds {
+
+class Image;
+class ImageSet;
+
+void MediaImageToProto(Image* proto, const media_session::MediaImage& image);
+
+ImageSet MediaImagesToProto(
+    const std::vector<media_session::MediaImage>& images,
+    int max_number);
+
+media_session::MediaImage ProtoToMediaImage(const Image& proto);
+
+std::vector<media_session::MediaImage> ProtoToMediaImages(
+    const ImageSet& image_set,
+    unsigned max_number);
+
+}  // namespace media_feeds
+
+#endif  // CHROME_BROWSER_MEDIA_FEEDS_MEDIA_FEEDS_UTILS_H_
diff --git a/chrome/browser/media/history/media_history_feed_items_table.cc b/chrome/browser/media/history/media_history_feed_items_table.cc
index 8cf8231..d9d7c3b8 100644
--- a/chrome/browser/media/history/media_history_feed_items_table.cc
+++ b/chrome/browser/media/history/media_history_feed_items_table.cc
@@ -7,6 +7,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/updateable_sequenced_task_runner.h"
 #include "chrome/browser/media/feeds/media_feeds.pb.h"
+#include "chrome/browser/media/feeds/media_feeds_utils.h"
 #include "chrome/browser/media/history/media_history_store.h"
 #include "sql/statement.h"
 #include "url/gurl.h"
@@ -15,28 +16,8 @@
 
 namespace {
 
-void BindProto(sql::Statement& s,
-               int col,
-               const google::protobuf::MessageLite& protobuf) {
-  std::string out;
-  CHECK(protobuf.SerializeToString(&out));
-  s.BindBlob(col, out.data(), out.size());
-}
-
-bool GetProto(sql::Statement& statement,
-              int col,
-              google::protobuf::MessageLite& protobuf,
-              MediaHistoryFeedItemsTable::FeedItemReadResult result) {
-  std::string value;
-  statement.ColumnBlobAsString(col, &value);
-
-  if (protobuf.ParseFromString(value))
-    return true;
-
-  base::UmaHistogramEnumeration(
-      MediaHistoryFeedItemsTable::kFeedItemReadResultHistogramName, result);
-  return false;
-}
+// The maximum number of images to allow.
+const int kMaxImageCount = 5;
 
 media_feeds::InteractionCounter_Type Convert(
     const media_feeds::mojom::InteractionCounterType& type) {
@@ -168,6 +149,7 @@
       "identifiers BLOB, "
       "shown_count INTEGER,"
       "clicked INTEGER, "
+      "images BLOB, "
       "CONSTRAINT fk_feed "
       "FOREIGN KEY (feed_id) "
       "REFERENCES mediaFeed(id) "
@@ -203,8 +185,8 @@
       "action_status, genre, duration_s, is_live, live_start_time_s, "
       "live_end_time_s, shown_count, clicked, author, action, "
       "interaction_counters, content_rating, identifiers, tv_episode, "
-      "play_next_candidate) VALUES "
-      "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+      "play_next_candidate, images) VALUES "
+      "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
 
   statement.BindInt64(0, feed_id);
   statement.BindInt64(1, static_cast<int>(item->type));
@@ -340,6 +322,13 @@
     statement.BindNull(19);
   }
 
+  if (!item->images.empty()) {
+    BindProto(statement, 20,
+              media_feeds::MediaImagesToProto(item->images, kMaxImageCount));
+  } else {
+    statement.BindNull(20);
+  }
+
   return statement.Run();
 }
 
@@ -366,7 +355,7 @@
       "action_status, genre, duration_s, is_live, live_start_time_s, "
       "live_end_time_s, shown_count, clicked, author, action, "
       "interaction_counters, content_rating, identifiers, tv_episode, "
-      "play_next_candidate FROM "
+      "play_next_candidate, images FROM "
       "mediaFeedItem WHERE feed_id = ?"));
 
   statement.BindInt64(0, feed_id);
@@ -396,8 +385,13 @@
 
     if (statement.GetColumnType(12) == sql::ColumnType::kBlob) {
       media_feeds::Author author;
-      if (!GetProto(statement, 12, author, FeedItemReadResult::kBadAuthor))
+      if (!GetProto(statement, 12, author)) {
+        base::UmaHistogramEnumeration(
+            MediaHistoryFeedItemsTable::kFeedItemReadResultHistogramName,
+            FeedItemReadResult::kBadAuthor);
+
         continue;
+      }
 
       item->author = media_feeds::mojom::Author::New();
       item->author->name = author.name();
@@ -406,16 +400,24 @@
 
     if (statement.GetColumnType(13) == sql::ColumnType::kBlob) {
       media_feeds::Action action;
-      if (!GetProto(statement, 13, action, FeedItemReadResult::kBadAction))
+      if (!GetProto(statement, 13, action)) {
+        base::UmaHistogramEnumeration(
+            MediaHistoryFeedItemsTable::kFeedItemReadResultHistogramName,
+            FeedItemReadResult::kBadAction);
+
         continue;
+      }
 
       item->action = Convert(action);
     }
 
     if (statement.GetColumnType(14) == sql::ColumnType::kBlob) {
       media_feeds::InteractionCounterSet counters;
-      if (!GetProto(statement, 14, counters,
-                    FeedItemReadResult::kBadInteractionCounters)) {
+      if (!GetProto(statement, 14, counters)) {
+        base::UmaHistogramEnumeration(
+            MediaHistoryFeedItemsTable::kFeedItemReadResultHistogramName,
+            FeedItemReadResult::kBadInteractionCounters);
+
         continue;
       }
 
@@ -427,8 +429,11 @@
 
     if (statement.GetColumnType(15) == sql::ColumnType::kBlob) {
       media_feeds::ContentRatingSet ratings;
-      if (!GetProto(statement, 15, ratings,
-                    FeedItemReadResult::kBadContentRatings)) {
+      if (!GetProto(statement, 15, ratings)) {
+        base::UmaHistogramEnumeration(
+            MediaHistoryFeedItemsTable::kFeedItemReadResultHistogramName,
+            FeedItemReadResult::kBadContentRatings);
+
         continue;
       }
 
@@ -442,8 +447,11 @@
 
     if (statement.GetColumnType(16) == sql::ColumnType::kBlob) {
       media_feeds::IdentifierSet identifiers;
-      if (!GetProto(statement, 16, identifiers,
-                    FeedItemReadResult::kBadIdentifiers)) {
+      if (!GetProto(statement, 16, identifiers)) {
+        base::UmaHistogramEnumeration(
+            MediaHistoryFeedItemsTable::kFeedItemReadResultHistogramName,
+            FeedItemReadResult::kBadIdentifiers);
+
         continue;
       }
 
@@ -453,8 +461,11 @@
 
     if (statement.GetColumnType(17) == sql::ColumnType::kBlob) {
       media_feeds::TVEpisode tv_episode;
-      if (!GetProto(statement, 17, tv_episode,
-                    FeedItemReadResult::kBadTVEpisode)) {
+      if (!GetProto(statement, 17, tv_episode)) {
+        base::UmaHistogramEnumeration(
+            MediaHistoryFeedItemsTable::kFeedItemReadResultHistogramName,
+            FeedItemReadResult::kBadTVEpisode);
+
         continue;
       }
 
@@ -469,8 +480,11 @@
 
     if (statement.GetColumnType(18) == sql::ColumnType::kBlob) {
       media_feeds::PlayNextCandidate play_next_candidate;
-      if (!GetProto(statement, 18, play_next_candidate,
-                    FeedItemReadResult::kBadPlayNextCandidate)) {
+      if (!GetProto(statement, 18, play_next_candidate)) {
+        base::UmaHistogramEnumeration(
+            MediaHistoryFeedItemsTable::kFeedItemReadResultHistogramName,
+            FeedItemReadResult::kBadPlayNextCandidate);
+
         continue;
       }
 
@@ -488,6 +502,19 @@
         item->play_next_candidate->identifiers.push_back(Convert(identifier));
     }
 
+    if (statement.GetColumnType(19) == sql::ColumnType::kBlob) {
+      media_feeds::ImageSet image_set;
+      if (!GetProto(statement, 19, image_set)) {
+        base::UmaHistogramEnumeration(
+            MediaHistoryFeedItemsTable::kFeedItemReadResultHistogramName,
+            FeedItemReadResult::kBadImages);
+
+        continue;
+      }
+
+      item->images = media_feeds::ProtoToMediaImages(image_set, kMaxImageCount);
+    }
+
     base::UmaHistogramEnumeration(kFeedItemReadResultHistogramName,
                                   FeedItemReadResult::kSuccess);
 
diff --git a/chrome/browser/media/history/media_history_feed_items_table.h b/chrome/browser/media/history/media_history_feed_items_table.h
index e57f079a..e322c78 100644
--- a/chrome/browser/media/history/media_history_feed_items_table.h
+++ b/chrome/browser/media/history/media_history_feed_items_table.h
@@ -38,7 +38,8 @@
     kBadIdentifiers = 7,
     kBadTVEpisode = 8,
     kBadPlayNextCandidate = 9,
-    kMaxValue = kBadPlayNextCandidate,
+    kBadImages = 10,
+    kMaxValue = kBadImages,
   };
 
   MediaHistoryFeedItemsTable(const MediaHistoryFeedItemsTable&) = delete;
diff --git a/chrome/browser/media/history/media_history_feeds_table.cc b/chrome/browser/media/history/media_history_feeds_table.cc
index 212c878..e753fad 100644
--- a/chrome/browser/media/history/media_history_feeds_table.cc
+++ b/chrome/browser/media/history/media_history_feeds_table.cc
@@ -4,16 +4,30 @@
 
 #include "chrome/browser/media/history/media_history_feeds_table.h"
 
+#include "base/metrics/histogram_functions.h"
 #include "base/strings/stringprintf.h"
 #include "base/updateable_sequenced_task_runner.h"
+#include "chrome/browser/media/feeds/media_feeds.pb.h"
+#include "chrome/browser/media/feeds/media_feeds_utils.h"
 #include "chrome/browser/media/history/media_history_store.h"
+#include "services/media_session/public/cpp/media_image.h"
 #include "sql/statement.h"
 #include "url/gurl.h"
 
 namespace media_history {
 
+namespace {
+
+// The maximum number of logos to allow.
+const int kMaxLogoCount = 5;
+
+}  // namespace
+
 const char MediaHistoryFeedsTable::kTableName[] = "mediaFeed";
 
+const char MediaHistoryFeedsTable::kFeedReadResultHistogramName[] =
+    "Media.Feeds.Feed.ReadResult";
+
 MediaHistoryFeedsTable::MediaHistoryFeedsTable(
     scoped_refptr<base::UpdateableSequencedTaskRunner> db_task_runner)
     : MediaHistoryTableBase(std::move(db_task_runner)) {}
@@ -30,6 +44,16 @@
                                        "origin_id INTEGER NOT NULL UNIQUE,"
                                        "url TEXT NOT NULL, "
                                        "last_discovery_time_s INTEGER, "
+                                       "last_fetch_time_s INTEGER, "
+                                       "user_status INTEGER DEFAULT 0, "
+                                       "last_fetch_result INTEGER DEFAULT 0, "
+                                       "fetch_failed_count INTEGER, "
+                                       "cache_expiry_time_s INTEGER, "
+                                       "last_fetch_item_count INTEGER, "
+                                       "last_fetch_play_next_count INTEGER, "
+                                       "last_fetch_content_types INTEGER, "
+                                       "logo BLOB, "
+                                       "display_name TEXT, "
                                        "CONSTRAINT fk_origin "
                                        "FOREIGN KEY (origin_id) "
                                        "REFERENCES origin(id) "
@@ -116,19 +140,71 @@
     return feeds;
 
   sql::Statement statement(DB()->GetUniqueStatement(
-      base::StringPrintf("SELECT id, url, last_discovery_time_s "
-                         "FROM %s",
-                         kTableName)
-          .c_str()));
+      "SELECT id, url, last_discovery_time_s, last_fetch_time_s, "
+      "user_status, last_fetch_result, fetch_failed_count, "
+      "cache_expiry_time_s, "
+      "last_fetch_item_count, last_fetch_play_next_count, "
+      "last_fetch_content_types, "
+      "logo, display_name FROM mediaFeed"));
 
   while (statement.Step()) {
     media_feeds::mojom::MediaFeedPtr feed(media_feeds::mojom::MediaFeed::New());
 
+    feed->user_status = static_cast<media_feeds::mojom::FeedUserStatus>(
+        statement.ColumnInt64(4));
+    feed->last_fetch_result =
+        static_cast<media_feeds::mojom::FetchResult>(statement.ColumnInt64(5));
+
+    if (!IsKnownEnumValue(feed->user_status)) {
+      base::UmaHistogramEnumeration(kFeedReadResultHistogramName,
+                                    FeedReadResult::kBadUserStatus);
+      continue;
+    }
+
+    if (!IsKnownEnumValue(feed->last_fetch_result)) {
+      base::UmaHistogramEnumeration(kFeedReadResultHistogramName,
+                                    FeedReadResult::kBadFetchResult);
+      continue;
+    }
+
+    if (statement.GetColumnType(11) == sql::ColumnType::kBlob) {
+      media_feeds::ImageSet image_set;
+      if (!GetProto(statement, 11, image_set)) {
+        base::UmaHistogramEnumeration(kFeedReadResultHistogramName,
+                                      FeedReadResult::kBadLogo);
+
+        continue;
+      }
+
+      feed->logos = media_feeds::ProtoToMediaImages(image_set, kMaxLogoCount);
+    }
+
+    base::UmaHistogramEnumeration(kFeedReadResultHistogramName,
+                                  FeedReadResult::kSuccess);
+
     feed->id = statement.ColumnInt64(0);
     feed->url = GURL(statement.ColumnString(1));
     feed->last_discovery_time = base::Time::FromDeltaSinceWindowsEpoch(
         base::TimeDelta::FromSeconds(statement.ColumnInt64(2)));
 
+    if (statement.GetColumnType(3) == sql::ColumnType::kInteger) {
+      feed->last_fetch_time = base::Time::FromDeltaSinceWindowsEpoch(
+          base::TimeDelta::FromSeconds(statement.ColumnInt64(3)));
+    }
+
+    feed->fetch_failed_count = statement.ColumnInt64(6);
+
+    if (statement.GetColumnType(7) == sql::ColumnType::kInteger) {
+      feed->cache_expiry_time = base::Time::FromDeltaSinceWindowsEpoch(
+          base::TimeDelta::FromSeconds(statement.ColumnInt64(7)));
+    }
+
+    feed->last_fetch_item_count = statement.ColumnInt64(8);
+    feed->last_fetch_play_next_count = statement.ColumnInt64(9);
+    feed->last_fetch_content_types = statement.ColumnInt64(10);
+
+    feed->display_name = statement.ColumnString(12);
+
     feeds.push_back(std::move(feed));
   }
 
@@ -136,4 +212,64 @@
   return feeds;
 }
 
+bool MediaHistoryFeedsTable::UpdateFeedFromFetch(
+    const int64_t feed_id,
+    const media_feeds::mojom::FetchResult result,
+    const base::Time& expiry_time,
+    const int item_count,
+    const int item_play_next_count,
+    const int item_content_types,
+    const std::vector<media_session::MediaImage>& logos,
+    const std::string& display_name) {
+  DCHECK_LT(0, DB()->transaction_nesting());
+  if (!CanAccessDatabase())
+    return false;
+
+  int fetch_failed_count = 0;
+
+  {
+    if (result != media_feeds::mojom::FetchResult::kSuccess) {
+      // See how many times we have failed to fetch the feed.
+      sql::Statement statement(DB()->GetCachedStatement(
+          SQL_FROM_HERE,
+          "SELECT fetch_failed_count FROM mediaFeed WHERE id = ?"));
+      statement.BindInt64(0, feed_id);
+
+      while (statement.Step()) {
+        DCHECK(!fetch_failed_count);
+        fetch_failed_count = statement.ColumnInt64(0) + 1;
+      }
+    }
+  }
+
+  sql::Statement statement(DB()->GetCachedStatement(
+      SQL_FROM_HERE,
+      "UPDATE mediaFeed SET last_fetch_time_s = ?, last_fetch_result = ?, "
+      "fetch_failed_count = ?, cache_expiry_time_s = ?, last_fetch_item_count "
+      "= ?, "
+      "last_fetch_play_next_count = ?, last_fetch_content_types = ?, "
+      "logo = ?, display_name = ?  WHERE id = ?"));
+
+  statement.BindInt64(0,
+                      base::Time::Now().ToDeltaSinceWindowsEpoch().InSeconds());
+  statement.BindInt64(1, static_cast<int>(result));
+  statement.BindInt64(2, fetch_failed_count);
+  statement.BindInt64(3, expiry_time.ToDeltaSinceWindowsEpoch().InSeconds());
+  statement.BindInt64(4, item_count);
+  statement.BindInt64(5, item_play_next_count);
+  statement.BindInt64(6, item_content_types);
+
+  if (!logos.empty()) {
+    BindProto(statement, 7,
+              media_feeds::MediaImagesToProto(logos, kMaxLogoCount));
+  } else {
+    statement.BindNull(7);
+  }
+
+  statement.BindString(8, display_name);
+  statement.BindInt64(9, feed_id);
+
+  return statement.Run() && DB()->GetLastChangeCount() == 1;
+}
+
 }  // namespace media_history
diff --git a/chrome/browser/media/history/media_history_feeds_table.h b/chrome/browser/media/history/media_history_feeds_table.h
index 9840388..3bb23b6 100644
--- a/chrome/browser/media/history/media_history_feeds_table.h
+++ b/chrome/browser/media/history/media_history_feeds_table.h
@@ -22,6 +22,20 @@
  public:
   static const char kTableName[];
 
+  static const char kFeedReadResultHistogramName[];
+
+  // If we read a feed item from the database then we record the result to
+  // |kFeedReadResultHistogramName|. Do not change the numbering since this
+  // is recorded.
+  enum class FeedReadResult {
+    kSuccess = 0,
+    kBadUserStatus = 1,
+    kBadFetchResult = 2,
+    kBadLogo = 3,
+    kBadUserIdentifier = 4,
+    kMaxValue = kBadUserIdentifier,
+  };
+
  private:
   friend class MediaHistoryStoreInternal;
 
@@ -37,6 +51,16 @@
   // Saves a newly discovered feed in the database.
   bool DiscoverFeed(const GURL& url);
 
+  // Updates the feed following a fetch.
+  bool UpdateFeedFromFetch(const int64_t feed_id,
+                           const media_feeds::mojom::FetchResult result,
+                           const base::Time& expiry_time,
+                           const int item_count,
+                           const int item_play_next_count,
+                           const int item_content_types,
+                           const std::vector<media_session::MediaImage>& logos,
+                           const std::string& display_name);
+
   // Returns the feed rows in the database.
   std::vector<media_feeds::mojom::MediaFeedPtr> GetRows();
 };
diff --git a/chrome/browser/media/history/media_history_keyed_service.cc b/chrome/browser/media/history/media_history_keyed_service.cc
index f3233cc..33791c4 100644
--- a/chrome/browser/media/history/media_history_keyed_service.cc
+++ b/chrome/browser/media/history/media_history_keyed_service.cc
@@ -194,11 +194,17 @@
                                                      std::move(callback));
 }
 
-void MediaHistoryKeyedService::ReplaceMediaFeedItems(
+void MediaHistoryKeyedService::StoreMediaFeedFetchResult(
     const int64_t feed_id,
-    std::vector<media_feeds::mojom::MediaFeedItemPtr> items) {
-  if (auto* store = store_->GetForWrite())
-    store->ReplaceMediaFeedItems(feed_id, std::move(items));
+    std::vector<media_feeds::mojom::MediaFeedItemPtr> items,
+    const media_feeds::mojom::FetchResult result,
+    const base::Time& expiry_time,
+    const std::vector<media_session::MediaImage>& logos,
+    const std::string& display_name) {
+  if (auto* store = store_->GetForWrite()) {
+    store->StoreMediaFeedFetchResult(feed_id, std::move(items), result,
+                                     expiry_time, logos, display_name);
+  }
 }
 
 void MediaHistoryKeyedService::GetURLsInTableForTest(
diff --git a/chrome/browser/media/history/media_history_keyed_service.h b/chrome/browser/media/history/media_history_keyed_service.h
index 14e3d99..3517b18a 100644
--- a/chrome/browser/media/history/media_history_keyed_service.h
+++ b/chrome/browser/media/history/media_history_keyed_service.h
@@ -90,10 +90,15 @@
           void(std::vector<media_feeds::mojom::MediaFeedItemPtr>)> callback);
 
   // Replaces the media items in |feed_id|. This will delete any old feed items
-  // and store the new ones in |items|.
-  void ReplaceMediaFeedItems(
+  // and store the new ones in |items|. This will also update the |result|,
+  // |expiry_time|, |logos| and |display_name| for the feed.
+  void StoreMediaFeedFetchResult(
       const int64_t feed_id,
-      std::vector<media_feeds::mojom::MediaFeedItemPtr> items);
+      std::vector<media_feeds::mojom::MediaFeedItemPtr> items,
+      const media_feeds::mojom::FetchResult result,
+      const base::Time& expiry_time,
+      const std::vector<media_session::MediaImage>& logos,
+      const std::string& display_name);
 
   void GetURLsInTableForTest(const std::string& table,
                              base::OnceCallback<void(std::set<GURL>)> callback);
diff --git a/chrome/browser/media/history/media_history_store.cc b/chrome/browser/media/history/media_history_store.cc
index 6ea7f716..e02c720 100644
--- a/chrome/browser/media/history/media_history_store.cc
+++ b/chrome/browser/media/history/media_history_store.cc
@@ -93,9 +93,13 @@
 
   void DiscoverMediaFeed(const GURL& url);
 
-  void ReplaceMediaFeedItems(
+  void StoreMediaFeedFetchResult(
       const int64_t feed_id,
-      std::vector<media_feeds::mojom::MediaFeedItemPtr> items);
+      std::vector<media_feeds::mojom::MediaFeedItemPtr> items,
+      const media_feeds::mojom::FetchResult result,
+      const base::Time& expiry_time,
+      const std::vector<media_session::MediaImage>& logos,
+      const std::string& display_name);
 
   std::vector<media_feeds::mojom::MediaFeedItemPtr>
   GetItemsForMediaFeedForDebug(const int64_t feed_id);
@@ -494,9 +498,13 @@
   DB()->CommitTransaction();
 }
 
-void MediaHistoryStoreInternal::ReplaceMediaFeedItems(
+void MediaHistoryStoreInternal::StoreMediaFeedFetchResult(
     const int64_t feed_id,
-    std::vector<media_feeds::mojom::MediaFeedItemPtr> items) {
+    std::vector<media_feeds::mojom::MediaFeedItemPtr> items,
+    const media_feeds::mojom::FetchResult result,
+    const base::Time& expiry_time,
+    const std::vector<media_session::MediaImage>& logos,
+    const std::string& display_name) {
   DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
   if (!initialization_successful_)
     return;
@@ -506,7 +514,7 @@
     return;
   }
 
-  if (!feed_items_table_)
+  if (!feeds_table_ || !feed_items_table_)
     return;
 
   // Remove all the items currently associated with this feed.
@@ -515,12 +523,33 @@
     return;
   }
 
+  int item_play_next_count = 0;
+  int item_content_types = 0;
+
   for (auto& item : items) {
     // Save each item to the table.
     if (!feed_items_table_->SaveItem(feed_id, item)) {
       DB()->RollbackTransaction();
       return;
     }
+
+    // If the item has a play next candidate or the user is currently watching
+    // this media then we should add it to the play next count.
+    if (item->play_next_candidate ||
+        item->action_status ==
+            media_feeds::mojom::MediaFeedItemActionStatus::kActive) {
+      item_play_next_count++;
+    }
+
+    item_content_types |= static_cast<int>(item->type);
+  }
+
+  // Update the metadata associated with this feed.
+  if (!feeds_table_->UpdateFeedFromFetch(
+          feed_id, result, expiry_time, items.size(), item_play_next_count,
+          item_content_types, logos, display_name)) {
+    DB()->RollbackTransaction();
+    return;
   }
 
   DB()->CommitTransaction();
@@ -672,13 +701,18 @@
                                          std::move(callback));
 }
 
-void MediaHistoryStore::ReplaceMediaFeedItems(
+void MediaHistoryStore::StoreMediaFeedFetchResult(
     const int64_t feed_id,
-    std::vector<media_feeds::mojom::MediaFeedItemPtr> items) {
+    std::vector<media_feeds::mojom::MediaFeedItemPtr> items,
+    const media_feeds::mojom::FetchResult result,
+    const base::Time& expiry_time,
+    const std::vector<media_session::MediaImage>& logos,
+    const std::string& display_name) {
   db_->db_task_runner_->PostTask(
       FROM_HERE,
-      base::BindOnce(&MediaHistoryStoreInternal::ReplaceMediaFeedItems, db_,
-                     feed_id, std::move(items)));
+      base::BindOnce(&MediaHistoryStoreInternal::StoreMediaFeedFetchResult, db_,
+                     feed_id, std::move(items), result, expiry_time, logos,
+                     display_name));
 }
 
 void MediaHistoryStore::GetItemsForMediaFeedForDebug(
diff --git a/chrome/browser/media/history/media_history_store.h b/chrome/browser/media/history/media_history_store.h
index ad83536..c31f7d3 100644
--- a/chrome/browser/media/history/media_history_store.h
+++ b/chrome/browser/media/history/media_history_store.h
@@ -103,9 +103,13 @@
   // Saves a newly discovered media feed in the media history store.
   void DiscoverMediaFeed(const GURL& url);
 
-  void ReplaceMediaFeedItems(
+  void StoreMediaFeedFetchResult(
       const int64_t feed_id,
-      std::vector<media_feeds::mojom::MediaFeedItemPtr> items);
+      std::vector<media_feeds::mojom::MediaFeedItemPtr> items,
+      const media_feeds::mojom::FetchResult result,
+      const base::Time& expiry_time,
+      const std::vector<media_session::MediaImage>& logos,
+      const std::string& display_name);
 
   // Gets all the feed items for |feed_id|.
   void GetItemsForMediaFeedForDebug(
diff --git a/chrome/browser/media/history/media_history_store_unittest.cc b/chrome/browser/media/history/media_history_store_unittest.cc
index e942074..e1d38fd 100644
--- a/chrome/browser/media/history/media_history_store_unittest.cc
+++ b/chrome/browser/media/history/media_history_store_unittest.cc
@@ -48,6 +48,22 @@
 // might be equal but it might be close too.
 const int kTimeErrorMargin = 10000;
 
+// The expected display name for the fetched media feed.
+const char kExpectedDisplayName[] = "Test Feed";
+
+// The expected counts and content types for the test feed.
+const int kExpectedFetchItemCount = 3;
+const int kExpectedFetchPlayNextCount = 2;
+const int kExpectedFetchContentTypes =
+    static_cast<int>(media_feeds::mojom::MediaFeedItemType::kMovie) |
+    static_cast<int>(media_feeds::mojom::MediaFeedItemType::kTVSeries);
+
+// The expected counts and content types for the alternate test feed.
+const int kExpectedAltFetchItemCount = 1;
+const int kExpectedAltFetchPlayNextCount = 1;
+const int kExpectedAltFetchContentTypes =
+    static_cast<int>(media_feeds::mojom::MediaFeedItemType::kVideo);
+
 base::FilePath g_temp_history_dir;
 
 std::unique_ptr<KeyedService> BuildTestHistoryService(
@@ -638,6 +654,20 @@
       item->play_next_candidate->action->url = GURL("https://www.example.com");
       item->play_next_candidate->identifiers.push_back(CreateIdentifier(
           media_feeds::mojom::Identifier::Type::kTMSId, "TEST4"));
+
+      {
+        media_session::MediaImage image;
+        image.src = GURL("https://www.example.org/image1.png");
+        item->images.push_back(image);
+      }
+
+      {
+        media_session::MediaImage image;
+        image.src = GURL("https://www.example.org/image2.png");
+        image.sizes.push_back(gfx::Size(10, 10));
+        item->images.push_back(image);
+      }
+
       items.push_back(std::move(item));
     }
 
@@ -646,7 +676,7 @@
       item->type = media_feeds::mojom::MediaFeedItemType::kTVSeries;
       item->name = base::ASCIIToUTF16("The TV Series");
       item->action_status =
-          media_feeds::mojom::MediaFeedItemActionStatus::kPotential;
+          media_feeds::mojom::MediaFeedItemActionStatus::kActive;
       item->author = media_feeds::mojom::Author::New();
       item->author->name = "Media Site";
       items.push_back(std::move(item));
@@ -684,6 +714,25 @@
     return items;
   }
 
+  static std::vector<media_session::MediaImage> GetExpectedLogos() {
+    std::vector<media_session::MediaImage> logos;
+
+    {
+      media_session::MediaImage image;
+      image.src = GURL("https://www.example.org/image1.png");
+      image.sizes.push_back(gfx::Size(10, 10));
+      logos.push_back(image);
+    }
+
+    {
+      media_session::MediaImage image;
+      image.src = GURL("https://www.example.org/image2.png");
+      logos.push_back(image);
+    }
+
+    return logos;
+  }
+
  private:
   base::test::ScopedFeatureList features_;
 };
@@ -719,6 +768,17 @@
 
       EXPECT_EQ(1, feeds[0]->id);
       EXPECT_EQ(url_a, feeds[0]->url);
+      EXPECT_FALSE(feeds[0]->last_fetch_time.has_value());
+      EXPECT_EQ(media_feeds::mojom::FetchResult::kNone,
+                feeds[0]->last_fetch_result);
+      EXPECT_EQ(0, feeds[0]->fetch_failed_count);
+      EXPECT_FALSE(feeds[0]->cache_expiry_time.has_value());
+      EXPECT_EQ(0, feeds[0]->last_fetch_item_count);
+      EXPECT_EQ(0, feeds[0]->last_fetch_play_next_count);
+      EXPECT_EQ(0, feeds[0]->last_fetch_content_types);
+      EXPECT_TRUE(feeds[0]->logos.empty());
+      EXPECT_TRUE(feeds[0]->display_name.empty());
+
       EXPECT_EQ(2, feeds[1]->id);
       EXPECT_EQ(url_b, feeds[1]->url);
     }
@@ -751,7 +811,7 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, ReplaceMediaFeedItems) {
+TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult) {
   service()->DiscoverMediaFeed(GURL("https://www.google.com/feed"));
   WaitForDB();
 
@@ -760,42 +820,80 @@
   // to ensure a no-op.
   const int feed_id = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
 
-  service()->ReplaceMediaFeedItems(feed_id, GetExpectedItems());
+  service()->StoreMediaFeedFetchResult(
+      feed_id, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
+      base::Time::Now(), GetExpectedLogos(), kExpectedDisplayName);
   WaitForDB();
 
   {
-    // The media items should be stored.
+    // The media items should be stored and the feed should be updated.
+    auto feeds = GetMediaFeedsSync(service());
     auto items = GetItemsForMediaFeedSync(service(), feed_id);
 
     if (IsReadOnly()) {
+      EXPECT_TRUE(feeds.empty());
       EXPECT_TRUE(items.empty());
     } else {
+      EXPECT_EQ(feed_id, feeds[0]->id);
+      EXPECT_TRUE(feeds[0]->last_fetch_time.has_value());
+      EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
+                feeds[0]->last_fetch_result);
+      EXPECT_EQ(0, feeds[0]->fetch_failed_count);
+      EXPECT_TRUE(feeds[0]->cache_expiry_time.has_value());
+      EXPECT_EQ(kExpectedFetchItemCount, feeds[0]->last_fetch_item_count);
+      EXPECT_EQ(kExpectedFetchPlayNextCount,
+                feeds[0]->last_fetch_play_next_count);
+      EXPECT_EQ(kExpectedFetchContentTypes, feeds[0]->last_fetch_content_types);
+      EXPECT_EQ(GetExpectedLogos(), feeds[0]->logos);
+      EXPECT_EQ(kExpectedDisplayName, feeds[0]->display_name);
+
       EXPECT_EQ(GetExpectedItems(), items);
     }
 
     // The OTR service should have the same data.
+    EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
     EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
   }
 
-  service()->ReplaceMediaFeedItems(feed_id, GetAltExpectedItems());
+  service()->StoreMediaFeedFetchResult(
+      feed_id, GetAltExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
+      base::Time::Now(), std::vector<media_session::MediaImage>(),
+      kExpectedDisplayName);
   WaitForDB();
 
   {
-    // The new media items should be stored.
+    // The media items should be stored and the feed should be updated.
+    auto feeds = GetMediaFeedsSync(service());
     auto items = GetItemsForMediaFeedSync(service(), feed_id);
 
     if (IsReadOnly()) {
+      EXPECT_TRUE(feeds.empty());
       EXPECT_TRUE(items.empty());
     } else {
+      EXPECT_EQ(feed_id, feeds[0]->id);
+      EXPECT_TRUE(feeds[0]->last_fetch_time.has_value());
+      EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
+                feeds[0]->last_fetch_result);
+      EXPECT_EQ(0, feeds[0]->fetch_failed_count);
+      EXPECT_TRUE(feeds[0]->cache_expiry_time.has_value());
+      EXPECT_EQ(kExpectedAltFetchItemCount, feeds[0]->last_fetch_item_count);
+      EXPECT_EQ(kExpectedAltFetchPlayNextCount,
+                feeds[0]->last_fetch_play_next_count);
+      EXPECT_EQ(kExpectedAltFetchContentTypes,
+                feeds[0]->last_fetch_content_types);
+      EXPECT_TRUE(feeds[0]->logos.empty());
+      EXPECT_EQ(kExpectedDisplayName, feeds[0]->display_name);
+
       EXPECT_EQ(GetAltExpectedItems(), items);
     }
 
     // The OTR service should have the same data.
+    EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
     EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, ReplaceMediaFeedItems_WithEmpty) {
+TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_WithEmpty) {
   service()->DiscoverMediaFeed(GURL("https://www.google.com/feed"));
   WaitForDB();
 
@@ -804,7 +902,10 @@
   // to ensure a no-op.
   const int feed_id = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
 
-  service()->ReplaceMediaFeedItems(feed_id, GetExpectedItems());
+  service()->StoreMediaFeedFetchResult(
+      feed_id, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
+      base::Time::Now(), std::vector<media_session::MediaImage>(),
+      std::string());
   WaitForDB();
 
   {
@@ -821,8 +922,10 @@
     EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
   }
 
-  service()->ReplaceMediaFeedItems(
-      feed_id, std::vector<media_feeds::mojom::MediaFeedItemPtr>());
+  service()->StoreMediaFeedFetchResult(
+      feed_id, std::vector<media_feeds::mojom::MediaFeedItemPtr>(),
+      media_feeds::mojom::FetchResult::kSuccess, base::Time::Now(),
+      std::vector<media_session::MediaImage>(), std::string());
   WaitForDB();
 
   {
@@ -835,7 +938,7 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, ReplaceMediaFeedItems_MultipleFeeds) {
+TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_MultipleFeeds) {
   service()->DiscoverMediaFeed(GURL("https://www.google.com/feed"));
   service()->DiscoverMediaFeed(GURL("https://www.google.co.uk/feed"));
   WaitForDB();
@@ -846,13 +949,43 @@
   const int feed_id_a = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
   const int feed_id_b = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[1]->id;
 
-  service()->ReplaceMediaFeedItems(feed_id_a, GetExpectedItems());
+  service()->StoreMediaFeedFetchResult(
+      feed_id_a, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
+      base::Time::Now(), std::vector<media_session::MediaImage>(),
+      std::string());
   WaitForDB();
 
-  service()->ReplaceMediaFeedItems(feed_id_b, GetAltExpectedItems());
+  service()->StoreMediaFeedFetchResult(
+      feed_id_b, GetAltExpectedItems(),
+      media_feeds::mojom::FetchResult::kFailedNetworkError, base::Time::Now(),
+      std::vector<media_session::MediaImage>(), std::string());
   WaitForDB();
 
   {
+    // Check the feeds were updated.
+    auto feeds = GetMediaFeedsSync(service());
+
+    if (IsReadOnly()) {
+      EXPECT_TRUE(feeds.empty());
+    } else {
+      EXPECT_EQ(2u, feeds.size());
+
+      EXPECT_EQ(feed_id_a, feeds[0]->id);
+      EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
+                feeds[0]->last_fetch_result);
+      EXPECT_EQ(0, feeds[0]->fetch_failed_count);
+
+      EXPECT_EQ(feed_id_b, feeds[1]->id);
+      EXPECT_EQ(media_feeds::mojom::FetchResult::kFailedNetworkError,
+                feeds[1]->last_fetch_result);
+      EXPECT_EQ(1, feeds[1]->fetch_failed_count);
+    }
+
+    // The OTR service should have the same data.
+    EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
+  }
+
+  {
     // The media items should be stored.
     auto items = GetItemsForMediaFeedSync(service(), feed_id_a);
 
@@ -881,7 +1014,7 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, ReplaceMediaFeedItems_BadType) {
+TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_BadType) {
   service()->DiscoverMediaFeed(GURL("https://www.google.com/feed"));
   WaitForDB();
 
@@ -890,7 +1023,10 @@
   // to ensure a no-op.
   const int feed_id = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
 
-  service()->ReplaceMediaFeedItems(feed_id, GetExpectedItems());
+  service()->StoreMediaFeedFetchResult(
+      feed_id, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
+      base::Time::Now(), std::vector<media_session::MediaImage>(),
+      std::string());
   WaitForDB();
 
   {
@@ -942,7 +1078,10 @@
     EXPECT_EQ(feed_url, feeds[0]->url);
   }
 
-  service()->ReplaceMediaFeedItems(feed_id, GetExpectedItems());
+  service()->StoreMediaFeedFetchResult(
+      feed_id, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
+      base::Time::Now(), std::vector<media_session::MediaImage>(),
+      std::string());
   WaitForDB();
 
   {
@@ -969,6 +1108,8 @@
     EXPECT_LE(feed_last_time, feeds[0]->last_discovery_time);
     EXPECT_EQ(feed_id, feeds[0]->id);
     EXPECT_EQ(feed_url, feeds[0]->url);
+    EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
+              feeds[0]->last_fetch_result);
   }
 
   {
@@ -996,6 +1137,8 @@
     EXPECT_LE(feed_last_time, feeds[0]->last_discovery_time);
     EXPECT_LT(feed_id, feeds[0]->id);
     EXPECT_EQ(new_url, feeds[0]->url);
+    EXPECT_EQ(media_feeds::mojom::FetchResult::kNone,
+              feeds[0]->last_fetch_result);
   }
 
   {
@@ -1008,4 +1151,223 @@
   }
 }
 
+TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_IncreaseFailed) {
+  service()->DiscoverMediaFeed(GURL("https://www.google.com/feed"));
+  WaitForDB();
+
+  // If we are read only we should use -1 as a placeholder feed id because the
+  // feed will not have been stored. This is so we can run the rest of the test
+  // to ensure a no-op.
+  const int feed_id = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
+
+  service()->StoreMediaFeedFetchResult(
+      feed_id, GetExpectedItems(),
+      media_feeds::mojom::FetchResult::kFailedNetworkError, base::Time::Now(),
+      GetExpectedLogos(), kExpectedDisplayName);
+  WaitForDB();
+
+  {
+    // The fetch failed count should have been increased.
+    auto feeds = GetMediaFeedsSync(service());
+
+    if (IsReadOnly()) {
+      EXPECT_TRUE(feeds.empty());
+    } else {
+      EXPECT_EQ(feed_id, feeds[0]->id);
+      EXPECT_EQ(media_feeds::mojom::FetchResult::kFailedNetworkError,
+                feeds[0]->last_fetch_result);
+      EXPECT_EQ(1, feeds[0]->fetch_failed_count);
+    }
+
+    // The OTR service should have the same data.
+    EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
+  }
+
+  service()->StoreMediaFeedFetchResult(
+      feed_id, GetExpectedItems(),
+      media_feeds::mojom::FetchResult::kFailedBackendError, base::Time::Now(),
+      GetExpectedLogos(), kExpectedDisplayName);
+  WaitForDB();
+
+  {
+    // The fetch failed count should have been increased.
+    auto feeds = GetMediaFeedsSync(service());
+
+    if (IsReadOnly()) {
+      EXPECT_TRUE(feeds.empty());
+    } else {
+      EXPECT_EQ(feed_id, feeds[0]->id);
+      EXPECT_EQ(media_feeds::mojom::FetchResult::kFailedBackendError,
+                feeds[0]->last_fetch_result);
+      EXPECT_EQ(2, feeds[0]->fetch_failed_count);
+    }
+
+    // The OTR service should have the same data.
+    EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
+  }
+
+  service()->StoreMediaFeedFetchResult(
+      feed_id, GetExpectedItems(), media_feeds::mojom::FetchResult::kSuccess,
+      base::Time::Now(), GetExpectedLogos(), kExpectedDisplayName);
+  WaitForDB();
+
+  {
+    // The fetch failed count should have been reset.
+    auto feeds = GetMediaFeedsSync(service());
+
+    if (IsReadOnly()) {
+      EXPECT_TRUE(feeds.empty());
+    } else {
+      EXPECT_EQ(feed_id, feeds[0]->id);
+      EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
+                feeds[0]->last_fetch_result);
+      EXPECT_EQ(0, feeds[0]->fetch_failed_count);
+    }
+
+    // The OTR service should have the same data.
+    EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
+  }
+}
+
+TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_CheckLogoMax) {
+  service()->DiscoverMediaFeed(GURL("https://www.google.com/feed"));
+  WaitForDB();
+
+  // If we are read only we should use -1 as a placeholder feed id because the
+  // feed will not have been stored. This is so we can run the rest of the test
+  // to ensure a no-op.
+  const int feed_id = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
+
+  std::vector<media_session::MediaImage> logos;
+
+  {
+    media_session::MediaImage image;
+    image.src = GURL("https://www.example.org/image1.png");
+    logos.push_back(image);
+  }
+
+  {
+    media_session::MediaImage image;
+    image.src = GURL("https://www.example.org/image2.png");
+    logos.push_back(image);
+  }
+
+  {
+    media_session::MediaImage image;
+    image.src = GURL("https://www.example.org/image3.png");
+    logos.push_back(image);
+  }
+
+  {
+    media_session::MediaImage image;
+    image.src = GURL("https://www.example.org/image4.png");
+    logos.push_back(image);
+  }
+
+  {
+    media_session::MediaImage image;
+    image.src = GURL("https://www.example.org/image5.png");
+    logos.push_back(image);
+  }
+
+  {
+    media_session::MediaImage image;
+    image.src = GURL("https://www.example.org/image6.png");
+    logos.push_back(image);
+  }
+
+  service()->StoreMediaFeedFetchResult(
+      feed_id, GetExpectedItems(),
+      media_feeds::mojom::FetchResult::kFailedNetworkError, base::Time::Now(),
+      logos, kExpectedDisplayName);
+  WaitForDB();
+
+  {
+    // The feed should have at most 5 logos.
+    auto feeds = GetMediaFeedsSync(service());
+
+    if (IsReadOnly()) {
+      EXPECT_TRUE(feeds.empty());
+    } else {
+      EXPECT_EQ(feed_id, feeds[0]->id);
+      EXPECT_EQ(5u, feeds[0]->logos.size());
+    }
+
+    // The OTR service should have the same data.
+    EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
+  }
+}
+
+TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_CheckImageMax) {
+  service()->DiscoverMediaFeed(GURL("https://www.google.com/feed"));
+  WaitForDB();
+
+  // If we are read only we should use -1 as a placeholder feed id because the
+  // feed will not have been stored. This is so we can run the rest of the test
+  // to ensure a no-op.
+  const int feed_id = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
+
+  auto item = media_feeds::mojom::MediaFeedItem::New();
+  item->name = base::ASCIIToUTF16("The Movie");
+  item->type = media_feeds::mojom::MediaFeedItemType::kMovie;
+
+  {
+    media_session::MediaImage image;
+    image.src = GURL("https://www.example.org/image1.png");
+    item->images.push_back(image);
+  }
+
+  {
+    media_session::MediaImage image;
+    image.src = GURL("https://www.example.org/image2.png");
+    item->images.push_back(image);
+  }
+
+  {
+    media_session::MediaImage image;
+    image.src = GURL("https://www.example.org/image3.png");
+    item->images.push_back(image);
+  }
+
+  {
+    media_session::MediaImage image;
+    image.src = GURL("https://www.example.org/image4.png");
+    item->images.push_back(image);
+  }
+
+  {
+    media_session::MediaImage image;
+    image.src = GURL("https://www.example.org/image5.png");
+    item->images.push_back(image);
+  }
+
+  {
+    media_session::MediaImage image;
+    image.src = GURL("https://www.example.org/image6.png");
+    item->images.push_back(image);
+  }
+
+  std::vector<media_feeds::mojom::MediaFeedItemPtr> items;
+  items.push_back(std::move(item));
+
+  service()->StoreMediaFeedFetchResult(
+      feed_id, std::move(items), media_feeds::mojom::FetchResult::kSuccess,
+      base::Time::Now(), GetExpectedLogos(), kExpectedDisplayName);
+  WaitForDB();
+
+  {
+    // The item should have at most 5 images.
+    auto items = GetItemsForMediaFeedSync(service(), feed_id);
+
+    if (IsReadOnly()) {
+      EXPECT_TRUE(items.empty());
+    } else {
+      EXPECT_EQ(5u, items[0]->images.size());
+    }
+
+    // The OTR service should have the same data.
+    EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
+  }
+}
+
 }  // namespace media_history
diff --git a/chrome/browser/media/history/media_history_table_base.cc b/chrome/browser/media/history/media_history_table_base.cc
index 31539bb..ab06c00 100644
--- a/chrome/browser/media/history/media_history_table_base.cc
+++ b/chrome/browser/media/history/media_history_table_base.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/media/history/media_history_table_base.h"
 
 #include "base/updateable_sequenced_task_runner.h"
+#include "sql/statement.h"
+#include "third_party/protobuf/src/google/protobuf/message_lite.h"
 
 namespace media_history {
 
@@ -41,4 +43,21 @@
   return db_;
 }
 
+void MediaHistoryTableBase::BindProto(
+    sql::Statement& s,
+    int col,
+    const google::protobuf::MessageLite& protobuf) {
+  std::string out;
+  CHECK(protobuf.SerializeToString(&out));
+  s.BindBlob(col, out.data(), out.size());
+}
+
+bool MediaHistoryTableBase::GetProto(sql::Statement& s,
+                                     int col,
+                                     google::protobuf::MessageLite& protobuf) {
+  std::string value;
+  s.ColumnBlobAsString(col, &value);
+  return protobuf.ParseFromString(value);
+}
+
 }  // namespace media_history
diff --git a/chrome/browser/media/history/media_history_table_base.h b/chrome/browser/media/history/media_history_table_base.h
index a94f1961..27ac2b1 100644
--- a/chrome/browser/media/history/media_history_table_base.h
+++ b/chrome/browser/media/history/media_history_table_base.h
@@ -12,8 +12,15 @@
 class UpdateableSequencedTaskRunner;
 }  // namespace base
 
+namespace google {
+namespace protobuf {
+class MessageLite;
+}  // namespace protobuf
+}  // namespace google
+
 namespace sql {
 class Database;
+class Statement;
 }  // namespace sql
 
 namespace media_history {
@@ -37,6 +44,17 @@
   void ResetDB();
   bool CanAccessDatabase();
 
+  // Serializes and binds the |protobuf| to be stored in |col| of the statement.
+  void BindProto(sql::Statement& s,
+                 int col,
+                 const google::protobuf::MessageLite& protobuf);
+
+  // Deserializes the proto stored in |col| and stores it in |protobuf|. Returns
+  // true if it was successful.
+  bool GetProto(sql::Statement& s,
+                int col,
+                google::protobuf::MessageLite& protobuf);
+
  private:
   friend class base::RefCountedThreadSafe<MediaHistoryTableBase>;
 
diff --git a/chrome/browser/metrics/chrome_metrics_services_manager_client.cc b/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
index c75a9c8..d12191e6 100644
--- a/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
@@ -45,8 +45,8 @@
 #include "base/win/registry.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/install_static/install_util.h"
-#include "components/crash/content/app/crash_export_thunks.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crash_export_thunks.h"
+#include "components/crash/core/app/crashpad.h"
 #endif  // OS_WIN
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/net/dns_util.cc b/chrome/browser/net/dns_util.cc
index 33e3549..0d77d75 100644
--- a/chrome/browser/net/dns_util.cc
+++ b/chrome/browser/net/dns_util.cc
@@ -4,9 +4,14 @@
 
 #include "chrome/browser/net/dns_util.h"
 
+#include "base/feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
+#include "chrome/common/chrome_features.h"
+#include "components/embedder_support/pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
 #include "net/third_party/uri_template/uri_template.h"
 #include "url/gurl.h"
 
@@ -20,6 +25,8 @@
 
 namespace {
 
+const char kAlternateErrorPagesBackup[] = "alternate_error_pages.backup";
+
 #if defined(OS_WIN)
 bool ShouldDisableDohForWindowsParentalControls() {
   const WinParentalControls& parental_controls = GetWinParentalControls();
@@ -59,4 +66,44 @@
   return false;
 }
 
+void RegisterDNSProbesSettingBackupPref(PrefRegistrySimple* registry) {
+  registry->RegisterBooleanPref(kAlternateErrorPagesBackup, true);
+}
+
+void MigrateDNSProbesSettingToOrFromBackup(PrefService* prefs) {
+  // If the privacy settings redesign is enabled and the user value of the
+  // preference hasn't been backed up yet, back it up, and clear it. That way,
+  // the preference will revert to using the hardcoded default value (unless
+  // it's managed by a policy or an extension). This is necessary, as the
+  // privacy settings redesign removed the user-facing toggle, and so the
+  // user value of the preference is no longer modifiable.
+  if (base::FeatureList::IsEnabled(features::kPrivacySettingsRedesign) &&
+      !prefs->HasPrefPath(kAlternateErrorPagesBackup)) {
+    // If the user never changed the value of the preference and still uses the
+    // hardcoded default value, we'll consider it to be the user value for
+    // the purposes of this migration.
+    const base::Value* user_value =
+        prefs->FindPreference(embedder_support::kAlternateErrorPagesEnabled)
+                ->HasUserSetting()
+            ? prefs->GetUserPrefValue(
+                  embedder_support::kAlternateErrorPagesEnabled)
+            : prefs->GetDefaultPrefValue(
+                  embedder_support::kAlternateErrorPagesEnabled);
+
+    DCHECK(user_value->is_bool());
+    prefs->SetBoolean(kAlternateErrorPagesBackup, user_value->GetBool());
+    prefs->ClearPref(embedder_support::kAlternateErrorPagesEnabled);
+  }
+
+  // If the privacy settings redesign is rolled back and there is a backed up
+  // value of the preference, restore it to the original preference, and clear
+  // the backup.
+  if (!base::FeatureList::IsEnabled(features::kPrivacySettingsRedesign) &&
+      prefs->HasPrefPath(kAlternateErrorPagesBackup)) {
+    prefs->SetBoolean(embedder_support::kAlternateErrorPagesEnabled,
+                      prefs->GetBoolean(kAlternateErrorPagesBackup));
+    prefs->ClearPref(kAlternateErrorPagesBackup);
+  }
+}
+
 }  // namespace chrome_browser_net
diff --git a/chrome/browser/net/dns_util.h b/chrome/browser/net/dns_util.h
index af7fb17..e1e0305e 100644
--- a/chrome/browser/net/dns_util.h
+++ b/chrome/browser/net/dns_util.h
@@ -7,6 +7,9 @@
 
 #include <string>
 
+class PrefRegistrySimple;
+class PrefService;
+
 namespace chrome_browser_net {
 
 // Returns true if there are any active machine level policies or if the machine
@@ -59,6 +62,18 @@
   kMaxValue = kSecureByEnterprisePolicy,
 };
 
+// Registers the backup preference required for the DNS probes setting reset.
+// TODO(crbug.com/1062698): Remove this once the privacy settings redesign
+// is fully launched.
+void RegisterDNSProbesSettingBackupPref(PrefRegistrySimple* registry);
+
+// Backs up the unneeded preference controlling DNS and captive portal probes
+// once the privacy settings redesign is enabled, or restores the backup
+// in case the feature is rolled back.
+// TODO(crbug.com/1062698): Remove this once the privacy settings redesign
+// is fully launched.
+void MigrateDNSProbesSettingToOrFromBackup(PrefService* prefs);
+
 }  // namespace chrome_browser_net
 
 #endif  // CHROME_BROWSER_NET_DNS_UTIL_H_
diff --git a/chrome/browser/net/dns_util_unittest.cc b/chrome/browser/net/dns_util_unittest.cc
new file mode 100644
index 0000000..0ac134c
--- /dev/null
+++ b/chrome/browser/net/dns_util_unittest.cc
@@ -0,0 +1,145 @@
+// 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 "chrome/browser/net/dns_util.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/common/chrome_features.h"
+#include "components/embedder_support/pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chrome_browser_net {
+
+namespace {
+
+const char kAlternateErrorPagesBackup[] = "alternate_error_pages.backup";
+
+}  // namespace
+
+class DNSUtilTest : public testing::Test {
+ public:
+  void SetUp() override { DisableRedesign(); }
+
+  void EnableRedesign() {
+    scoped_feature_list_.Reset();
+    scoped_feature_list_.InitAndEnableFeatureWithParameters(
+        features::kPrivacySettingsRedesign, base::FieldTrialParams());
+  }
+
+  void DisableRedesign() { scoped_feature_list_.Reset(); }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(DNSUtilTest, MigrateDNSProbesPref) {
+  TestingPrefServiceSimple prefs;
+  prefs.registry()->RegisterBooleanPref(
+      embedder_support::kAlternateErrorPagesEnabled, true);
+  prefs.registry()->RegisterBooleanPref(kAlternateErrorPagesBackup, true);
+
+  const PrefService::Preference* current_pref =
+      prefs.FindPreference(embedder_support::kAlternateErrorPagesEnabled);
+  const PrefService::Preference* backup_pref =
+      prefs.FindPreference(kAlternateErrorPagesBackup);
+
+  // No migration happens if the privacy settings redesign is not enabled.
+  MigrateDNSProbesSettingToOrFromBackup(&prefs);
+  EXPECT_FALSE(backup_pref->HasUserSetting());
+
+  // The hardcoded default value of TRUE gets correctly migrated.
+  EnableRedesign();
+  MigrateDNSProbesSettingToOrFromBackup(&prefs);
+  EXPECT_FALSE(current_pref->HasUserSetting());
+  EXPECT_TRUE(backup_pref->HasUserSetting());
+  EXPECT_TRUE(prefs.GetBoolean(kAlternateErrorPagesBackup));
+
+  // And correctly restored.
+  DisableRedesign();
+  MigrateDNSProbesSettingToOrFromBackup(&prefs);
+  EXPECT_TRUE(current_pref->HasUserSetting());
+  EXPECT_TRUE(prefs.GetBoolean(embedder_support::kAlternateErrorPagesEnabled));
+  EXPECT_FALSE(backup_pref->HasUserSetting());
+
+  // An explicit user value of TRUE will be correctly migrated.
+  EnableRedesign();
+  prefs.SetBoolean(embedder_support::kAlternateErrorPagesEnabled, true);
+  MigrateDNSProbesSettingToOrFromBackup(&prefs);
+  EXPECT_FALSE(current_pref->HasUserSetting());
+  EXPECT_TRUE(backup_pref->HasUserSetting());
+  EXPECT_TRUE(prefs.GetBoolean(kAlternateErrorPagesBackup));
+
+  // And correctly restored.
+  DisableRedesign();
+  MigrateDNSProbesSettingToOrFromBackup(&prefs);
+  EXPECT_TRUE(current_pref->HasUserSetting());
+  EXPECT_TRUE(prefs.GetBoolean(embedder_support::kAlternateErrorPagesEnabled));
+  EXPECT_FALSE(backup_pref->HasUserSetting());
+
+  // An explicit user value of FALSE will also be correctly migrated.
+  EnableRedesign();
+  prefs.SetBoolean(embedder_support::kAlternateErrorPagesEnabled, false);
+  MigrateDNSProbesSettingToOrFromBackup(&prefs);
+  EXPECT_FALSE(current_pref->HasUserSetting());
+  EXPECT_TRUE(backup_pref->HasUserSetting());
+  EXPECT_FALSE(prefs.GetBoolean(kAlternateErrorPagesBackup));
+
+  // And correctly restored.
+  DisableRedesign();
+  MigrateDNSProbesSettingToOrFromBackup(&prefs);
+  EXPECT_TRUE(current_pref->HasUserSetting());
+  EXPECT_FALSE(prefs.GetBoolean(embedder_support::kAlternateErrorPagesEnabled));
+  EXPECT_FALSE(backup_pref->HasUserSetting());
+
+  // A policy-sourced value of TRUE takes precedence over the user-sourced value
+  // of FALSE when the preference is evaluated. However, it will still be the
+  // user-sourced value of FALSE that will be migrated.
+  prefs.SetManagedPref(embedder_support::kAlternateErrorPagesEnabled,
+                       std::make_unique<base::Value>(true));
+  EnableRedesign();
+  MigrateDNSProbesSettingToOrFromBackup(&prefs);
+  EXPECT_FALSE(current_pref->HasUserSetting());
+  EXPECT_TRUE(backup_pref->HasUserSetting());
+  EXPECT_FALSE(prefs.GetBoolean(kAlternateErrorPagesBackup));
+
+  // And correctly restored.
+  DisableRedesign();
+  MigrateDNSProbesSettingToOrFromBackup(&prefs);
+  EXPECT_TRUE(current_pref->HasUserSetting());
+  {
+    const base::Value* user_pref =
+        prefs.GetUserPref(embedder_support::kAlternateErrorPagesEnabled);
+    ASSERT_TRUE(user_pref->is_bool());
+    EXPECT_FALSE(user_pref->GetBool());
+  }
+  EXPECT_FALSE(backup_pref->HasUserSetting());
+
+  // After clearing the user-sourced value, the hardcoded value of TRUE should
+  // be the value which is migrated, even if it is overridden by
+  // a policy-sourced value of FALSE.
+  prefs.ClearPref(embedder_support::kAlternateErrorPagesEnabled);
+  prefs.SetManagedPref(embedder_support::kAlternateErrorPagesEnabled,
+                       std::make_unique<base::Value>(false));
+  EnableRedesign();
+  MigrateDNSProbesSettingToOrFromBackup(&prefs);
+  EXPECT_FALSE(current_pref->HasUserSetting());
+  EXPECT_TRUE(backup_pref->HasUserSetting());
+  EXPECT_TRUE(prefs.GetBoolean(kAlternateErrorPagesBackup));
+
+  // And correctly restored.
+  DisableRedesign();
+  MigrateDNSProbesSettingToOrFromBackup(&prefs);
+  EXPECT_TRUE(current_pref->HasUserSetting());
+  {
+    const base::Value* user_pref =
+        prefs.GetUserPref(embedder_support::kAlternateErrorPagesEnabled);
+    ASSERT_TRUE(user_pref->is_bool());
+    EXPECT_TRUE(user_pref->GetBool());
+  }
+  EXPECT_FALSE(backup_pref->HasUserSetting());
+}
+
+}  // namespace chrome_browser_net
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac.mm b/chrome/browser/notifications/notification_platform_bridge_mac.mm
index 51319f8..ebd61c7 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac.mm
@@ -37,7 +37,7 @@
 #import "chrome/browser/ui/cocoa/notifications/notification_response_builder_mac.h"
 #include "chrome/common/buildflags.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/url_formatter/elide_url.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/page_load_metrics/integration_tests/first_input_delay_browsertest.cc b/chrome/browser/page_load_metrics/integration_tests/first_input_delay_browsertest.cc
new file mode 100644
index 0000000..c5bc3204
--- /dev/null
+++ b/chrome/browser/page_load_metrics/integration_tests/first_input_delay_browsertest.cc
@@ -0,0 +1,49 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/integration_tests/metric_integration_test.h"
+
+#include "base/test/trace_event_analyzer.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+
+using ukm::builders::PageLoad;
+
+IN_PROC_BROWSER_TEST_F(MetricIntegrationTest, FirstInputDelay) {
+  LoadHTML(R"HTML(
+    <p>Sample website</p>
+    <script>
+    runtest = async () => {
+      const observePromise = new Promise(resolve => {
+        new PerformanceObserver(e => {
+          e.getEntries().forEach(entry => {
+            const fid = entry.processingStart - entry.startTime;
+            resolve(fid);
+          })
+        }).observe({type: 'first-input', buffered: true});
+      });
+      return await observePromise;
+    };
+    </script>
+  )HTML");
+
+  StartTracing({"loading"});
+
+  content::SimulateMouseClick(web_contents(), 0,
+                              blink::WebMouseEvent::Button::kLeft);
+
+  // Check web perf API.
+  double expected_fid = EvalJs(web_contents(), "runtest()").ExtractDouble();
+  EXPECT_GT(expected_fid, 0.0);
+
+  ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
+
+  // Check UKM.
+  ExpectUKMPageLoadMetric(PageLoad::kInteractiveTiming_FirstInputDelay4Name,
+                          expected_fid);
+
+  // Check UMA.
+  histogram_tester().ExpectUniqueSample(
+      "PageLoad.InteractiveTiming.FirstInputDelay4", expected_fid, 1);
+}
diff --git a/chrome/browser/page_load_metrics/integration_tests/metric_integration_test.cc b/chrome/browser/page_load_metrics/integration_tests/metric_integration_test.cc
index 61e1b1a..a85eb97 100644
--- a/chrome/browser/page_load_metrics/integration_tests/metric_integration_test.cc
+++ b/chrome/browser/page_load_metrics/integration_tests/metric_integration_test.cc
@@ -130,13 +130,10 @@
 
 void MetricIntegrationTest::ExpectUKMPageLoadMetric(StringPiece metric_name,
                                                     int64_t expected_value) {
-  std::vector<const UkmEntry*> entries =
-      ukm_recorder().GetEntriesByName(PageLoad::kEntryName);
-  auto name_filter = [&metric_name](const UkmEntry* entry) {
-    return !TestUkmRecorder::EntryHasMetric(entry, metric_name);
-  };
-  entries.erase(std::remove_if(entries.begin(), entries.end(), name_filter),
-                entries.end());
-  EXPECT_EQ(1ul, entries.size());
-  TestUkmRecorder::ExpectEntryMetric(entries[0], metric_name, expected_value);
+  std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
+      ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+  EXPECT_EQ(1ul, merged_entries.size());
+  const auto& kv = merged_entries.begin();
+  TestUkmRecorder::ExpectEntryMetric(kv->second.get(), metric_name,
+                                     expected_value);
 }
diff --git a/chrome/browser/page_load_metrics/integration_tests/sources.gni b/chrome/browser/page_load_metrics/integration_tests/sources.gni
index c25d588..73edadfb 100644
--- a/chrome/browser/page_load_metrics/integration_tests/sources.gni
+++ b/chrome/browser/page_load_metrics/integration_tests/sources.gni
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 metric_integration_sources = [
+  "//chrome/browser/page_load_metrics/integration_tests/first_input_delay_browsertest.cc",
   "//chrome/browser/page_load_metrics/integration_tests/layout_instability_browsertest.cc",
   "//chrome/browser/page_load_metrics/integration_tests/metric_integration_test.cc",
   "//chrome/browser/page_load_metrics/integration_tests/metric_integration_test.h",
diff --git a/chrome/browser/password_manager/password_accessory_controller_impl.cc b/chrome/browser/password_manager/password_accessory_controller_impl.cc
index fec4632..95d05ee 100644
--- a/chrome/browser/password_manager/password_accessory_controller_impl.cc
+++ b/chrome/browser/password_manager/password_accessory_controller_impl.cc
@@ -210,6 +210,18 @@
                << static_cast<int>(selected_action);
 }
 
+void PasswordAccessoryControllerImpl::OnToggleChanged(
+    autofill::AccessoryAction toggled_action,
+    bool enabled) {
+  if (toggled_action == autofill::AccessoryAction::TOGGLE_SAVE_PASSWORDS) {
+    // TODO(crbug.com/1044930): Update the cache and the password store
+    // according to the toggle value.
+    return;
+  }
+  NOTREACHED() << "Unhandled selected action: "
+               << static_cast<int>(toggled_action);
+}
+
 void PasswordAccessoryControllerImpl::RefreshSuggestionsForField(
     FocusedFieldType focused_field_type,
     bool is_manual_generation_available) {
diff --git a/chrome/browser/password_manager/password_accessory_controller_impl.h b/chrome/browser/password_manager/password_accessory_controller_impl.h
index 285c31a..005bc0c0 100644
--- a/chrome/browser/password_manager/password_accessory_controller_impl.h
+++ b/chrome/browser/password_manager/password_accessory_controller_impl.h
@@ -41,6 +41,8 @@
   // AccessoryController:
   void OnFillingTriggered(const autofill::UserInfo::Field& selection) override;
   void OnOptionSelected(autofill::AccessoryAction selected_action) override;
+  void OnToggleChanged(autofill::AccessoryAction toggled_action,
+                       bool enabled) override;
 
   // PasswordAccessoryController:
   void RefreshSuggestionsForField(
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index ce266009..5fd211c 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -103,6 +103,7 @@
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h"
 #include "chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.h"
 #include "chrome/browser/chromeos/policy/secondary_google_account_signin_policy_handler.h"
+#include "chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h"
 #include "chrome/browser/policy/default_geolocation_policy_handler.h"
 #include "chrome/common/chrome_features.h"
 #include "chromeos/constants/chromeos_pref_names.h"
@@ -1609,6 +1610,8 @@
   handlers->AddHandler(
       WebUsbAllowDevicesForUrlsPolicyHandler::CreateForDevicePolicy(
           chrome_schema));
+  handlers->AddHandler(
+      std::make_unique<SystemFeaturesDisableListPolicyHandler>());
 #if defined(USE_CUPS)
   handlers->AddHandler(std::make_unique<extensions::ExtensionListPolicyHandler>(
       key::kPrintingAPIExtensionsWhitelist,
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index c94fffe..bb5314e 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -190,6 +190,8 @@
     public static final String CRASH_UPLOAD_SUCCESS_OTHER = "other_crash_success_upload";
     public static final String CRASH_UPLOAD_SUCCESS_RENDERER = "renderer_crash_success_upload";
 
+    public static final String CRYPTID_LAST_RENDER_TIMESTAMP = "Chrome.Cryptid.LastRenderTimestamp";
+
     public static final KeyPrefix CUSTOM_TABS_DEX_LAST_UPDATE_TIME_PREF_PREFIX =
             new KeyPrefix("pref_local_custom_tabs_module_dex_last_update_time_*");
     public static final String CUSTOM_TABS_LAST_URL = "pref_last_custom_tab_url";
@@ -674,6 +676,7 @@
                 CONTEXT_MENU_OPEN_IMAGE_IN_EPHEMERAL_TAB_CLICKED,
                 CONTEXT_MENU_OPEN_IN_EPHEMERAL_TAB_CLICKED,
                 CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS_CLICKED,
+                CRYPTID_LAST_RENDER_TIMESTAMP,
                 EXPLORE_OFFLINE_CONTENT_AVAILABILITY_STATUS,
                 FLAGS_CACHED.pattern(),
                 FLAGS_CACHED_DUET_TABSTRIP_INTEGRATION_ANDROID_ENABLED,
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 39e4dbc..c4e7d779 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -37,6 +37,7 @@
 #include "chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.h"
 #include "chrome/browser/memory/enterprise_memory_limit_pref_observer.h"
 #include "chrome/browser/metrics/chrome_metrics_service_client.h"
+#include "chrome/browser/net/dns_util.h"
 #include "chrome/browser/net/net_error_tab_helper.h"
 #include "chrome/browser/net/prediction_options.h"
 #include "chrome/browser/net/profile_network_context_service.h"
@@ -126,6 +127,7 @@
 #include "components/policy/core/common/policy_pref_names.h"
 #include "components/policy/core/common/policy_statistics_collector.h"
 #include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_registry.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/proxy_config/pref_proxy_config_tracker_impl.h"
@@ -175,6 +177,7 @@
 #include "chrome/browser/chromeos/kerberos/kerberos_credentials_manager.h"
 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.h"
 #include "chrome/browser/chromeos/login/existing_user_controller.h"
+#include "chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h"
 #include "chrome/browser/chromeos/settings/stats_reporting_controller.h"
 #include "chrome/browser/component_updater/metadata_table_chromeos.h"
 #else
@@ -648,6 +651,8 @@
   registry->RegisterListPref(kInvalidatorSavedInvalidations);
   registry->RegisterStringPref(kInvalidatorInvalidationState, std::string());
   registry->RegisterStringPref(kInvalidatorClientId, std::string());
+
+  chrome_browser_net::RegisterDNSProbesSettingBackupPref(registry);
 }
 
 }  // namespace
@@ -771,6 +776,7 @@
   policy::PolicyCertServiceFactory::RegisterPrefs(registry);
   policy::TPMAutoUpdateModePolicyHandler::RegisterPrefs(registry);
   policy::WebUsbAllowDevicesForUrlsPolicyHandler::RegisterPrefs(registry);
+  policy::SystemFeaturesDisableListPolicyHandler::RegisterPrefs(registry);
   quirks::QuirksManager::RegisterPrefs(registry);
   UpgradeDetectorChromeos::RegisterPrefs(registry);
   syncer::PerUserTopicSubscriptionManager::RegisterPrefs(registry);
@@ -1320,4 +1326,5 @@
 
   // Added 3/2020.
   profile_prefs->ClearPref(kDataReductionNetworkProperties);
+  chrome_browser_net::MigrateDNSProbesSettingToOrFromBackup(profile_prefs);
 }
diff --git a/chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/Profile.java b/chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/Profile.java
index cac09b9..c976a79 100644
--- a/chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/Profile.java
+++ b/chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/Profile.java
@@ -134,7 +134,7 @@
     }
 
     @NativeMethods
-    interface Natives {
+    public interface Natives {
         Object getLastUsedRegularProfile();
         Object fromWebContents(WebContents webContents);
         void destroyWhenAppropriate(long nativeProfileAndroid, Profile caller);
diff --git a/chrome/browser/resources/chromeos/login/encryption_migration.html b/chrome/browser/resources/chromeos/login/encryption_migration.html
index 66e958c..902f3e1 100644
--- a/chrome/browser/resources/chromeos/login/encryption_migration.html
+++ b/chrome/browser/resources/chromeos/login/encryption_migration.html
@@ -13,129 +13,117 @@
     <link rel="stylesheet" href="oobe_dialog_host.css">
     <link rel="stylesheet" href="encryption_migration.css">
     <link rel="stylesheet" href="oobe_flex_layout.css">
-    <template is="dom-if" if="[[isInitial_(uiState)]]">
-      <oobe-dialog tabindex="0" has-buttons></oobe-dialog>
-    </template>
-    <template is="dom-if" if="[[isMigratingMinimal_(uiState)]]">
-      <oobe-dialog id="minimal-migration-dialog" tabindex="0" has-buttons>
-        <div slot="footer" class="layout vertical">
-          <throbber-notice text-key="gaiaLoading"></throbber-notice>
+    <oobe-dialog tabindex="0" has-buttons hidden="[[!isInitial_(uiState)]]">
+    </oobe-dialog>
+    <oobe-dialog id="minimal-migration-dialog" tabindex="0" has-buttons
+        hidden="[[!isMigratingMinimal_(uiState)]]">
+      <div slot="footer" class="layout vertical">
+        <throbber-notice text-key="gaiaLoading"></throbber-notice>
+      </div>
+    </oobe-dialog>
+    <oobe-dialog tabindex="0" id="ready-dialog" has-buttons
+        title-key="migrationReadyTitle" hidden="[[!isReady_(uiState)]]"
+        subtitle-key="migrationReadyDescription">
+      <iron-icon slot="oobe-icon" class="warning-icon"></iron-icon>
+      <div slot="footer" class="layout vertical center center-justified"
+          hidden="[[!isEnoughBattery]]">
+        <img srcset="images/security_update_1x.png 1x,
+                     images/security_update_2x.png 2x"
+             class="oobe-illustration">
+      </div>
+      <div slot="footer" class="layout vertical" aria-live="polite"
+          hidden="[[isEnoughBattery]]">
+        <div class="warning">
+          [[computeBatteryWarningLabel_(locale, batteryPercent)]]
         </div>
-      </oobe-dialog>
-    </template>
-    <template is="dom-if" if="[[isReady_(uiState)]]">
-      <oobe-dialog tabindex="0" id="ready-dialog" has-buttons
-          title-key="migrationReadyTitle"
-          subtitle-key="migrationReadyDescription">
-        <iron-icon slot="oobe-icon" class="warning-icon"></iron-icon>
-        <template is="dom-if" if="[[isEnoughBattery]]">
-          <div slot="footer" class="layout vertical center center-justified">
-            <img srcset="images/security_update_1x.png 1x,
-                         images/security_update_2x.png 2x"
-                 class="oobe-illustration">
+        <template is="dom-if" if="[[isCharging]]">
+          <div>[[i18nDynamic(locale,'migrationChargingLabel')]]</div>
+        </template>
+        <template is="dom-if" if="[[!isCharging]]">
+          <div>[[i18nDynamic(locale,'migrationAskChargeMessage')]]</div>
+        </template>
+        <div>
+          [[computeNecessaryBatteryLevelLabel_(locale,
+                                               necessaryBatteryPercent)]]
+        </div>
+      </div>
+      <div slot="bottom-buttons" class="flex layout horizontal"
+          hidden="[[isResuming]]">
+        <div class="flex"></div>
+        <oobe-text-button border id="skip-button" on-tap="onSkip_"
+            hidden="[[isSkipHidden_()]]" text-key="migrationButtonSkip">
+        </oobe-text-button>
+        <oobe-text-button inverse id="upgrade-button" on-tap="onUpgrade_"
+            disabled="[[isUpdateDisabled_(isEnoughBattery, isSkipped)]]"
+            text-key="migrationButtonUpdate"></oobe-text-button>
+      </div>
+    </oobe-dialog>
+    <oobe-dialog id="migrating-dialog" tabindex="0"
+        hidden="[[!isMigrating_(uiState)]]"
+        title-key="migrationMigratingTitle"
+        subtitle-key="migrationMigratingDescription">
+      <iron-icon slot="oobe-icon" class="chrome-logo"></iron-icon>
+      <div slot="footer" class="flex layout vertical">
+        <paper-progress id="migration-progress"
+            value="[[progress]]" max="1" step="0.001"
+            indeterminate="[[isProgressIndeterminate_(progress)]]">
+        </paper-progress>
+        <template is="dom-if" if="[[!isProgressIndeterminate_(progress)]]">
+          <div aria-live="polite">
+            [[computeProgressLabel_(locale, progress)]]
           </div>
         </template>
-        <template is="dom-if" if="[[!isEnoughBattery]]">
-          <div slot="footer" class="layout vertical" aria-live="polite">
-            <div class="warning">
-              [[computeBatteryWarningLabel_(locale, batteryPercent)]]
-            </div>
-            <template is="dom-if" if="[[isCharging]]">
-              <div>[[i18nDynamic(locale,'migrationChargingLabel')]]</div>
-            </template>
-            <template is="dom-if" if="[[!isCharging]]">
-              <div>[[i18nDynamic(locale,'migrationAskChargeMessage')]]</div>
-            </template>
-            <div>
-              [[computeNecessaryBatteryLevelLabel_(locale,
-                                                   necessaryBatteryPercent)]]
-            </div>
-          </div>
-        </template>
-        </div>
-        <template is="dom-if" if="[[!isResuming]]">
-          <div slot="bottom-buttons" class="flex layout horizontal">
-            <div class="flex"></div>
-            <oobe-text-button border id="skip-button" on-tap="onSkip_"
-                hidden="[[isSkipHidden_()]]" text-key="migrationButtonSkip">
-            </oobe-text-button>
-            <oobe-text-button inverse id="upgrade-button" on-tap="onUpgrade_"
-                disabled="[[isUpdateDisabled_(isEnoughBattery, isSkipped)]]"
-                text-key="migrationButtonUpdate"></oobe-text-button>
-          </div>
-        </template>
-      </oobe-dialog>
-    </template>
-    <template is="dom-if" if="[[isMigrating_(uiState)]]">
-      <oobe-dialog id="migrating-dialog" tabindex="0"
-          title-key="migrationMigratingTitle"
-          subtitle-key="migrationMigratingDescription">
-        <iron-icon slot="oobe-icon" class="chrome-logo"></iron-icon>
-        <div slot="footer" class="flex layout vertical">
-          <paper-progress id="migration-progress"
-              value="[[progress]]" max="1" step="0.001"
-              indeterminate="[[isProgressIndeterminate_(progress)]]">
-          </paper-progress>
-          <template is="dom-if" if="[[!isProgressIndeterminate_(progress)]]">
-            <div aria-live="polite">
-              [[computeProgressLabel_(locale, progress)]]
-            </div>
-          </template>
-        </div>
-      </oobe-dialog>
-    </template>
-    <template is="dom-if" if="[[isMigrationFailed_(uiState)]]">
-      <oobe-dialog id="error-dialog" tabindex="0" has-buttons
-          title-key="migrationFailedTitle"
-          subtitle-key="migrationFailedSubtitle">
-        <iron-icon icon="cr:warning" slot="oobe-icon" class="warning">
-        </iron-icon>
-        <div slot="footer">
-          <div>[[i18nDynamic(locale,'migrationFailedMessage')]]</div>
-        </div>
-        <div slot="bottom-buttons" class="flex layout horizontal">
-          <div class="flex"></div>
+      </div>
+    </oobe-dialog>
+    <oobe-dialog id="error-dialog" tabindex="0" has-buttons
+        hidden="[[!isMigrationFailed_(uiState)]]"
+        title-key="migrationFailedTitle"
+        subtitle-key="migrationFailedSubtitle">
+      <iron-icon icon="cr:warning" slot="oobe-icon" class="warning">
+      </iron-icon>
+      <div slot="footer">
+        <div>[[i18nDynamic(locale,'migrationFailedMessage')]]</div>
+      </div>
+      <div slot="bottom-buttons" class="flex layout horizontal">
+        <div class="flex"></div>
 <if expr="_google_chrome">
-          <oobe-text-button border on-tap="onReportAnIssue_"
-              text-key="migrationButtonReportAnIssue"></oobe-text-button>
+        <oobe-text-button border on-tap="onReportAnIssue_"
+            text-key="migrationButtonReportAnIssue"></oobe-text-button>
 </if>
-          <oobe-text-button inverse id="restart-button"
-              on-tap="onRestartOnFailure_" text-key="migrationButtonRestart">
-          </oobe-text-button>
+        <oobe-text-button inverse id="restart-button"
+            on-tap="onRestartOnFailure_" text-key="migrationButtonRestart">
+        </oobe-text-button>
+      </div>
+    </oobe-dialog>
+    <oobe-dialog id="insufficient-space-dialog" tabindex="0" has-buttons
+        hidden="[[!isNotEnoughSpace_(uiState)]]"
+        title-key="migrationReadyTitle"
+        subtitle-key="migrationReadyDescription">
+      <iron-icon slot="oobe-icon" class="chrome-logo"></iron-icon>
+      <div slot="footer" class="layout vertical" aria-live="polite">
+        <div class="warning">
+          [[i18nDynamic(locale,'migrationNospaceWarningLabel')]]
         </div>
-      </oobe-dialog>
-    </template>
-    <template is="dom-if" if="[[isNotEnoughSpace_(uiState)]]">
-      <oobe-dialog id="insufficient-space-dialog" tabindex="0" has-buttons
-          title-key="migrationReadyTitle"
-          subtitle-key="migrationReadyDescription">
-        <iron-icon slot="oobe-icon" class="chrome-logo"></iron-icon>
-        <div slot="footer" class="layout vertical" aria-live="polite">
-          <div class="warning">
-            [[i18nDynamic(locale,'migrationNospaceWarningLabel')]]
-          </div>
-          <div>[[i18nDynamic(locale,'migrationAskFreeSpaceMessage')]]</div>
-          <div>
-            [[computeAvailableSpaceLabel_(locale, availableSpaceInString)]]
-          </div>
-          <div>
-            [[computeNecessarySpaceLabel_(locale, necessarySpaceInString)]]
-          </div>
+        <div>[[i18nDynamic(locale,'migrationAskFreeSpaceMessage')]]</div>
+        <div>
+          [[computeAvailableSpaceLabel_(locale, availableSpaceInString)]]
         </div>
-        <div slot="bottom-buttons" class="flex layout horizontal">
-          <div class="flex"></div>
-          <template is="dom-if" if="[[!isResuming]]">
-            <oobe-text-button inverse id="insufficient-space-skip-button"
-                on-tap="onSkip_" text-key="migrationButtonContinue">
-            </oobe-text-button>
-          </template>
-          <template is="dom-if" if="[[isResuming]]">
-            <oobe-text-button inverse id="insufficient-space-restart-button"
-                on-tap="onRestartOnLowStorage_"
-                text-key="migrationButtonRestart"></oobe-text-button>
-          </template>
+        <div>
+          [[computeNecessarySpaceLabel_(locale, necessarySpaceInString)]]
         </div>
-      </oobe-dialog>
-    </template>
+      </div>
+      <div slot="bottom-buttons" class="flex layout horizontal">
+        <div class="flex"></div>
+        <oobe-text-button inverse id="insufficient-space-skip-button"
+            hidden="[[isResuming]]"
+            on-tap="onSkip_" text-key="migrationButtonContinue">
+        </oobe-text-button>
+        <oobe-text-button inverse id="insufficient-space-restart-button"
+            hidden="[[!isResuming]]"
+            on-tap="onRestartOnLowStorage_"
+            text-key="migrationButtonRestart">
+        </oobe-text-button>
+      </div>
+    </oobe-dialog>
   </template>
 </dom-module>
diff --git a/chrome/browser/resources/chromeos/login/security_token_pin.html b/chrome/browser/resources/chromeos/login/security_token_pin.html
index a1653ed..cff9a95 100644
--- a/chrome/browser/resources/chromeos/login/security_token_pin.html
+++ b/chrome/browser/resources/chromeos/login/security_token_pin.html
@@ -37,13 +37,14 @@
             <div id="errorContainer" role="alert" problem
                 invisible$="[[!isLabelVisible_(parameters, userEdited_)]]">
               <iron-icon id="errorIcon" icon="cr:error-outline"></iron-icon>
-              [[getLabel_(locale, parameters, errorLabelId_, userEdited_)]]
+              <span id="error">[[getLabel_(locale, parameters, errorLabelId_,
+                                           userEdited_)]]</span>
             </div>
           </pin-keyboard>
         </div>
       </div>
       <div slot="bottom-buttons" class="layout horizontal justified">
-        <oobe-back-button on-tap="onBackClicked_"></oobe-back-button>
+        <oobe-back-button id="back" on-tap="onBackClicked_"></oobe-back-button>
         <oobe-next-button id="submit" on-tap="onSubmit_"
             disabled="[[!canSubmit_]]"></oobe-next-button>
       </div>
diff --git a/chrome/browser/resources/chromeos/login/security_token_pin_browsertest.js b/chrome/browser/resources/chromeos/login/security_token_pin_browsertest.js
index bba346fd..c77c553 100644
--- a/chrome/browser/resources/chromeos/login/security_token_pin_browsertest.js
+++ b/chrome/browser/resources/chromeos/login/security_token_pin_browsertest.js
@@ -31,11 +31,15 @@
   let progressElement;
   let pinInput;
   let inputField;
+  let errorContainer;
+  let errorElement;
   let submitElement;
+  let backElement;
 
   setup(() => {
     securityTokenPin = document.createElement('security-token-pin');
     document.body.appendChild(securityTokenPin);
+    securityTokenPin.onBeforeShow();
     securityTokenPin.parameters = DEFAULT_PARAMETERS;
 
     pinKeyboardContainer = securityTokenPin.$$('#pinKeyboardContainer');
@@ -48,8 +52,328 @@
     assert(pinInput);
     inputField = pinInput.$$('input');
     assert(inputField);
+    errorContainer = securityTokenPin.$$('#errorContainer');
+    assert(errorContainer);
+    errorElement = securityTokenPin.$$('#error');
+    assert(errorElement);
     submitElement = securityTokenPin.$$('#submit');
     assert(submitElement);
+    backElement = securityTokenPin.$$('#back');
+    assert(backElement);
+  });
+
+  teardown(() => {
+    securityTokenPin.remove();
+  });
+
+  // Test that the 'completed' event is fired when the user submits the input.
+  test('completion events in basic flow', () => {
+    const FIRST_PIN = '0123';
+    const SECOND_PIN = '987';
+
+    let completedEventDetail = null;
+    securityTokenPin.addEventListener('completed', (event) => {
+      expectNotEquals(event.detail, null);
+      expectEquals(completedEventDetail, null);
+      completedEventDetail = event.detail;
+    });
+    securityTokenPin.addEventListener('cancel', () => {
+      expectNotReached();
+    });
+
+    // The user enters some value. No 'completed' event is triggered so far.
+    pinInput.value = FIRST_PIN;
+    expectEquals(completedEventDetail, null);
+
+    // The user submits the PIN. The 'completed' event has been triggered.
+    submitElement.click();
+    expectEquals(completedEventDetail, FIRST_PIN);
+    completedEventDetail = null;
+
+    // The response arrives, requesting to prompt for the PIN again.
+    securityTokenPin.parameters = {
+      codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
+      enableUserInput: true,
+      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN,
+      attemptsLeft: -1
+    };
+
+    // The user enters some value. No new 'completed' event is triggered so far.
+    pinInput.value = SECOND_PIN;
+    expectEquals(completedEventDetail, null);
+
+    // The user submits the new PIN. The 'completed' event has been triggered.
+    submitElement.click();
+    expectEquals(completedEventDetail, SECOND_PIN);
+  });
+
+  // Test that the 'cancel' event is fired when the user aborts the dialog.
+  test('completion events in cancellation flow', () => {
+    let cancelEventCount = 0;
+    securityTokenPin.addEventListener('cancel', () => {
+      ++cancelEventCount;
+    });
+    securityTokenPin.addEventListener('completed', () => {
+      expectNotReached();
+    });
+
+    // The user clicks the 'back' button. The cancel event is triggered.
+    backElement.click();
+    expectEquals(cancelEventCount, 1);
+  });
+
+  // Test that the submit button is only enabled when the input is non-empty.
+  test('submit button availability', () => {
+    // Initially, the submit button is disabled.
+    expectTrue(submitElement.disabled);
+
+    // The user enters a single digit. The submit button is enabled.
+    pinInput.value = '1';
+    expectFalse(submitElement.disabled);
+
+    // The user clears the input. The submit button is disabled.
+    pinInput.value = '';
+    expectTrue(submitElement.disabled);
+  });
+
+  // Test that the input field is disabled when the final error is displayed and
+  // no further user input is expected.
+  test('input availability', () => {
+    // Initially, the input is enabled.
+    expectFalse(inputField.disabled);
+
+    // The user enters and submits a PIN. The response arrives, requesting the
+    // PIN again. The input is still enabled.
+    pinInput.value = '123';
+    submitElement.click();
+    securityTokenPin.parameters = {
+      codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
+      enableUserInput: true,
+      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN,
+      attemptsLeft: -1
+    };
+    expectFalse(inputField.disabled);
+
+    // The user enters and submits a PIN again. The response arrives, with a
+    // final error. The input is disabled.
+    pinInput.value = '456';
+    submitElement.click();
+    securityTokenPin.parameters = {
+      codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
+      enableUserInput: false,
+      errorLabel:
+          OobeTypes.SecurityTokenPinDialogErrorType.MAX_ATTEMPTS_EXCEEDED,
+      attemptsLeft: 0
+    };
+    expectTrue(inputField.disabled);
+  });
+
+  // Test that the input field gets cleared when the user is prompted again.
+  test('input cleared on new request', () => {
+    const PIN = '123';
+    pinInput.value = PIN;
+    expectEquals(inputField.value, PIN);
+
+    // The user submits the PIN. The response arrives, requesting the PIN again.
+    // The input gets cleared.
+    submitElement.click();
+    securityTokenPin.parameters = {
+      codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
+      enableUserInput: true,
+      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN,
+      attemptsLeft: -1
+    };
+    expectEquals(pinInput.value, '');
+    expectEquals(inputField.value, '');
+  });
+
+  // Test that the input field gets cleared when the request fails with the
+  // final error.
+  test('input cleared on final error', () => {
+    // The user enters and submits a PIN. The response arrives, requesting the
+    // PIN again. The input is cleared.
+    const PIN = '123';
+    pinInput.value = PIN;
+    expectEquals(inputField.value, PIN);
+
+    // The user submits the PIN. The response arrives, reporting a final error
+    // and that the user input isn't requested anymore. The input gets cleared.
+    submitElement.click();
+    securityTokenPin.parameters = {
+      codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
+      enableUserInput: false,
+      errorLabel:
+          OobeTypes.SecurityTokenPinDialogErrorType.MAX_ATTEMPTS_EXCEEDED,
+      attemptsLeft: 0
+    };
+    expectEquals(pinInput.value, '');
+    expectEquals(inputField.value, '');
+  });
+
+  // Test that the PIN can be entered via the on-screen PIN keypad.
+  test('PIN input via keypad', () => {
+    const PIN = '13097';
+
+    let completedEventDetail = null;
+    securityTokenPin.addEventListener('completed', (event) => {
+      completedEventDetail = event.detail;
+    });
+
+    // The user clicks the buttons of the on-screen keypad. The input field is
+    // updated accordingly.
+    for (const character of PIN)
+      pinKeyboard.$$('#digitButton' + character).click();
+    expectEquals(pinInput.value, PIN);
+    expectEquals(inputField.value, PIN);
+
+    // The user submits the PIN. The completed event is fired, containing the
+    // PIN.
+    submitElement.click();
+    expectEquals(completedEventDetail, PIN);
+  });
+
+  // Test that the error is displayed only when it's set in the request.
+  test('error visibility', () => {
+    function getErrorContainerVisibility() {
+      return getComputedStyle(errorContainer).getPropertyValue('visibility');
+    }
+
+    // Initially, no error is shown.
+    expectEquals(getErrorContainerVisibility(), 'hidden');
+    expectFalse(pinInput.hasAttribute('invalid'));
+
+    // The user submits some PIN, and the error response arrives. The error gets
+    // displayed.
+    pinInput.value = '123';
+    submitElement.click();
+    securityTokenPin.parameters = {
+      codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
+      enableUserInput: true,
+      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN,
+      attemptsLeft: -1
+    };
+    expectEquals(getErrorContainerVisibility(), 'visible');
+    expectTrue(pinInput.hasAttribute('invalid'));
+
+    // The user modifies the input field. No error is shown.
+    pinInput.value = '4';
+    expectEquals(getErrorContainerVisibility(), 'hidden');
+    expectFalse(pinInput.hasAttribute('invalid'));
+  });
+
+  // Test the text of the label for the |INVALID_PIN| error.
+  test('label text: invalid PIN', () => {
+    securityTokenPin.parameters = {
+      codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
+      enableUserInput: true,
+      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN,
+      attemptsLeft: -1
+    };
+    expectEquals(
+        errorElement.textContent,
+        loadTimeData.getString('securityTokenPinDialogUnknownInvalidPin'));
+  });
+
+  // Test the text of the label for the |INVALID_PUK| error.
+  test('label text: invalid PUK', () => {
+    securityTokenPin.parameters = {
+      codeType: OobeTypes.SecurityTokenPinDialogType.PUK,
+      enableUserInput: true,
+      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PUK,
+      attemptsLeft: -1
+    };
+    expectEquals(
+        errorElement.textContent,
+        loadTimeData.getString('securityTokenPinDialogUnknownInvalidPuk'));
+  });
+
+  // Test the text of the label for the |MAX_ATTEMPTS_EXCEEDED| error.
+  test('label text: max attempts exceeded', () => {
+    securityTokenPin.parameters = {
+      codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
+      enableUserInput: false,
+      errorLabel:
+          OobeTypes.SecurityTokenPinDialogErrorType.MAX_ATTEMPTS_EXCEEDED,
+      attemptsLeft: 0
+    };
+    expectEquals(
+        errorElement.textContent,
+        loadTimeData.getString(
+            'securityTokenPinDialogUnknownMaxAttemptsExceeded'));
+  });
+
+  // Test the text of the label for the |UNKNOWN| error.
+  test('label text: unknown error', () => {
+    securityTokenPin.parameters = {
+      codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
+      enableUserInput: true,
+      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.UNKNOWN,
+      attemptsLeft: -1
+    };
+    expectEquals(
+        errorElement.textContent,
+        loadTimeData.getString('securityTokenPinDialogUnknownError'));
+  });
+
+  // Test the text of the label when the number of attempts left is given.
+  test('label text: attempts number', () => {
+    const ATTEMPTS_LEFT = 3;
+    securityTokenPin.parameters = {
+      codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
+      enableUserInput: true,
+      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.NONE,
+      attemptsLeft: ATTEMPTS_LEFT
+    };
+    expectEquals(
+        errorElement.textContent,
+        loadTimeData.getStringF(
+            'securityTokenPinDialogAttemptsLeft', ATTEMPTS_LEFT));
+  });
+
+  // Test that the label is empty when the number of attempts is, heuristically,
+  // too big to be displayed for the user.
+  test('label text: hidden attempts number', () => {
+    const BIG_ATTEMPTS_LEFT = 4;
+    securityTokenPin.parameters = {
+      codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
+      enableUserInput: true,
+      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.NONE,
+      attemptsLeft: BIG_ATTEMPTS_LEFT
+    };
+    expectEquals(errorElement.textContent, '');
+  });
+
+  // Test the text of the label for the |INVALID_PIN| error when the number of
+  // attempts left is given.
+  test('label text: invalid PIN with attempts number', () => {
+    const ATTEMPTS_LEFT = 3;
+    securityTokenPin.parameters = {
+      codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
+      enableUserInput: true,
+      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN,
+      attemptsLeft: ATTEMPTS_LEFT
+    };
+    expectEquals(
+        errorElement.textContent,
+        loadTimeData.getStringF(
+            'securityTokenPinDialogErrorAttempts',
+            loadTimeData.getString('securityTokenPinDialogUnknownInvalidPin'),
+            ATTEMPTS_LEFT));
+  });
+
+  // Test the text of the label for the |INVALID_PIN| error when the number of
+  // attempts left is, heuristically, too big to be displayed for the user.
+  test('label text: invalid PIN with hidden attempts number', () => {
+    const BIG_ATTEMPTS_LEFT = 4;
+    securityTokenPin.parameters = {
+      codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
+      enableUserInput: true,
+      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN,
+      attemptsLeft: BIG_ATTEMPTS_LEFT
+    };
+    expectEquals(
+        errorElement.textContent,
+        loadTimeData.getString('securityTokenPinDialogUnknownInvalidPin'));
   });
 
   // Test that no scrolling is necessary in order to see all dots after entering
@@ -69,6 +393,8 @@
         'normal');
   });
 
+  // Test that the focus on the input field isn't lost when the PIN is requested
+  // again after the failed verification.
   test('focus restores after progress animation', () => {
     // The PIN keyboard is displayed initially.
     expectFalse(pinKeyboardContainer.hidden);
@@ -103,6 +429,8 @@
     expectEquals(inputField.getRootNode().activeElement, inputField);
   });
 
+  // Test that the input field gets focused when the PIN is requested again
+  // after the failed verification.
   test('focus set after progress animation', () => {
     // The PIN keyboard is displayed initially.
     expectFalse(pinKeyboardContainer.hidden);
diff --git a/chrome/browser/resources/media/media_feeds.html b/chrome/browser/resources/media/media_feeds.html
index faeee94..448f9be 100644
--- a/chrome/browser/resources/media/media_feeds.html
+++ b/chrome/browser/resources/media/media_feeds.html
@@ -10,6 +10,8 @@
   <script src="chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js"></script>
   <script src="chrome://resources/mojo/mojo/public/mojom/base/time.mojom-lite.js"></script>
   <script src="chrome://resources/mojo/url/mojom/url.mojom-lite.js"></script>
+  <script src="ui/gfx/geometry/mojom/geometry.mojom-lite.js"></script>
+  <script src="services/media_session/public/mojom/media_session.mojom-lite.js"></script>
   <script src="chrome://resources/js/util.js"></script>
 
   <script src="chrome/browser/media/feeds/media_feeds_store.mojom-lite.js">
@@ -86,9 +88,39 @@
         <th sort-key="url">
           Url
         </th>
+        <th sort-key="displayName">
+          Display Name
+        </th>
         <th sort-key="lastDiscoveryTime">
           Last Discovery Time
         </th>
+        <th sort-key="lastFetchTime">
+          Last Fetch Time
+        </th>
+        <th sort-key="userStatus">
+          User Status
+        </th>
+        <th sort-key="lastFetchResult">
+          Last Fetch Result
+        </th>
+        <th sort-key="fetchFailedCount">
+          Fetch Failed Count
+        </th>
+        <th sort-key="cacheExpiryTime">
+          Cache Expiry Time
+        </th>
+        <th sort-key="lastFetchItemCount">
+          Last Fetch Item Count
+        </th>
+        <th sort-key="lastFetchPlayNextCount">
+          Last Fetch Play Next Count
+        </th>
+        <th sort-key="lastFetchContentTypes">
+          Last Fetch Content Types
+        </th>
+        <th>
+          Logos
+        </th>
       </tr>
     </thead>
     <tbody id="feed-table-body">
@@ -99,7 +131,17 @@
     <tr>
       <td class="id-cell"></td>
       <td class="url-cell"></td>
+      <td></td>
       <td class="last-discovery-time-cell"></td>
+      <td></td>
+      <td></td>
+      <td></td>
+      <td></td>
+      <td></td>
+      <td></td>
+      <td></td>
+      <td></td>
+      <td></td>
     </tr>
   </template>
 </body>
diff --git a/chrome/browser/resources/media/media_feeds.js b/chrome/browser/resources/media/media_feeds.js
index e1d8ed23..fe5151e6 100644
--- a/chrome/browser/resources/media/media_feeds.js
+++ b/chrome/browser/resources/media/media_feeds.js
@@ -32,7 +32,68 @@
 
   td[0].textContent = rowInfo.id;
   td[1].textContent = rowInfo.url.url;
-  td[2].textContent = convertMojoTimeToJS(rowInfo.lastDiscoveryTime).toString();
+  td[2].textContent = rowInfo.displayName;
+  td[3].textContent = convertMojoTimeToJS(rowInfo.lastDiscoveryTime).toString();
+
+  if (rowInfo.lastFetchTime != null) {
+    td[4].textContent = convertMojoTimeToJS(rowInfo.lastFetchTime).toString();
+  }
+
+  if (rowInfo.userStatus == mediaFeeds.mojom.FeedUserStatus.kAuto) {
+    td[5].textContent = 'Auto';
+  } else if (rowInfo.userStatus == mediaFeeds.mojom.FeedUserStatus.kDisabled) {
+    td[5].textContent = 'Disabled';
+  }
+
+  if (rowInfo.lastFetchResult == mediaFeeds.mojom.FetchResult.kNone) {
+    td[6].textContent = 'None';
+  } else if (rowInfo.lastFetchResult == mediaFeeds.mojom.FetchResult.kSuccess) {
+    td[6].textContent = 'Success';
+  } else if (
+      rowInfo.lastFetchResult ==
+      mediaFeeds.mojom.FetchResult.kFailedBackendError) {
+    td[6].textContent = 'Failed (Backend Error)';
+  } else if (
+      rowInfo.lastFetchResult ==
+      mediaFeeds.mojom.FetchResult.kFailedNetworkError) {
+    td[6].textContent = 'Failed (Network Error)';
+  }
+
+  td[7].textContent = rowInfo.fetchFailedCount;
+
+  if (rowInfo.cacheExpiryTime != null) {
+    td[8].textContent = convertMojoTimeToJS(rowInfo.cacheExpiryTime).toString();
+  }
+
+  td[9].textContent = rowInfo.lastFetchItemCount;
+  td[10].textContent = rowInfo.lastFetchPlayNextCount;
+
+  const contentTypes = [];
+  if (rowInfo.lastFetchContentTypes &
+      mediaFeeds.mojom.MediaFeedItemType.kVideo) {
+    contentTypes.push('Video');
+  } else if (
+      rowInfo.lastFetchContentTypes &
+      mediaFeeds.mojom.MediaFeedItemType.kTVSeries) {
+    contentTypes.push('TV Series');
+  } else if (
+      rowInfo.lastFetchContentTypes &
+      mediaFeeds.mojom.MediaFeedItemType.kMovie) {
+    contentTypes.push('Movie');
+  }
+
+  td[11].textContent =
+      contentTypes.length === 0 ? 'None' : contentTypes.join(',');
+
+  // Format an array of mojo media images.
+  rowInfo.logos.forEach((image) => {
+    const a = document.createElement('a');
+    a.href = image.src.url;
+    a.textContent = image.src.url;
+    a.target = '_blank';
+    td[12].appendChild(a);
+    td[12].appendChild(document.createElement('br'));
+  });
 
   return document.importNode(template.content, true);
 }
@@ -89,6 +150,22 @@
   } else if (sortKey == 'lastDiscoveryTime') {
     return (
         a.lastDiscoveryTime.internalValue > b.lastDiscoveryTime.internalValue);
+  } else if (sortKey == 'displayName') {
+    return a.displayName > b.displayName;
+  } else if (sortKey == 'userStatus') {
+    return a.userStatus > b.userStatus;
+  } else if (sortKey == 'lastFetchResult') {
+    return a.lastFetchResult > b.lastFetchResult;
+  } else if (sortKey == 'fetchFailedCount') {
+    return a.fetchFailedCount > b.fetchFailedCount;
+  } else if (sortKey == 'cacheExpiryTime') {
+    return a.cacheExpiryTime > b.cacheExpiryTime;
+  } else if (sortKey == 'lastFetchItemCount') {
+    return a.lastFetchItemCount > b.lastFetchItemCount;
+  } else if (sortKey == 'lastFetchPlayNextCount') {
+    return a.lastFetchPlayNextCount > b.lastFetchPlayNextCount;
+  } else if (sortKey == 'lastFetchContentTypes') {
+    return a.lastFetchContentTypes > b.lastFetchContentTypes;
   }
 
   assertNotReached('Unsupported sort key: ' + sortKey);
diff --git a/chrome/browser/resources/settings/autofill_page/password_check.html b/chrome/browser/resources/settings/autofill_page/password_check.html
index d78a1a39..97c649f 100644
--- a/chrome/browser/resources/settings/autofill_page/password_check.html
+++ b/chrome/browser/resources/settings/autofill_page/password_check.html
@@ -62,7 +62,7 @@
       <picture>
         <source srcset="[[bannerImageSrc_(1, status_)]]"
                 media="(prefers-color-scheme: dark)">
-        <img id="bannerImage" src="[[bannerImageSrc_(0, status_)]]">
+        <img id="bannerImage" src="[[bannerImageSrc_(0, status_)]]" alt="">
       </picture>
     </template>
 
diff --git a/chrome/browser/resources/settings/autofill_page/password_manager_proxy.js b/chrome/browser/resources/settings/autofill_page/password_manager_proxy.js
index a0768c3b9..f80724d3 100644
--- a/chrome/browser/resources/settings/autofill_page/password_manager_proxy.js
+++ b/chrome/browser/resources/settings/autofill_page/password_manager_proxy.js
@@ -152,6 +152,12 @@
   isOptedInForAccountStorage() {}
 
   /**
+   * Triggers the opt-in or opt-out flow for the account storage.
+   * @param {boolean} optIn Whether the user wants to opt in or opt out.
+   */
+  optInForAccountStorage(optIn) {}
+
+  /**
    * Requests the start of the bulk password check.
    */
   startBulkPasswordCheck() {}
@@ -383,6 +389,11 @@
   }
 
   /** @override */
+  optInForAccountStorage(optIn) {
+    chrome.passwordsPrivate.optInForAccountStorage(optIn);
+  }
+
+  /** @override */
   startBulkPasswordCheck() {
     chrome.passwordsPrivate.startPasswordCheck();
   }
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.html b/chrome/browser/resources/settings/autofill_page/passwords_section.html
index 39515e3..59158c3b 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.html
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.html
@@ -158,6 +158,16 @@
       <!-- This span lays out the url correctly, relative to the label. -->
       <span>$i18nRaw{managePasswordsLabel}</span>
     </div>
+    <template is="dom-if" if="[[enableAccountStorage_]]">
+      <cr-button on-click="onOptIn_"
+          hidden$="[[isOptedInForAccountStorage_]]">
+          $i18n{optInAccountStorageLabel}
+      </cr-button>
+      <cr-button on-click="onOptOut_"
+          hidden$="[[!isOptedInForAccountStorage_]]">
+          $i18n{optOutAccountStorageLabel}
+      </cr-button>
+    </template>
     <div class="settings-box first">
       <h2 id="savedPasswordsHeading" class="start">
         $i18n{savedPasswordsHeading}
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.js b/chrome/browser/resources/settings/autofill_page/passwords_section.js
index 7048941..3243261 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.js
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.js
@@ -153,6 +153,14 @@
     },
 
     /** @private */
+    enableAccountStorage_: {
+      type: Boolean,
+      value() {
+        return loadTimeData.getBoolean('enableAccountStorage');
+      }
+    },
+
+    /** @private */
     showPasswordEditDialog_: Boolean,
 
     /** @private */
@@ -594,6 +602,16 @@
     cr.ui.focusWithoutInk(assert(this.activeDialogAnchorStack_.pop()));
   },
 
+  /** @private */
+  onOptIn_: function() {
+    this.passwordManager_.optInForAccountStorage(true);
+  },
+
+  /** @private */
+  onOptOut_: function() {
+    this.passwordManager_.optInForAccountStorage(false);
+  },
+
   /**
    * Returns true if the list exists and has items.
    * @param {Array<Object>} list
diff --git a/chrome/browser/resources/settings/autofill_page/show_password_behavior.js b/chrome/browser/resources/settings/autofill_page/show_password_behavior.js
index 9992571..df9a5ab 100644
--- a/chrome/browser/resources/settings/autofill_page/show_password_behavior.js
+++ b/chrome/browser/resources/settings/autofill_page/show_password_behavior.js
@@ -60,16 +60,18 @@
 
   /**
    * Gets the text of the password. Will use the value of |password| unless it
-   * cannot be shown, in which case it will be spaces. It can also be the
-   * federated text.
+   * cannot be shown, in which case it will be a fixed number of spaces. It can
+   * also be the federated text.
    * @private
    */
   getPassword_() {
     if (!this.item) {
       return '';
     }
+
+    const NUM_PLACEHOLDERS = 10;
     return this.item.entry.federationText || this.item.password ||
-        ' '.repeat(this.item.entry.numCharactersInPassword);
+        ' '.repeat(NUM_PLACEHOLDERS);
   },
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index ec97c11..70f3bf05 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -86,6 +86,7 @@
     ":os_route",
     ":os_settings_routes",
     ":route_origin_behavior",
+    "ambient_mode_page:closure_compile",
     "bluetooth_page:closure_compile",
     "crostini_page:closure_compile",
     "date_time_page:closure_compile",
diff --git a/chrome/browser/resources/settings/chromeos/ambient_mode_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/ambient_mode_page/BUILD.gn
new file mode 100644
index 0000000..583c5bd
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/ambient_mode_page/BUILD.gn
@@ -0,0 +1,27 @@
+# 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("//third_party/closure_compiler/compile_js.gni")
+
+js_type_check("closure_compile") {
+  deps = [
+    ":ambient_mode_browser_proxy",
+    ":ambient_mode_page",
+  ]
+}
+
+js_library("ambient_mode_browser_proxy") {
+  deps = [ "//ui/webui/resources/js:cr" ]
+  externs_list = [ "$externs_path/chrome_send.js" ]
+}
+
+js_library("ambient_mode_page") {
+  deps = [
+    ":ambient_mode_browser_proxy",
+    "../../prefs:prefs_behavior",
+    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:i18n_behavior",
+    "//ui/webui/resources/js:load_time_data",
+  ]
+}
diff --git a/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_browser_proxy.html b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_browser_proxy.html
new file mode 100644
index 0000000..07419df
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_browser_proxy.html
@@ -0,0 +1,2 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+<script src="ambient_mode_browser_proxy.js"></script>
diff --git a/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_browser_proxy.js b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_browser_proxy.js
new file mode 100644
index 0000000..5bd188a
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_browser_proxy.js
@@ -0,0 +1,32 @@
+// 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.
+
+/**
+ * @fileoverview A helper object used from the ambient mode section to interact
+ * with the browser.
+ */
+
+cr.define('settings', function() {
+  /** @interface */
+  class AmbientModeBrowserProxy {
+    onAmbientModePageReady() {}
+  }
+
+  /** @implements {settings.AmbientModeBrowserProxy} */
+  class AmbientModeBrowserProxyImpl {
+    /** @override */
+    onAmbientModePageReady() {
+      chrome.send('onAmbientModePageReady');
+    }
+  }
+
+  // The singleton instance_ is replaced with a test version of this wrapper
+  // during testing.
+  cr.addSingletonGetter(AmbientModeBrowserProxyImpl);
+
+  return {
+    AmbientModeBrowserProxy: AmbientModeBrowserProxy,
+    AmbientModeBrowserProxyImpl: AmbientModeBrowserProxyImpl,
+  };
+});
diff --git a/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_page.html b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_page.html
new file mode 100644
index 0000000..8284aeb
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_page.html
@@ -0,0 +1,50 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="ambient_mode_browser_proxy.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_radio_button/cr_radio_button.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="../../controls/settings_radio_group.html">
+<link rel="import" href="../../controls/settings_toggle_button.html">
+<link rel="import" href="../../settings_shared_css.html">
+
+<dom-module id="settings-ambient-mode-page">
+  <template>
+    <style include="settings-shared">
+      #ambientModeEnable {
+        border-bottom: var(--cr-separator-line);
+      }
+
+      #topicSourceDescription {
+        display: block;
+        padding-inline-end: var(--cr-section-padding);
+        padding-inline-start: var(--cr-section-padding);
+      }
+    </style>
+    <settings-toggle-button id="ambientModeEnable"
+        class="first primary-toggle"
+        pref="{{prefs.settings.ambient_mode.enabled}}"
+        label="[[getAmbientModeOnOffLabel_(
+            prefs.settings.ambient_mode.enabled.value)]]">
+    </settings-toggle-button>
+
+    <template is="dom-if" if="[[prefs.settings.ambient_mode.enabled.value]]">
+      <label id="topicSourceDescription" class="settings-box-text">
+          $i18n{ambientModeTopicSourceTitle}
+      </label>
+      <settings-radio-group id="topicSourceRadioGroup"
+          class="list-frame"
+          aria-labelledby="topicSourceDescription"
+          pref="{{prefs.settings.ambient_mode.topic_source}}">
+        <cr-radio-button name="[[topicSource_.GOOGLE_PHOTOS]]"
+            class="list-item underbar"
+            label="$i18n{ambientModeTopicSourceGooglePhotos}">
+        </cr-radio-button>
+        <cr-radio-button name="[[topicSource_.ART_GALLERY]]"
+            class="list-item underbar"
+            label="$i18n{ambientModeTopicSourceArtGallery}">
+        </cr-radio-button>
+      </settings-radio-group>
+    </template>
+  </template>
+  <script src="ambient_mode_page.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_page.js b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_page.js
new file mode 100644
index 0000000..109219d
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_page.js
@@ -0,0 +1,62 @@
+// 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.
+
+/**
+ * @fileoverview 'settings-ambient-mode-page' is the settings page containing
+ * ambient mode settings.
+ */
+Polymer({
+  is: 'settings-ambient-mode-page',
+
+  behaviors: [I18nBehavior, PrefsBehavior],
+
+  properties: {
+    prefs: Object,
+
+    /**
+     * Enum values for the 'settings.ambient_mode.topic_source' preference.
+     * Values need to stay in sync with the |ash::ambient::prefs::TopicSource|.
+     * @private {!Object<string, number>}
+     */
+    topicSource_: {
+      type: Object,
+      value: {
+        GOOGLE_PHOTOS: 0,
+        ART_GALLERY: 1,
+      },
+      readOnly: true,
+    },
+
+    /** @private */
+    isAmbientModeEnabled_: {
+      type: Boolean,
+      value() {
+        return loadTimeData.getBoolean('isAmbientModeEnabled');
+      },
+      readOnly: true,
+    },
+  },
+
+  /** @private {?settings.AmbientModeBrowserProxy} */
+  browserProxy_: null,
+
+  /** @override */
+  created() {
+    this.browserProxy_ = settings.AmbientModeBrowserProxyImpl.getInstance();
+  },
+
+  /** @override */
+  ready() {
+    this.browserProxy_.onAmbientModePageReady();
+  },
+
+  /**
+   * @param {boolean} toggleValue
+   * @return {string}
+   * @private
+   */
+  getAmbientModeOnOffLabel_(toggleValue) {
+    return this.i18n(toggleValue ? 'ambientModeOn' : 'ambientModeOff');
+  },
+});
diff --git a/chrome/browser/resources/settings/chromeos/os_route.js b/chrome/browser/resources/settings/chromeos/os_route.js
index e34f84c..b8ea4ae 100644
--- a/chrome/browser/resources/settings/chromeos/os_route.js
+++ b/chrome/browser/resources/settings/chromeos/os_route.js
@@ -117,6 +117,7 @@
       r.PERSONALIZATION =
           r.BASIC.createSection('/personalization', 'personalization');
       r.CHANGE_PICTURE = r.PERSONALIZATION.createChild('/changePicture');
+      r.AMBIENT_MODE = r.PERSONALIZATION.createChild('/ambientMode');
 
       // Files (analogous to Downloads)
       r.FILES = r.ADVANCED.createSection('/files', 'files');
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
index 3a6b07d..e8a8866 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
@@ -159,7 +159,8 @@
         <template is="dom-if" if="[[!isGuestMode_]]" restamp>
           <settings-section page-title="$i18n{personalizationPageTitle}"
               section="personalization">
-            <settings-personalization-page></settings-personalization-page>
+            <settings-personalization-page prefs="{{prefs}}">
+            </settings-personalization-page>
           </settings-section>
         </template>
         <settings-section page-title="$i18n{osSearchPageTitle}"
diff --git a/chrome/browser/resources/settings/chromeos/personalization_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/personalization_page/BUILD.gn
index 3850c2c..5b6f366 100644
--- a/chrome/browser/resources/settings/chromeos/personalization_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/personalization_page/BUILD.gn
@@ -38,7 +38,9 @@
     ":wallpaper_browser_proxy",
     "..:os_route",
     "../..:router",
+    "../../prefs",
     "../../settings_page:settings_animated_pages",
+    "../ambient_mode_page:ambient_mode_browser_proxy",
     "//ui/webui/resources/js:cr",
   ]
 }
diff --git a/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.html b/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.html
index 9ab4ac8..9b4d1dc3 100644
--- a/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.html
+++ b/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.html
@@ -1,6 +1,8 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="../ambient_mode_page/ambient_mode_page.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="change_picture.html">
 <link rel="import" href="../os_route.html">
 <link rel="import" href="../../router.html">
@@ -31,6 +33,19 @@
             </cr-policy-indicator>
           </template>
         </cr-link-row>
+
+        <!-- Ambient mode -->
+        <template is="dom-if" if="[[isAmbientModeEnabled_]]">
+          <cr-link-row
+              class="hr"
+              id="ambientModeRow"
+              label="$i18n{ambientModeTitle}"
+              sub-label="[[getAmbientModeRowSubLabel_(
+                  prefs.settings.ambient_mode.enabled.value)]]"
+              on-click="navigateToAmbientMode_"
+              role-description="$i18n{subpageArrowRoleDescription}">
+          </cr-link-row>
+        </template>
       </div>
 
       <template is="dom-if" route-path="/changePicture">
@@ -40,6 +55,14 @@
           <settings-change-picture></settings-change-picture>
         </settings-subpage>
       </template>
+      <template is="dom-if" route-path="/ambientMode">
+        <settings-subpage
+            associated-control="[[$$('#ambientModeRow')]]"
+            page-title="$i18n{ambientModeTitle}">
+          <settings-ambient-mode-page prefs="{{prefs}}">
+          </settings-ambient-mode-page>
+        </settings-subpage>
+      </template>
     </settings-animated-pages>
   </template>
   <script src="personalization_page.js"></script>
diff --git a/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.js b/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.js
index 3058475..afd03cf 100644
--- a/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.js
+++ b/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.js
@@ -12,13 +12,29 @@
 Polymer({
   is: 'settings-personalization-page',
 
+  behaviors: [I18nBehavior],
+
   properties: {
+    /**
+     * Preferences state.
+     */
+    prefs: Object,
+
     /** @private */
     showWallpaperRow_: {type: Boolean, value: true},
 
     /** @private */
     isWallpaperPolicyControlled_: {type: Boolean, value: true},
 
+    /** @private */
+    isAmbientModeEnabled_: {
+      type: Boolean,
+      value() {
+        return loadTimeData.getBoolean('isAmbientModeEnabled');
+      },
+      readOnly: true,
+    },
+
     /** @private {!Map<string, string>} */
     focusConfig_: {
       type: Object,
@@ -26,7 +42,10 @@
         const map = new Map();
         if (settings.routes.CHANGE_PICTURE) {
           map.set(settings.routes.CHANGE_PICTURE.path, '#changePictureRow');
+        } else if (settings.routes.AMBIENT_MODE) {
+          map.set(settings.routes.AMBIENT_MODE.path, '#ambientModeRow');
         }
+
         return map;
       }
     },
@@ -63,5 +82,20 @@
   navigateToChangePicture_() {
     settings.Router.getInstance().navigateTo(settings.routes.CHANGE_PICTURE);
   },
+
+  /** @private */
+  navigateToAmbientMode_() {
+    settings.Router.getInstance().navigateTo(settings.routes.AMBIENT_MODE);
+  },
+
+  /**
+   * @param {boolean} toggleValue
+   * @return {string}
+   * @private
+   */
+  getAmbientModeRowSubLabel_(toggleValue) {
+    return this.i18n(
+        toggleValue ? 'ambientModeEnabled' : 'ambientModeDisabled');
+  },
 });
 })();
diff --git a/chrome/browser/resources/settings/os_settings_resources.grd b/chrome/browser/resources/settings/os_settings_resources.grd
index c6786ba..819fad9 100644
--- a/chrome/browser/resources/settings/os_settings_resources.grd
+++ b/chrome/browser/resources/settings/os_settings_resources.grd
@@ -61,6 +61,18 @@
       <structure name="IDR_OS_SETTINGS_A11Y_PAGE_HTML"
                  file="chromeos/os_a11y_page/os_a11y_page.html"
                  type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_AMBIENT_MODE_PAGE_AMBIENT_MODE_PAGE_JS"
+                 file="chromeos/ambient_mode_page/ambient_mode_page.js"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_AMBIENT_MODE_PAGE_AMBIENT_MODE_PAGE_HTML"
+                 file="chromeos/ambient_mode_page/ambient_mode_page.html"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_AMBIENT_MODE_PAGE_AMBIENT_MODE_BROWSER_PROXY_JS"
+                 file="chromeos/ambient_mode_page/ambient_mode_browser_proxy.js"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_AMBIENT_MODE_PAGE_AMBIENT_MODE_BROWSER_PROXY_HTML"
+                 file="chromeos/ambient_mode_page/ambient_mode_browser_proxy.html"
+                 type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_APPS_PAGE_JS"
                  file="chromeos/os_apps_page/os_apps_page.js"
                  type="chrome_html" />
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_page.html b/chrome/browser/resources/settings/safety_check_page/safety_check_page.html
index c15b37e..1b2aaa20 100644
--- a/chrome/browser/resources/settings/safety_check_page/safety_check_page.html
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_page.html
@@ -96,7 +96,7 @@
             if="[[shouldShowUpdatesButton_(updatesStatus_)]]" restamp>
           <div class="separator"></div>
           <cr-button id="safetyCheckUpdatesButton" class="action-button"
-              on-click="onSafetyCheckUpdatesButtonClicked_" no-search
+              on-click="onSafetyCheckUpdatesButtonClick_" no-search
             aria-label="$i18n{safetyCheckUpdatesButtonAriaLabel}">
             $i18n{aboutRelaunch}
           </cr-button>
@@ -176,7 +176,7 @@
           <div class="separator"></div>
           <cr-button id="safetyCheckExtensionsButton"
               class$="[[getExtensionsButtonClass_(extensionsStatus_)]]"
-              on-click="onSafetyCheckExtensionsButtonClicked_" no-search
+              on-click="onSafetyCheckExtensionsButtonClick_" no-search
               aria-label="$i18n{safetyCheckExtensionsButtonAriaLabel}">
             $i18n{safetyCheckExtensionsButton}
           </cr-button>
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_page.js b/chrome/browser/resources/settings/safety_check_page/safety_check_page.js
index 30b4101..9c7d6fd 100644
--- a/chrome/browser/resources/settings/safety_check_page/safety_check_page.js
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_page.js
@@ -54,6 +54,17 @@
 };
 
 /**
+ * UI states a safety check child can be in. Defines the basic UI of the child.
+ * @enum {number}
+ */
+const ChildUiStatus = {
+  RUNNING: 0,
+  SAFE: 1,
+  INFO: 2,
+  WARNING: 3,
+};
+
+/**
  * @typedef {{
  *   newState: settings.SafetyCheckUpdatesStatus,
  *   displayString: string,
@@ -351,6 +362,60 @@
   },
 
   /**
+   * Returns the default icon for a safety check child in the specified state.
+   * @private
+   * @param {ChildUiStatus} childUiStatus
+   * @return {?string}
+   */
+  getChildUiIcon_: function(childUiStatus) {
+    switch (childUiStatus) {
+      case ChildUiStatus.RUNNING:
+        return null;
+      case ChildUiStatus.SAFE:
+        return 'cr:check';
+      case ChildUiStatus.INFO:
+        return 'cr:info';
+      case ChildUiStatus.WARNING:
+        return 'cr:warning';
+      default:
+        assertNotReached();
+    }
+  },
+
+  /**
+   * Returns the default icon src for animated icons for a safety check child
+   * in the specified state.
+   * @private
+   * @param {ChildUiStatus} childUiStatus
+   * @return {?string}
+   */
+  getChildUiIconSrc_: function(childUiStatus) {
+    if (childUiStatus == ChildUiStatus.RUNNING) {
+      return 'chrome://resources/images/throbber_small.svg';
+    }
+    return null;
+  },
+
+  /**
+   * Returns the default icon class for a safety check child in the specified
+   * state.
+   * @private
+   * @param {ChildUiStatus} childUiStatus
+   * @return {string}
+   */
+  getChildUiIconClass_: function(childUiStatus) {
+    switch (childUiStatus) {
+      case ChildUiStatus.RUNNING:
+      case ChildUiStatus.SAFE:
+        return 'icon-blue';
+      case ChildUiStatus.WARNING:
+        return 'icon-red';
+      default:
+        return '';
+    }
+  },
+
+  /**
    * @private
    * @return {boolean}
    */
@@ -368,27 +433,27 @@
   },
 
   /** @private */
-  onSafetyCheckUpdatesButtonClicked_: function() {
+  onSafetyCheckUpdatesButtonClick_: function() {
     this.lifetimeBrowserProxy_.relaunch();
   },
 
   /**
+   * @return {ChildUiStatus}
    * @private
-   * @return {?string}
    */
-  getUpdatesIcon_: function() {
+  getUpdatesUiStatus_: function() {
     switch (this.updatesStatus_) {
       case settings.SafetyCheckUpdatesStatus.CHECKING:
       case settings.SafetyCheckUpdatesStatus.UPDATING:
-        return null;
+        return ChildUiStatus.RUNNING;
       case settings.SafetyCheckUpdatesStatus.UPDATED:
-        return 'cr:check';
+        return ChildUiStatus.SAFE;
       case settings.SafetyCheckUpdatesStatus.RELAUNCH:
       case settings.SafetyCheckUpdatesStatus.DISABLED_BY_ADMIN:
       case settings.SafetyCheckUpdatesStatus.FAILED_OFFLINE:
-        return 'cr:info';
+        return ChildUiStatus.INFO;
       case settings.SafetyCheckUpdatesStatus.FAILED:
-        return 'cr:warning';
+        return ChildUiStatus.WARNING;
       default:
         assertNotReached();
     }
@@ -398,14 +463,16 @@
    * @private
    * @return {?string}
    */
+  getUpdatesIcon_: function() {
+    return this.getChildUiIcon_(this.getUpdatesUiStatus_());
+  },
+
+  /**
+   * @private
+   * @return {?string}
+   */
   getUpdatesIconSrc_: function() {
-    switch (this.updatesStatus_) {
-      case settings.SafetyCheckUpdatesStatus.CHECKING:
-      case settings.SafetyCheckUpdatesStatus.UPDATING:
-        return 'chrome://resources/images/throbber_small.svg';
-      default:
-        return null;
-    }
+    return this.getChildUiIconSrc_(this.getUpdatesUiStatus_());
   },
 
   /**
@@ -413,16 +480,7 @@
    * @return {string}
    */
   getUpdatesIconClass_: function() {
-    switch (this.updatesStatus_) {
-      case settings.SafetyCheckUpdatesStatus.CHECKING:
-      case settings.SafetyCheckUpdatesStatus.UPDATED:
-      case settings.SafetyCheckUpdatesStatus.UPDATING:
-        return 'icon-blue';
-      case settings.SafetyCheckUpdatesStatus.FAILED:
-        return 'icon-red';
-      default:
-        return '';
-    }
+    return this.getChildUiIconClass_(this.getUpdatesUiStatus_());
   },
 
   /**
@@ -436,22 +494,22 @@
 
   /**
    * @private
-   * @return {?string}
+   * @return {ChildUiStatus}
    */
-  getPasswordsIcon_: function() {
+  getPasswordsUiStatus_: function() {
     switch (this.passwordsStatus_) {
       case settings.SafetyCheckPasswordsStatus.CHECKING:
-        return null;
+        return ChildUiStatus.RUNNING;
       case settings.SafetyCheckPasswordsStatus.SAFE:
-        return 'cr:check';
+        return ChildUiStatus.SAFE;
       case settings.SafetyCheckPasswordsStatus.COMPROMISED:
-        return 'cr:warning';
+        return ChildUiStatus.WARNING;
       case settings.SafetyCheckPasswordsStatus.OFFLINE:
       case settings.SafetyCheckPasswordsStatus.NO_PASSWORDS:
       case settings.SafetyCheckPasswordsStatus.SIGNED_OUT:
       case settings.SafetyCheckPasswordsStatus.QUOTA_LIMIT:
       case settings.SafetyCheckPasswordsStatus.ERROR:
-        return 'cr:info';
+        return ChildUiStatus.INFO;
       default:
         assertNotReached();
     }
@@ -461,13 +519,16 @@
    * @private
    * @return {?string}
    */
+  getPasswordsIcon_: function() {
+    return this.getChildUiIcon_(this.getPasswordsUiStatus_());
+  },
+
+  /**
+   * @private
+   * @return {?string}
+   */
   getPasswordsIconSrc_: function() {
-    switch (this.passwordsStatus_) {
-      case settings.SafetyCheckPasswordsStatus.CHECKING:
-        return 'chrome://resources/images/throbber_small.svg';
-      default:
-        return null;
-    }
+    return this.getChildUiIconSrc_(this.getPasswordsUiStatus_());
   },
 
   /**
@@ -475,15 +536,7 @@
    * @return {string}
    */
   getPasswordsIconClass_: function() {
-    switch (this.passwordsStatus_) {
-      case settings.SafetyCheckPasswordsStatus.CHECKING:
-      case settings.SafetyCheckPasswordsStatus.SAFE:
-        return 'icon-blue';
-      case settings.SafetyCheckPasswordsStatus.COMPROMISED:
-        return 'icon-red';
-      default:
-        return '';
-    }
+    return this.getChildUiIconClass_(this.getPasswordsUiStatus_());
   },
 
   /** @private */
@@ -528,18 +581,18 @@
 
   /**
    * @private
-   * @return {?string}
+   * @return {ChildUiStatus}
    */
-  getSafeBrowsingIcon_: function() {
+  getSafeBrowsingUiStatus_: function() {
     switch (this.safeBrowsingStatus_) {
       case settings.SafetyCheckSafeBrowsingStatus.CHECKING:
-        return null;
+        return ChildUiStatus.RUNNING;
       case settings.SafetyCheckSafeBrowsingStatus.ENABLED:
-        return 'cr:check';
+        return ChildUiStatus.SAFE;
       case settings.SafetyCheckSafeBrowsingStatus.DISABLED:
       case settings.SafetyCheckSafeBrowsingStatus.DISABLED_BY_ADMIN:
       case settings.SafetyCheckSafeBrowsingStatus.DISABLED_BY_EXTENSION:
-        return 'cr:info';
+        return ChildUiStatus.INFO;
       default:
         assertNotReached();
     }
@@ -549,13 +602,16 @@
    * @private
    * @return {?string}
    */
+  getSafeBrowsingIcon_: function() {
+    return this.getChildUiIcon_(this.getSafeBrowsingUiStatus_());
+  },
+
+  /**
+   * @private
+   * @return {?string}
+   */
   getSafeBrowsingIconSrc_: function() {
-    switch (this.safeBrowsingStatus_) {
-      case settings.SafetyCheckSafeBrowsingStatus.CHECKING:
-        return 'chrome://resources/images/throbber_small.svg';
-      default:
-        return null;
-    }
+    return this.getChildUiIconSrc_(this.getSafeBrowsingUiStatus_());
   },
 
   /**
@@ -563,13 +619,7 @@
    * @return {string}
    */
   getSafeBrowsingIconClass_: function() {
-    switch (this.safeBrowsingStatus_) {
-      case settings.SafetyCheckSafeBrowsingStatus.CHECKING:
-      case settings.SafetyCheckSafeBrowsingStatus.ENABLED:
-        return 'icon-blue';
-      default:
-        return '';
-    }
+    return this.getChildUiIconClass_(this.getSafeBrowsingUiStatus_());
   },
 
   /** @private */
@@ -604,30 +654,30 @@
   },
 
   /** @private */
-  onSafetyCheckExtensionsButtonClicked_: function() {
+  onSafetyCheckExtensionsButtonClick_: function() {
     settings.OpenWindowProxyImpl.getInstance().openURL('chrome://extensions');
   },
 
   /**
    * @private
-   * @return {?string}
+   * @return {ChildUiStatus}
    */
-  getExtensionsIcon_: function() {
+  getExtensionsUiStatus_: function() {
     switch (this.extensionsStatus_) {
       case settings.SafetyCheckExtensionsStatus.CHECKING:
-        return null;
+        return ChildUiStatus.RUNNING;
       case settings.SafetyCheckExtensionsStatus.ERROR:
       case settings.SafetyCheckExtensionsStatus
           .BLOCKLISTED_REENABLED_ALL_BY_ADMIN:
-        return 'cr:info';
+        return ChildUiStatus.INFO;
       case settings.SafetyCheckExtensionsStatus.NO_BLOCKLISTED_EXTENSIONS:
       case settings.SafetyCheckExtensionsStatus.BLOCKLISTED_ALL_DISABLED:
-        return 'cr:check';
+        return ChildUiStatus.SAFE;
       case settings.SafetyCheckExtensionsStatus
           .BLOCKLISTED_REENABLED_ALL_BY_USER:
       case settings.SafetyCheckExtensionsStatus
           .BLOCKLISTED_REENABLED_SOME_BY_USER:
-        return 'cr:warning';
+        return ChildUiStatus.WARNING;
       default:
         assertNotReached();
     }
@@ -637,13 +687,16 @@
    * @private
    * @return {?string}
    */
+  getExtensionsIcon_: function() {
+    return this.getChildUiIcon_(this.getExtensionsUiStatus_());
+  },
+
+  /**
+   * @private
+   * @return {?string}
+   */
   getExtensionsIconSrc_: function() {
-    switch (this.extensionsStatus_) {
-      case settings.SafetyCheckExtensionsStatus.CHECKING:
-        return 'chrome://resources/images/throbber_small.svg';
-      default:
-        return null;
-    }
+    return this.getChildUiIconSrc_(this.getExtensionsUiStatus_());
   },
 
   /**
@@ -651,19 +704,7 @@
    * @return {string}
    */
   getExtensionsIconClass_: function() {
-    switch (this.extensionsStatus_) {
-      case settings.SafetyCheckExtensionsStatus.CHECKING:
-      case settings.SafetyCheckExtensionsStatus.NO_BLOCKLISTED_EXTENSIONS:
-      case settings.SafetyCheckExtensionsStatus.BLOCKLISTED_ALL_DISABLED:
-        return 'icon-blue';
-      case settings.SafetyCheckExtensionsStatus
-          .BLOCKLISTED_REENABLED_ALL_BY_USER:
-      case settings.SafetyCheckExtensionsStatus
-          .BLOCKLISTED_REENABLED_SOME_BY_USER:
-        return 'icon-red';
-      default:
-        return '';
-    }
+    return this.getChildUiIconClass_(this.getExtensionsUiStatus_());
   },
 
   /**
diff --git a/chrome/browser/resources/settings/settings_routes.js b/chrome/browser/resources/settings/settings_routes.js
index 9c80732..743240c 100644
--- a/chrome/browser/resources/settings/settings_routes.js
+++ b/chrome/browser/resources/settings/settings_routes.js
@@ -12,6 +12,7 @@
  *   ACCESSIBILITY: !settings.Route,
  *   ADVANCED: !settings.Route,
  *   ADDRESSES: !settings.Route,
+ *   AMBIENT_MODE: !settings.Route,
  *   APPEARANCE: !settings.Route,
  *   AUTOFILL: !settings.Route,
  *   BASIC: !settings.Route,
diff --git a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
index 778a71b3..3a74c14 100644
--- a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
@@ -81,24 +81,22 @@
 
 GURL GetTrustedVaultRetrievalURL(
     const net::test_server::EmbeddedTestServer& test_server,
-    const std::string& encryption_key) {
+    const std::vector<uint8_t>& encryption_key) {
   const char kGaiaId[] = "gaia_id_for_user_gmail.com";
+  // encryption_keys_retrieval.html would populate encryption key to sync
+  // service upon loading. Key is provided as part of URL and needs to be
+  // encoded with Base64, because |encryption_key| is binary.
+  const std::string base64_encoded_key = base::Base64Encode(encryption_key);
   return test_server.GetURL(
       base::StringPrintf("/sync/encryption_keys_retrieval.html?%s#%s", kGaiaId,
-                         encryption_key.c_str()));
+                         base64_encoded_key.c_str()));
 }
 
-KeyParams KeystoreKeyParams(const std::vector<uint8_t>& key) {
+KeyParams Pbkdf2KeyParams(const std::vector<uint8_t>& key) {
   return {syncer::KeyDerivationParams::CreateForPbkdf2(),
           base::Base64Encode(key)};
 }
 
-KeyParams TrustedVaultKeyParams(const std::string& key) {
-  std::string encoded_key;
-  base::Base64Encode(key, &encoded_key);
-  return {syncer::KeyDerivationParams::CreateForPbkdf2(), encoded_key};
-}
-
 std::string ComputeKeyName(const KeyParams& key_params) {
   std::string key_name;
   syncer::Nigori::CreateByDerivation(key_params.derivation_params,
@@ -152,7 +150,7 @@
 }
 
 sync_pb::NigoriSpecifics BuildTrustedVaultNigoriSpecifics(
-    const std::vector<std::string>& trusted_vault_keys) {
+    const std::vector<std::vector<uint8_t>>& trusted_vault_keys) {
   sync_pb::NigoriSpecifics specifics;
   specifics.set_passphrase_type(
       sync_pb::NigoriSpecifics::TRUSTED_VAULT_PASSPHRASE);
@@ -160,12 +158,10 @@
 
   std::unique_ptr<syncer::CryptographerImpl> cryptographer =
       syncer::CryptographerImpl::CreateEmpty();
-  for (const std::string& trusted_vault_key : trusted_vault_keys) {
-    std::string encoded_key;
-    base::Base64Encode(trusted_vault_key, &encoded_key);
-
+  for (const std::vector<uint8_t>& trusted_vault_key : trusted_vault_keys) {
     const std::string key_name = cryptographer->EmplaceKey(
-        encoded_key, syncer::KeyDerivationParams::CreateForPbkdf2());
+        base::Base64Encode(trusted_vault_key),
+        syncer::KeyDerivationParams::CreateForPbkdf2());
     cryptographer->SelectDefaultEncryptionKey(key_name);
   }
 
@@ -302,7 +298,7 @@
       GetFakeServer()->GetKeystoreKeys();
   ASSERT_TRUE(keystore_keys.size() == 1);
   EXPECT_THAT(specifics.encryption_keybag(),
-              IsDataEncryptedWith(KeystoreKeyParams(keystore_keys.back())));
+              IsDataEncryptedWith(Pbkdf2KeyParams(keystore_keys.back())));
   EXPECT_EQ(specifics.passphrase_type(),
             sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE);
   EXPECT_TRUE(specifics.keybag_is_frozen());
@@ -345,7 +341,7 @@
   const std::vector<std::vector<uint8_t>>& keystore_keys =
       GetFakeServer()->GetKeystoreKeys();
   ASSERT_THAT(keystore_keys, SizeIs(1));
-  const KeyParams kKeystoreKeyParams = KeystoreKeyParams(keystore_keys.back());
+  const KeyParams kKeystoreKeyParams = Pbkdf2KeyParams(keystore_keys.back());
   SetNigoriInFakeServer(GetFakeServer(),
                         BuildKeystoreNigoriSpecifics(
                             /*keybag_keys_params=*/{kKeystoreKeyParams},
@@ -370,7 +366,7 @@
   const std::vector<std::vector<uint8_t>>& keystore_keys =
       GetFakeServer()->GetKeystoreKeys();
   ASSERT_THAT(keystore_keys, SizeIs(1));
-  const KeyParams kKeystoreKeyParams = KeystoreKeyParams(keystore_keys.back());
+  const KeyParams kKeystoreKeyParams = Pbkdf2KeyParams(keystore_keys.back());
   const KeyParams kDefaultKeyParams = {
       syncer::KeyDerivationParams::CreateForPbkdf2(), "password"};
   SetNigoriInFakeServer(
@@ -396,7 +392,7 @@
   const std::vector<std::vector<uint8_t>>& keystore_keys =
       GetFakeServer()->GetKeystoreKeys();
   ASSERT_THAT(keystore_keys, SizeIs(2));
-  const KeyParams new_keystore_key_params = KeystoreKeyParams(keystore_keys[1]);
+  const KeyParams new_keystore_key_params = Pbkdf2KeyParams(keystore_keys[1]);
   const std::string expected_key_bag_key_name =
       ComputeKeyName(new_keystore_key_params);
   EXPECT_TRUE(ServerNigoriKeyNameChecker(expected_key_bag_key_name,
@@ -409,7 +405,7 @@
   const std::vector<std::vector<uint8_t>>& keystore_keys =
       GetFakeServer()->GetKeystoreKeys();
   ASSERT_THAT(keystore_keys, SizeIs(1));
-  const KeyParams kKeystoreKeyParams = KeystoreKeyParams(keystore_keys.back());
+  const KeyParams kKeystoreKeyParams = Pbkdf2KeyParams(keystore_keys.back());
   SetNigoriInFakeServer(GetFakeServer(),
                         BuildKeystoreNigoriSpecifics(
                             /*keybag_keys_params=*/{kKeystoreKeyParams},
@@ -451,8 +447,7 @@
   // key used in NigoriSpecifics.
   std::vector<uint8_t> corrupted_keystore_key = keystore_keys[0];
   corrupted_keystore_key.push_back(42u);
-  const KeyParams kKeystoreKeyParams =
-      KeystoreKeyParams(corrupted_keystore_key);
+  const KeyParams kKeystoreKeyParams = Pbkdf2KeyParams(corrupted_keystore_key);
   const KeyParams kDefaultKeyParams = {
       syncer::KeyDerivationParams::CreateForPbkdf2(), "password"};
   SetNigoriInFakeServer(
@@ -531,7 +526,7 @@
   const std::vector<std::vector<uint8_t>>& keystore_keys =
       GetFakeServer()->GetKeystoreKeys();
   ASSERT_THAT(keystore_keys, SizeIs(1));
-  const KeyParams kKeystoreKeyParams = KeystoreKeyParams(keystore_keys.back());
+  const KeyParams kKeystoreKeyParams = Pbkdf2KeyParams(keystore_keys.back());
 
   const autofill::PasswordForm password_form =
       passwords_helper::CreateTestPasswordForm(0);
@@ -575,7 +570,7 @@
 
 IN_PROC_BROWSER_TEST_F(SingleClientNigoriWithWebApiTest,
                        ShouldAcceptEncryptionKeysFromTheWebWhileSignedIn) {
-  const std::string kTestEncryptionKey = "testpassphrase1";
+  const std::vector<uint8_t> kTestEncryptionKey = {1, 2, 3, 4};
 
   const GURL retrieval_url =
       GetTrustedVaultRetrievalURL(*embedded_test_server(), kTestEncryptionKey);
@@ -640,7 +635,7 @@
 
 IN_PROC_BROWSER_TEST_F(SingleClientNigoriWithWebApiTest,
                        PRE_ShouldAcceptEncryptionKeysFromTheWebBeforeSignIn) {
-  const std::string kTestEncryptionKey = "testpassphrase1";
+  const std::vector<uint8_t> kTestEncryptionKey = {1, 2, 3, 4};
   const GURL retrieval_url =
       GetTrustedVaultRetrievalURL(*embedded_test_server(), kTestEncryptionKey);
 
@@ -666,7 +661,7 @@
 
 IN_PROC_BROWSER_TEST_F(SingleClientNigoriWithWebApiTest,
                        ShouldAcceptEncryptionKeysFromTheWebBeforeSignIn) {
-  const std::string kTestEncryptionKey = "testpassphrase1";
+  const std::vector<uint8_t> kTestEncryptionKey = {1, 2, 3, 4};
 
   // Mimic the account being already using a trusted vault passphrase.
   encryption_helper::SetNigoriInFakeServer(
@@ -687,7 +682,7 @@
 IN_PROC_BROWSER_TEST_F(
     SingleClientNigoriWithWebApiTest,
     PRE_ShouldClearEncryptionKeysFromTheWebWhenSigninCookiesCleared) {
-  const std::string kTestEncryptionKey = "testpassphrase1";
+  const std::vector<uint8_t> kTestEncryptionKey = {1, 2, 3, 4};
   const GURL retrieval_url =
       GetTrustedVaultRetrievalURL(*embedded_test_server(), kTestEncryptionKey);
 
@@ -725,7 +720,7 @@
 IN_PROC_BROWSER_TEST_F(
     SingleClientNigoriWithWebApiTest,
     ShouldClearEncryptionKeysFromTheWebWhenSigninCookiesCleared) {
-  const std::string kTestEncryptionKey = "testpassphrase1";
+  const std::vector<uint8_t> kTestEncryptionKey = {1, 2, 3, 4};
 
   // Mimic the account being already using a trusted vault passphrase.
   encryption_helper::SetNigoriInFakeServer(
@@ -744,7 +739,7 @@
 IN_PROC_BROWSER_TEST_F(
     SingleClientNigoriWithWebApiTest,
     ShouldRemotelyTransitFromTrustedVaultToKeystorePassphrase) {
-  const std::string kTestEncryptionKey = "testpassphrase1";
+  const std::vector<uint8_t> kTestEncryptionKey = {1, 2, 3, 4};
 
   const GURL retrieval_url =
       GetTrustedVaultRetrievalURL(*embedded_test_server(), kTestEncryptionKey);
@@ -781,9 +776,8 @@
   const std::vector<std::vector<uint8_t>>& keystore_keys =
       GetFakeServer()->GetKeystoreKeys();
   ASSERT_THAT(keystore_keys, SizeIs(1));
-  const KeyParams kKeystoreKeyParams = KeystoreKeyParams(keystore_keys.back());
-  const KeyParams kTrustedVaultKeyParams =
-      TrustedVaultKeyParams(kTestEncryptionKey);
+  const KeyParams kKeystoreKeyParams = Pbkdf2KeyParams(keystore_keys.back());
+  const KeyParams kTrustedVaultKeyParams = Pbkdf2KeyParams(kTestEncryptionKey);
   SetNigoriInFakeServer(
       GetFakeServer(),
       BuildKeystoreNigoriSpecifics(
@@ -811,7 +805,7 @@
 IN_PROC_BROWSER_TEST_F(
     SingleClientNigoriWithWebApiTest,
     ShouldRemotelyTransitFromTrustedVaultToCustomPassphrase) {
-  const std::string kTestEncryptionKey = "testpassphrase1";
+  const std::vector<uint8_t> kTestEncryptionKey = {1, 2, 3, 4};
 
   const GURL retrieval_url =
       GetTrustedVaultRetrievalURL(*embedded_test_server(), kTestEncryptionKey);
@@ -847,8 +841,7 @@
   // Mimic remote transition to custom passphrase.
   const KeyParams kCustomPassphraseKeyParams = {
       syncer::KeyDerivationParams::CreateForPbkdf2(), "passphrase"};
-  const KeyParams kTrustedVaultKeyParams =
-      TrustedVaultKeyParams(kTestEncryptionKey);
+  const KeyParams kTrustedVaultKeyParams = Pbkdf2KeyParams(kTestEncryptionKey);
   SetNigoriInFakeServer(GetFakeServer(),
                         CreateCustomPassphraseNigori(kCustomPassphraseKeyParams,
                                                      kTrustedVaultKeyParams));
@@ -897,7 +890,7 @@
 
 IN_PROC_BROWSER_TEST_F(SingleClientNigoriWithWebApiFromUntrustedOriginTest,
                        ShouldNotExposeJavascriptApi) {
-  const std::string kTestEncryptionKey = "testpassphrase1";
+  const std::vector<uint8_t> kTestEncryptionKey = {1, 2, 3, 4};
 
   const GURL retrieval_url =
       GetTrustedVaultRetrievalURL(*embedded_test_server(), kTestEncryptionKey);
diff --git a/chrome/browser/themes/theme_helper.cc b/chrome/browser/themes/theme_helper.cc
index 6c08495..c271144 100644
--- a/chrome/browser/themes/theme_helper.cc
+++ b/chrome/browser/themes/theme_helper.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/themes/browser_theme_pack.h"
 #include "chrome/browser/themes/custom_theme_supplier.h"
 #include "chrome/browser/themes/theme_properties.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "chrome/grit/theme_resources.h"
 #include "components/grit/components_scaled_resources.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -25,7 +26,53 @@
 // "Default" theme. We have to detect this case specifically. (By the time we
 // realize we've installed the default theme, we already have an extension
 // unpacked on the filesystem.)
-const char kDefaultThemeGalleryID[] = "hkacjpbfdknhflllbcmjibkdeoafencn";
+constexpr char kDefaultThemeGalleryID[] = "hkacjpbfdknhflllbcmjibkdeoafencn";
+
+const std::array<SkColor, 2> GetTabGroupColors(int color_id) {
+  switch (color_id) {
+    case TP::COLOR_TAB_GROUP_CONTEXT_MENU_BLUE:
+    case TP::COLOR_TAB_GROUP_DIALOG_BLUE:
+    case TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_BLUE:
+    case TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_BLUE:
+      return {gfx::kGoogleBlue600, gfx::kGoogleBlue300};
+    case TP::COLOR_TAB_GROUP_CONTEXT_MENU_RED:
+    case TP::COLOR_TAB_GROUP_DIALOG_RED:
+    case TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_RED:
+    case TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_RED:
+      return {gfx::kGoogleRed600, gfx::kGoogleRed300};
+    case TP::COLOR_TAB_GROUP_CONTEXT_MENU_YELLOW:
+    case TP::COLOR_TAB_GROUP_DIALOG_YELLOW:
+    case TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_YELLOW:
+    case TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_YELLOW:
+      return {gfx::kGoogleYellow900, gfx::kGoogleYellow300};
+    case TP::COLOR_TAB_GROUP_CONTEXT_MENU_GREEN:
+    case TP::COLOR_TAB_GROUP_DIALOG_GREEN:
+    case TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_GREEN:
+    case TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_GREEN:
+      return {gfx::kGoogleGreen600, gfx::kGoogleGreen300};
+    case TP::COLOR_TAB_GROUP_CONTEXT_MENU_PINK:
+    case TP::COLOR_TAB_GROUP_DIALOG_PINK:
+    case TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_PINK:
+    case TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_PINK:
+      return {gfx::kGooglePink700, gfx::kGooglePink300};
+    case TP::COLOR_TAB_GROUP_CONTEXT_MENU_PURPLE:
+    case TP::COLOR_TAB_GROUP_DIALOG_PURPLE:
+    case TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_PURPLE:
+    case TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_PURPLE:
+      return {gfx::kGooglePurple600, gfx::kGooglePurple200};
+    case TP::COLOR_TAB_GROUP_CONTEXT_MENU_CYAN:
+    case TP::COLOR_TAB_GROUP_DIALOG_CYAN:
+    case TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_CYAN:
+    case TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_CYAN:
+      return {gfx::kGoogleCyan900, gfx::kGoogleCyan300};
+    case TP::COLOR_TAB_GROUP_CONTEXT_MENU_GREY:
+    case TP::COLOR_TAB_GROUP_DIALOG_GREY:
+    case TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_GREY:
+    case TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_GREY:
+    default:
+      return {gfx::kGoogleGrey700, gfx::kGoogleGrey400};
+  }
+}
 
 SkColor IncreaseLightness(SkColor color, double percent) {
   color_utils::HSL result;
@@ -242,6 +289,10 @@
     int id,
     bool incognito,
     const CustomThemeSupplier* theme_supplier) const {
+  if (TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_GREY <= id &&
+      id <= TP::COLOR_TAB_GROUP_CONTEXT_MENU_CYAN)
+    return GetTabGroupColor(id, incognito, theme_supplier);
+
   // For backward compat with older themes, some newer colors are generated from
   // older ones if they are missing.
   const auto get_frame_color = [this, incognito, theme_supplier](bool active) {
@@ -586,3 +637,27 @@
       return base::nullopt;
   }
 }
+
+SkColor ThemeHelper::GetTabGroupColor(
+    int id,
+    bool incognito,
+    const CustomThemeSupplier* theme_supplier) const {
+  // Deal with tab group colors in the tabstrip.
+  if (id <= TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_CYAN) {
+    int tab_color_id = id < TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_GREY
+                           ? TP::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_ACTIVE
+                           : TP::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_INACTIVE;
+
+    // TODO(tluk) Change to ensure consistent color ids between WebUI and views
+    // tabstrip groups. Currently WebUI tabstrip groups always check active
+    // frame colors (https://crbug.com/1060398).
+    if (base::FeatureList::IsEnabled(features::kWebUITabStrip))
+      tab_color_id = TP::COLOR_FRAME_ACTIVE;
+
+    return GetTabGroupColors(id)[color_utils::IsDark(
+        GetColor(tab_color_id, incognito, theme_supplier))];
+  }
+
+  // Deal with the rest of the tab group colors.
+  return GetTabGroupColors(id)[UseDarkModeColors(theme_supplier)];
+}
diff --git a/chrome/browser/themes/theme_helper.h b/chrome/browser/themes/theme_helper.h
index daac90c7..3ae127e 100644
--- a/chrome/browser/themes/theme_helper.h
+++ b/chrome/browser/themes/theme_helper.h
@@ -163,6 +163,10 @@
       bool incognito,
       const CustomThemeSupplier* theme_supplier) const;
 
+  SkColor GetTabGroupColor(int id,
+                           bool incognito,
+                           const CustomThemeSupplier* theme_supplier) const;
+
   SEQUENCE_CHECKER(sequence_checker_);
 };
 
diff --git a/chrome/browser/themes/theme_helper_win.cc b/chrome/browser/themes/theme_helper_win.cc
index da3ecd9..531318f9 100644
--- a/chrome/browser/themes/theme_helper_win.cc
+++ b/chrome/browser/themes/theme_helper_win.cc
@@ -121,6 +121,10 @@
     case ThemeProperties::COLOR_FRAME_ACTIVE_INCOGNITO:
     case ThemeProperties::COLOR_FRAME_INACTIVE:
     case ThemeProperties::COLOR_FRAME_INACTIVE_INCOGNITO:
+    case ThemeProperties::COLOR_TAB_BACKGROUND_ACTIVE_FRAME_ACTIVE:
+    case ThemeProperties::COLOR_TAB_BACKGROUND_ACTIVE_FRAME_ACTIVE_INCOGNITO:
+    case ThemeProperties::COLOR_TAB_BACKGROUND_ACTIVE_FRAME_INACTIVE:
+    case ThemeProperties::COLOR_TAB_BACKGROUND_ACTIVE_FRAME_INACTIVE_INCOGNITO:
     case ThemeProperties::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_ACTIVE:
     case ThemeProperties::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_ACTIVE_INCOGNITO:
     case ThemeProperties::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_INACTIVE:
diff --git a/chrome/browser/themes/theme_properties.h b/chrome/browser/themes/theme_properties.h
index d0a81db..e00eff2 100644
--- a/chrome/browser/themes/theme_properties.h
+++ b/chrome/browser/themes/theme_properties.h
@@ -149,6 +149,48 @@
     COLOR_TAB_PIP_PLAYING,
     COLOR_TAB_ALERT_CAPTURING,
 
+    // Note: All tab group color ids must be grouped together consecutively and
+    // grouped together by use (eg grouped by dialog, context menu etc).
+    // This permits range checking and reduces redundant code. If you change or
+    // add to any of the below color ids, change the relevant code in
+    // ThemeHelper.
+
+    // The colors used for tab groups in the tabstrip.
+    COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_GREY,
+    COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_BLUE,
+    COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_RED,
+    COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_YELLOW,
+    COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_GREEN,
+    COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_PINK,
+    COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_PURPLE,
+    COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_CYAN,
+    COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_GREY,
+    COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_BLUE,
+    COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_RED,
+    COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_YELLOW,
+    COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_GREEN,
+    COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_PINK,
+    COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_PURPLE,
+    COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_CYAN,
+    // The colors used for tab groups in the bubble dialog view.
+    COLOR_TAB_GROUP_DIALOG_GREY,
+    COLOR_TAB_GROUP_DIALOG_BLUE,
+    COLOR_TAB_GROUP_DIALOG_RED,
+    COLOR_TAB_GROUP_DIALOG_YELLOW,
+    COLOR_TAB_GROUP_DIALOG_GREEN,
+    COLOR_TAB_GROUP_DIALOG_PINK,
+    COLOR_TAB_GROUP_DIALOG_PURPLE,
+    COLOR_TAB_GROUP_DIALOG_CYAN,
+    // The colors used for tab groups in the context submenu.
+    COLOR_TAB_GROUP_CONTEXT_MENU_GREY,
+    COLOR_TAB_GROUP_CONTEXT_MENU_BLUE,
+    COLOR_TAB_GROUP_CONTEXT_MENU_RED,
+    COLOR_TAB_GROUP_CONTEXT_MENU_YELLOW,
+    COLOR_TAB_GROUP_CONTEXT_MENU_GREEN,
+    COLOR_TAB_GROUP_CONTEXT_MENU_PINK,
+    COLOR_TAB_GROUP_CONTEXT_MENU_PURPLE,
+    COLOR_TAB_GROUP_CONTEXT_MENU_CYAN,
+
     // Calculated representative colors for the background of window control
     // buttons.
     COLOR_WINDOW_CONTROL_BUTTON_BACKGROUND_ACTIVE,
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 765812d..966d13a 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -638,7 +638,7 @@
   if (!is_fuchsia) {
     # TODO(crbug.com/753619): Enable crash reporting on Fuchsia.
     deps += [
-      "//components/crash/content/app",
+      "//components/crash/core/app",
       "//components/crash/core/browser",
     ]
   }
@@ -1197,6 +1197,8 @@
       "tabs/tab_group_controller.h",
       "tabs/tab_group_model.cc",
       "tabs/tab_group_model.h",
+      "tabs/tab_group_theme.cc",
+      "tabs/tab_group_theme.h",
       "tabs/tab_menu_model.cc",
       "tabs/tab_menu_model.h",
       "tabs/tab_menu_model_factory.cc",
@@ -1982,6 +1984,8 @@
       "webui/settings/chromeos/accessibility_handler.h",
       "webui/settings/chromeos/account_manager_handler.cc",
       "webui/settings/chromeos/account_manager_handler.h",
+      "webui/settings/chromeos/ambient_mode_handler.cc",
+      "webui/settings/chromeos/ambient_mode_handler.h",
       "webui/settings/chromeos/android_apps_handler.cc",
       "webui/settings/chromeos/android_apps_handler.h",
       "webui/settings/chromeos/app_management/app_management_page_handler_factory.cc",
@@ -2401,12 +2405,8 @@
       "cocoa/fullscreen/fullscreen_toolbar_animation_controller.mm",
       "cocoa/fullscreen/fullscreen_toolbar_controller.h",
       "cocoa/fullscreen/fullscreen_toolbar_controller.mm",
-      "cocoa/fullscreen/fullscreen_toolbar_controller_views.h",
-      "cocoa/fullscreen/fullscreen_toolbar_controller_views.mm",
       "cocoa/fullscreen/fullscreen_toolbar_mouse_tracker.h",
       "cocoa/fullscreen/fullscreen_toolbar_mouse_tracker.mm",
-      "cocoa/fullscreen/fullscreen_toolbar_visibility_lock_controller.h",
-      "cocoa/fullscreen/fullscreen_toolbar_visibility_lock_controller.mm",
       "cocoa/handoff_active_url_observer.cc",
       "cocoa/handoff_active_url_observer.h",
       "cocoa/handoff_active_url_observer_bridge.h",
diff --git a/chrome/browser/ui/android/passwords/manual_filling_view_android.cc b/chrome/browser/ui/android/passwords/manual_filling_view_android.cc
index eb71287..55082e2 100644
--- a/chrome/browser/ui/android/passwords/manual_filling_view_android.cc
+++ b/chrome/browser/ui/android/passwords/manual_filling_view_android.cc
@@ -116,6 +116,15 @@
       static_cast<autofill::AccessoryAction>(selected_action));
 }
 
+void ManualFillingViewAndroid::OnToggleChanged(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj,
+    jint selected_action,
+    jboolean enabled) {
+  controller_->OnToggleChanged(
+      static_cast<autofill::AccessoryAction>(selected_action), enabled);
+}
+
 ScopedJavaLocalRef<jobject>
 ManualFillingViewAndroid::ConvertAccessorySheetDataToJavaObject(
     JNIEnv* env,
diff --git a/chrome/browser/ui/android/passwords/manual_filling_view_android.h b/chrome/browser/ui/android/passwords/manual_filling_view_android.h
index ec4aba0..404d06a 100644
--- a/chrome/browser/ui/android/passwords/manual_filling_view_android.h
+++ b/chrome/browser/ui/android/passwords/manual_filling_view_android.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_ANDROID_PASSWORDS_MANUAL_FILLING_VIEW_ANDROID_H_
 #define CHROME_BROWSER_UI_ANDROID_PASSWORDS_MANUAL_FILLING_VIEW_ANDROID_H_
 
+#include <jni.h>
 #include <vector>
 
 #include "base/android/scoped_java_ref.h"
@@ -49,6 +50,10 @@
   void OnOptionSelected(JNIEnv* env,
                         const base::android::JavaParamRef<jobject>& obj,
                         jint selected_action);
+  void OnToggleChanged(JNIEnv* env,
+                       const base::android::JavaParamRef<jobject>& obj,
+                       jint selected_action,
+                       jboolean enabled);
 
  private:
   void OnImageFetched(base::android::ScopedJavaGlobalRef<jstring> j_origin,
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.cc b/chrome/browser/ui/ash/chrome_new_window_client.cc
index e921c11..73c0e41b 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client.cc
@@ -77,6 +77,7 @@
     {{ChromePage::ACCESSIBILITY, chrome::kAccessibilitySubPage},
      {ChromePage::ACCOUNTS, chrome::kAccountSubPage},
      {ChromePage::ACCOUNTMANAGER, chrome::kAccountManagerSubPage},
+     {ChromePage::AMBIENTMODE, chrome::kAmbientModeSubPage},
      {ChromePage::ANDROIDAPPSDETAILS, chrome::kAndroidAppsDetailsSubPage},
      {ChromePage::ANDROIDAPPSDETAILSINBROWSERSETTINGS,
       chrome::kAndroidAppsDetailsSubPageInBrowserSettings},
diff --git a/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc b/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
index cb519257..fa56647 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
@@ -286,6 +286,8 @@
                      base_url.Resolve(chrome::kAccessibilitySubPage));
   TestOpenChromePage(ChromePage::ACCOUNTMANAGER,
                      base_url.Resolve(chrome::kAccountManagerSubPage));
+  TestOpenChromePage(ChromePage::AMBIENTMODE,
+                     base_url.Resolve(chrome::kAmbientModeSubPage));
   TestOpenChromePage(ChromePage::ANDROIDAPPSDETAILS,
                      base_url.Resolve(chrome::kAndroidAppsDetailsSubPage));
   TestOpenChromePage(
diff --git a/chrome/browser/ui/ash/drag_to_overview_interactive_uitest.cc b/chrome/browser/ui/ash/drag_to_overview_interactive_uitest.cc
deleted file mode 100644
index 724edfd..0000000
--- a/chrome/browser/ui/ash/drag_to_overview_interactive_uitest.cc
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/public/cpp/ash_switches.h"
-#include "ash/public/cpp/test/shell_test_api.h"
-#include "base/bind.h"
-#include "base/macros.h"
-#include "base/run_loop.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
-#include "chrome/browser/ui/views/tabs/tab_strip.h"
-#include "chrome/test/base/interactive_test_utils.h"
-#include "chrome/test/base/perf/performance_test.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/test/test_utils.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "ui/aura/window.h"
-#include "ui/base/test/ui_controls.h"
-#include "ui/compositor/compositor.h"
-#include "ui/display/display.h"
-#include "ui/display/screen.h"
-#include "ui/gfx/geometry/vector2d.h"
-#include "ui/views/widget/widget.h"
-
-namespace {
-
-int GetDetachY(TabStrip* tab_strip) {
-  return std::max(TabDragController::kTouchVerticalDetachMagnetism,
-                  TabDragController::kVerticalDetachMagnetism) +
-         tab_strip->height() + 1;
-}
-
-// Waits for the primary display to present a frame after the object is
-// constructed.
-class NextFrameWaiter {
- public:
-  NextFrameWaiter() {
-    ash::ShellTestApi().WaitForNextFrame(base::BindOnce(
-        &NextFrameWaiter::OnFramePresented, base::Unretained(this)));
-  }
-  ~NextFrameWaiter() { EXPECT_TRUE(frame_presented_); }
-
-  void WaitForDisplay() {
-    if (!frame_presented_) {
-      run_loop_ = std::make_unique<base::RunLoop>(
-          base::RunLoop::Type::kNestableTasksAllowed);
-      run_loop_->Run();
-      EXPECT_TRUE(frame_presented_);
-    }
-  }
-
- private:
-  void OnFramePresented() {
-    frame_presented_ = true;
-    if (run_loop_)
-      run_loop_->Quit();
-  }
-
-  bool frame_presented_ = false;
-  std::unique_ptr<base::RunLoop> run_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(NextFrameWaiter);
-};
-
-}  // namespace
-
-class DragToOverviewTest : public UIPerformanceTest {
- public:
-  DragToOverviewTest() = default;
-  ~DragToOverviewTest() override = default;
-
-  // UIPerformanceTest:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(ash::switches::kAshEnableTabletMode);
-  }
-  void SetUpOnMainThread() override {
-    UIPerformanceTest::SetUpOnMainThread();
-    ash::ShellTestApi().SetTabletModeEnabledForTest(true);
-
-    if (base::SysInfo::IsRunningOnChromeOS()) {
-      base::RunLoop run_loop;
-      base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-          FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(5));
-      run_loop.Run();
-    }
-  }
-  std::vector<std::string> GetUMAHistogramNames() const override {
-    if (tab_drag_test_) {
-      return {"Ash.SwipeDownDrag.Tab.PresentationTime.TabletMode",
-              "Ash.SwipeDownDrag.Tab.PresentationTime.MaxLatency.TabletMode"};
-    }
-    return {
-        "Ash.SwipeDownDrag.Window.PresentationTime.TabletMode",
-        "Ash.SwipeDownDrag.Window.PresentationTime.MaxLatency.TabletMode",
-    };
-  }
-
-  // Continue a drag by perform |count| steps mouse dragging with each step move
-  // |delta|, then moue up.
-  void ContinueDrag(const gfx::Point& start_position,
-                    const gfx::Vector2d& delta,
-                    int count) {
-    gfx::Point drag_position = start_position;
-    for (int i = 0; i < count; ++i) {
-      drag_position += delta;
-
-      ash::ShellTestApi().WaitForNoPointerHoldLock();
-      NextFrameWaiter waiter;
-      ASSERT_TRUE(
-          ui_controls::SendMouseMove(drag_position.x(), drag_position.y()));
-      waiter.WaitForDisplay();
-    }
-
-    {
-      NextFrameWaiter waiter;
-      ASSERT_TRUE(
-          ui_controls::SendMouseEvents(ui_controls::LEFT, ui_controls::UP));
-      waiter.WaitForDisplay();
-    }
-  }
-
-  void VerifyTabDetachedAndContinueDrag(const gfx::Point& start_position,
-                                        const gfx::Vector2d delta,
-                                        int count) {
-    // Tab should be detached to create a new browser window.
-    EXPECT_EQ(2u, BrowserList::GetInstance()->size());
-    EXPECT_TRUE(TabDragController::IsActive());
-
-    ContinueDrag(start_position, delta, count);
-  }
-
-  void set_tab_drag_test(bool tab_drag_test) { tab_drag_test_ = tab_drag_test; }
-
- private:
-  bool tab_drag_test_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(DragToOverviewTest);
-};
-
-// TODO(https://crbug.com/1031526) flaky test
-IN_PROC_BROWSER_TEST_F(DragToOverviewTest, DISABLED_DragWindow) {
-  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
-  aura::Window* browser_window = browser_view->GetWidget()->GetNativeWindow();
-  gfx::Rect browser_screen_bounds = browser_window->GetBoundsInScreen();
-
-  const gfx::Point start_position(
-      browser_screen_bounds.CenterPoint().x(),
-      browser_screen_bounds.y() + browser_view->GetTabStripHeight() / 2);
-
-  ash::ShellTestApi().WaitForNoPointerHoldLock();
-  ASSERT_TRUE(
-      ui_test_utils::SendMouseMoveSync(start_position) &&
-      ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, ui_controls::DOWN));
-
-  // One more mouse move to start drag.
-  ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(start_position));
-
-  // Drag 25% of the work area height.
-  const int drag_length = display::Screen::GetScreen()
-                              ->GetDisplayNearestWindow(browser_window)
-                              .work_area_size()
-                              .height() /
-                          4;
-  constexpr int kSteps = 20;
-  gfx::Vector2d delta(0, drag_length / kSteps);
-  ContinueDrag(start_position, delta, kSteps);
-  EXPECT_TRUE(ash::ShellTestApi().IsOverviewSelecting());
-}
-
-// TODO(http://crbug.com/1028386): Test flakily fails.
-IN_PROC_BROWSER_TEST_F(DragToOverviewTest, DISABLED_DragTab) {
-  set_tab_drag_test(true);
-
-  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
-  aura::Window* browser_window = browser_view->GetWidget()->GetNativeWindow();
-
-  AddBlankTabAndShow(browser());
-  browser_view->tabstrip()->StopAnimating(true);
-
-  gfx::Point drag_position(ui_test_utils::GetCenterInScreenCoordinates(
-      browser_view->tabstrip()->tab_at(0)));
-
-  ash::ShellTestApi().WaitForNoPointerHoldLock();
-  ASSERT_TRUE(
-      ui_test_utils::SendMouseMoveSync(drag_position) &&
-      ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, ui_controls::DOWN));
-
-  // Drag 25% of the work area height.
-  const int drag_length = display::Screen::GetScreen()
-                              ->GetDisplayNearestWindow(browser_window)
-                              .work_area_size()
-                              .height() /
-                          4;
-  constexpr int kSteps = 20;
-  gfx::Vector2d delta(0, drag_length / kSteps);
-
-  // Drag tab far enough to detach.
-  ash::ShellTestApi().WaitForNoPointerHoldLock();
-  drag_position.Offset(0, GetDetachY(browser_view->tabstrip()));
-  ui_controls::SendMouseMoveNotifyWhenDone(
-      drag_position.x(), drag_position.y(),
-      base::BindOnce(&DragToOverviewTest::VerifyTabDetachedAndContinueDrag,
-                     base::Unretained(this), drag_position, delta, kSteps));
-
-  // Wait for the drag to finish.
-  content::WindowedNotificationObserver(
-      chrome::NOTIFICATION_TAB_DRAG_LOOP_DONE,
-      content::NotificationService::AllSources())
-      .Wait();
-}
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller.h b/chrome/browser/ui/autofill/autofill_popup_controller.h
index 141f5b3..d0b9e74 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller.h
+++ b/chrome/browser/ui/autofill/autofill_popup_controller.h
@@ -35,10 +35,10 @@
   virtual int GetLineCount() const = 0;
 
   // Returns the full set of autofill suggestions, if applicable.
-  virtual const std::vector<autofill::Suggestion> GetSuggestions() = 0;
+  virtual std::vector<Suggestion> GetSuggestions() const = 0;
 
   // Returns the suggestion at the given |row| index.
-  virtual const autofill::Suggestion& GetSuggestionAt(int row) const = 0;
+  virtual const Suggestion& GetSuggestionAt(int row) const = 0;
 
   // Returns the suggestion value string at the given |row| index.
   virtual const base::string16& GetSuggestionValueAt(int row) const = 0;
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
index 7ba43ff0..79199d6f 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
@@ -63,10 +63,8 @@
   if (previous.get())
     previous->Hide(PopupHidingReason::kViewDestroyed);
 
-  AutofillPopupControllerImpl* controller =
-      new AutofillPopupControllerImpl(
-          delegate, web_contents, container_view, element_bounds,
-          text_direction);
+  AutofillPopupControllerImpl* controller = new AutofillPopupControllerImpl(
+      delegate, web_contents, container_view, element_bounds, text_direction);
   return controller->GetWeakPtr();
 }
 #endif
@@ -136,14 +134,12 @@
       ->RegisterKeyPressHandler(
           base::Bind(&AutofillPopupControllerImpl::HandleKeyPressEvent,
                      base::Unretained(this)));
-  pinned_until_update_ = false;
   delegate_->OnPopupShown();
 }
 
 void AutofillPopupControllerImpl::UpdateDataListValues(
     const std::vector<base::string16>& values,
     const std::vector<base::string16>& labels) {
-
   selected_line_.reset();
   // Remove all the old data list values, which should always be at the top of
   // the list if they are present.
@@ -160,7 +156,7 @@
       suggestions_.erase(suggestions_.begin());
     }
 
-     // The popup contents have changed, so either update the bounds or hide it.
+    // The popup contents have changed, so either update the bounds or hide it.
     if (HasSuggestions())
       OnSuggestionsChanged();
     else
@@ -187,8 +183,8 @@
   OnSuggestionsChanged();
 }
 
-void AutofillPopupControllerImpl::PinViewUntilUpdate() {
-  pinned_until_update_ = true;
+void AutofillPopupControllerImpl::PinView() {
+  is_view_pinned_ = true;
 }
 
 base::span<const Suggestion>
@@ -199,9 +195,9 @@
 void AutofillPopupControllerImpl::Hide(PopupHidingReason reason) {
   // If the reason for hiding is only stale data or a user interacting with
   // native Chrome UI (kFocusChanged/kEndEditing), the popup might be kept open.
-  if (pinned_until_update_ && (reason == PopupHidingReason::kStaleData ||
-                               reason == PopupHidingReason::kFocusChanged ||
-                               reason == PopupHidingReason::kEndEditing)) {
+  if (is_view_pinned_ && (reason == PopupHidingReason::kStaleData ||
+                          reason == PopupHidingReason::kFocusChanged ||
+                          reason == PopupHidingReason::kEndEditing)) {
     return;  // Don't close the popup while waiting for an update.
   }
   if (delegate_) {
@@ -307,7 +303,7 @@
   return controller_common_.text_direction == base::i18n::RIGHT_TO_LEFT;
 }
 
-const std::vector<Suggestion> AutofillPopupControllerImpl::GetSuggestions() {
+std::vector<Suggestion> AutofillPopupControllerImpl::GetSuggestions() const {
   return suggestions_;
 }
 
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.h b/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
index 2df0bf0..f943b54e 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
@@ -25,7 +25,7 @@
 namespace content {
 struct NativeWebKeyboardEvent;
 class WebContents;
-}
+}  // namespace content
 
 namespace ui {
 class AXPlatformNode;
@@ -62,9 +62,10 @@
   virtual void UpdateDataListValues(const std::vector<base::string16>& values,
                                     const std::vector<base::string16>& labels);
 
-  // Informs the controller that |Show| will be called again which enables
-  // keeping the UI alive.
-  void PinViewUntilUpdate();
+  // Informs the controller that the popup may not be hidden by stale data or
+  // interactions with native Chrome UI. This state remains active until the
+  // view is destroyed.
+  void PinView();
 
   // Returns (not elided) suggestions currently held by the controller.
   base::span<const Suggestion> GetUnelidedSuggestions() const;
@@ -94,7 +95,7 @@
   const gfx::RectF& element_bounds() const override;
   void SetElementBounds(const gfx::RectF& bounds);
   bool IsRTL() const override;
-  const std::vector<Suggestion> GetSuggestions() override;
+  std::vector<Suggestion> GetSuggestions() const override;
 
   // AutofillPopupController implementation.
   void OnSuggestionsChanged() override;
@@ -164,9 +165,9 @@
   AutofillPopupView* view_ = nullptr;  // Weak reference.
   base::WeakPtr<AutofillPopupDelegate> delegate_;
 
-  // If set to true, the popup will not be hidden because of stale data or if
+  // If set to true, the popup will never be hidden because of stale data or if
   // the user interacts with native UI.
-  bool pinned_until_update_ = false;
+  bool is_view_pinned_ = false;
 
   // The current Autofill query values.
   std::vector<Suggestion> suggestions_;
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
index b1185d22..cd9a9f8b 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
@@ -43,13 +43,13 @@
 #include "content/public/browser/browser_accessibility_state.h"
 #endif
 
+using base::ASCIIToUTF16;
+using base::WeakPtr;
 using ::testing::_;
 using ::testing::AtLeast;
 using ::testing::Mock;
 using ::testing::NiceMock;
 using ::testing::StrictMock;
-using base::ASCIIToUTF16;
-using base::WeakPtr;
 
 namespace autofill {
 namespace {
@@ -146,8 +146,7 @@
                                     nullptr,
                                     nullptr,
                                     element_bounds,
-                                    base::i18n::UNKNOWN_DIRECTION) {
-  }
+                                    base::i18n::UNKNOWN_DIRECTION) {}
   ~TestAutofillPopupController() override = default;
 
   // Making protected functions public for testing
@@ -271,9 +270,7 @@
     return autofill_popup_controller_;
   }
 
-  MockAutofillExternalDelegate* delegate() {
-    return external_delegate_.get();
-  }
+  MockAutofillExternalDelegate* delegate() { return external_delegate_.get(); }
 
   MockAutofillPopupView* autofill_popup_view() {
     return autofill_popup_view_.get();
@@ -336,8 +333,7 @@
   EXPECT_FALSE(autofill_popup_controller_->selected_line());
   // Check that there are at least 2 values so that the first and last selection
   // are different.
-  EXPECT_GE(2,
-      static_cast<int>(autofill_popup_controller_->GetLineCount()));
+  EXPECT_GE(2, static_cast<int>(autofill_popup_controller_->GetLineCount()));
 
   // Test wrapping before the front.
   autofill_popup_controller_->SelectPreviousLine();
@@ -638,10 +634,8 @@
       AutofillPopupControllerImpl::GetOrCreate(
           test_controller->GetWeakPtr(), delegate.GetWeakPtr(), nullptr,
           nullptr, bounds, base::i18n::UNKNOWN_DIRECTION);
-  EXPECT_EQ(
-      bounds,
-      static_cast<AutofillPopupController*>(controller3.get())->
-          element_bounds());
+  EXPECT_EQ(bounds, static_cast<AutofillPopupController*>(controller3.get())
+                        ->element_bounds());
   controller3->Hide(PopupHidingReason::kViewDestroyed);
 
   // Hide the test_controller to delete it.
@@ -697,7 +691,7 @@
 
 TEST_F(AutofillPopupControllerUnitTest, DontHideWhenWaitingForData) {
   EXPECT_CALL(*autofill_popup_view(), Hide).Times(0);
-  autofill_popup_controller_->PinViewUntilUpdate();
+  autofill_popup_controller_->PinView();
 
   // Hide() will not work for stale data or when focusing native UI.
   autofill_popup_controller_->DoHide(PopupHidingReason::kStaleData);
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index c573fc19..95b7b76 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -501,9 +501,9 @@
   return popup_controller_->GetUnelidedSuggestions();
 }
 
-void ChromeAutofillClient::PinPopupViewUntilUpdate() {
+void ChromeAutofillClient::PinPopupView() {
   if (popup_controller_.get())
-    popup_controller_->PinViewUntilUpdate();
+    popup_controller_->PinView();
 }
 
 void ChromeAutofillClient::UpdatePopup(
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h
index e8452bc..3e51e27 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.h
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -140,7 +140,7 @@
       const std::vector<base::string16>& values,
       const std::vector<base::string16>& labels) override;
   base::span<const Suggestion> GetPopupSuggestions() const override;
-  void PinPopupViewUntilUpdate() override;
+  void PinPopupView() override;
   void UpdatePopup(const std::vector<autofill::Suggestion>& suggestions,
                    PopupType popup_type) override;
   void HideAutofillPopup(PopupHidingReason reason) override;
diff --git a/chrome/browser/ui/cocoa/fullscreen/fullscreen_menubar_tracker.mm b/chrome/browser/ui/cocoa/fullscreen/fullscreen_menubar_tracker.mm
index 6c7e5dae..b57c80f 100644
--- a/chrome/browser/ui/cocoa/fullscreen/fullscreen_menubar_tracker.mm
+++ b/chrome/browser/ui/cocoa/fullscreen/fullscreen_menubar_tracker.mm
@@ -10,7 +10,6 @@
 #include "base/mac/mac_util.h"
 #include "base/stl_util.h"
 #import "chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller.h"
-#import "chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_visibility_lock_controller.h"
 #include "ui/base/cocoa/appkit_utils.h"
 
 namespace {
@@ -18,6 +17,8 @@
 // The event kind value for a undocumented menubar show/hide Carbon event.
 const CGFloat kMenuBarRevealEventKind = 2004;
 
+// TODO(https://crbug.com/1063417): Replace this with something that works
+// on modern macOS versions.
 OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
                               EventRef event,
                               void* context) {
@@ -50,7 +51,6 @@
 
 @interface FullscreenMenubarTracker () {
   FullscreenToolbarController* _controller;        // weak
-  id<FullscreenToolbarContextDelegate> _delegate;  // weak
 
   // A Carbon event handler that tracks the revealed fraction of the menubar.
   EventHandlerRef _menubarTrackingHandler;
@@ -70,7 +70,6 @@
     (FullscreenToolbarController*)controller {
   if ((self = [super init])) {
     _controller = controller;
-    _delegate = [controller delegate];
     _state = FullscreenMenubarState::HIDDEN;
 
     // Install the Carbon event handler for the menubar show, hide and
@@ -112,8 +111,8 @@
 }
 
 - (void)setMenubarProgress:(CGFloat)progress {
-  if (![_delegate isInAnyFullscreenMode] ||
-      [_delegate isFullscreenTransitionInProgress]) {
+  if (![_controller isInAnyFullscreenMode] ||
+      [_controller isFullscreenTransitionInProgress]) {
     return;
   }
 
@@ -123,7 +122,7 @@
     return;
 
   // Ignore the menubarFraction changes if the Space is inactive.
-  if (![[_delegate window] isOnActiveSpace])
+  if (![[_controller window] isOnActiveSpace])
     return;
 
   if (ui::IsCGFloatEqual(progress, 1.0))
@@ -137,7 +136,6 @@
 
   _menubarFraction = progress;
   [_controller layoutToolbar];
-
   // AppKit drives the menu bar animation from a nested run loop. Flush
   // explicitly so that Chrome's UI updates during the animation.
   [CATransaction flush];
@@ -145,14 +143,12 @@
 
 - (BOOL)isMouseOnScreen {
   return NSMouseInRect([NSEvent mouseLocation],
-                       [[_delegate window] screen].frame, false);
+                       [[_controller window] screen].frame, false);
 }
 
 - (void)activeSpaceDidChange:(NSNotification*)notification {
   _menubarFraction = 0.0;
   _state = FullscreenMenubarState::HIDDEN;
-  [[_controller visibilityLockController] releaseToolbarVisibilityForOwner:self
-                                                             withAnimation:NO];
   [_controller layoutToolbar];
 }
 
diff --git a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller.h b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller.h
index e9438c5..460386d2 100644
--- a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller.h
+++ b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller.h
@@ -8,13 +8,9 @@
 #import <Cocoa/Cocoa.h>
 
 #include "base/mac/mac_util.h"
-#include "base/mac/scoped_nsobject.h"
 
-@class BrowserWindowController;
+class BrowserView;
 @class FullscreenMenubarTracker;
-class FullscreenToolbarAnimationController;
-@class FullscreenToolbarMouseTracker;
-@class FullscreenToolbarVisibilityLockController;
 
 namespace content {
 class WebContents;
@@ -53,36 +49,11 @@
 // window. This class sets up the animation manager, visibility locks, menubar
 // tracking, and mouse tracking associated with the toolbar. It receives input
 // from these objects to update and recompute the fullscreen toolbar laytout.
-@interface FullscreenToolbarController : NSObject {
- @private
-  // Whether or not we are in fullscreen mode.
-  BOOL _inFullscreenMode;
-
-  // Updates the fullscreen toolbar layout for changes in the menubar. This
-  // object is only set when the browser is in fullscreen mode.
-  base::scoped_nsobject<FullscreenMenubarTracker> _menubarTracker;
-
-  // Maintains the toolbar's visibility locks for the TOOLBAR_HIDDEN style.
-  base::scoped_nsobject<FullscreenToolbarVisibilityLockController>
-      _visibilityLockController;
-
-  // Manages the toolbar animations for the TOOLBAR_HIDDEN style.
-  std::unique_ptr<FullscreenToolbarAnimationController> _animationController;
-
-  // When the menu bar and toolbar are visible, creates a tracking area which
-  // is used to keep them visible until the mouse moves far enough away from
-  // them. Only set when the browser is in fullscreen mode.
-  base::scoped_nsobject<FullscreenToolbarMouseTracker> _mouseTracker;
-
-  // The style of the fullscreen toolbar.
-  FullscreenToolbarStyle _toolbarStyle;
-
-  // Delegate for query fullscreen status and context. Weak.
-  id<FullscreenToolbarContextDelegate> _delegate;
-}
+@interface FullscreenToolbarController
+    : NSObject <FullscreenToolbarContextDelegate>
 
 // Designated initializer.
-- (id)initWithDelegate:(id<FullscreenToolbarContextDelegate>)delegate;
+- (id)initWithBrowserView:(BrowserView*)browserView;
 
 // Informs the controller that the browser has entered or exited fullscreen
 // mode. |-enterFullscreenMode| should be called when the window is about to
@@ -119,31 +90,9 @@
 // Returns the object in |menubarTracker_|;
 - (FullscreenMenubarTracker*)menubarTracker;
 
-// Returns the object in |visibilityLockController_|;
-- (FullscreenToolbarVisibilityLockController*)visibilityLockController;
-
 // Sets the value of |toolbarStyle_|.
 - (void)setToolbarStyle:(FullscreenToolbarStyle)style;
 
-- (id<FullscreenToolbarContextDelegate>)delegate;
-
-@end
-
-// Private methods exposed for testing.
-@interface FullscreenToolbarController (ExposedForTesting)
-
-// Returns |animationController_|.
-- (FullscreenToolbarAnimationController*)animationController;
-
-// Allows tests to set a mock FullscreenMenubarTracker object.
-- (void)setMenubarTracker:(FullscreenMenubarTracker*)tracker;
-
-// Allows tests to set a mock FullscreenToolbarMouseTracker object.
-- (void)setMouseTracker:(FullscreenToolbarMouseTracker*)tracker;
-
-// Sets the value of |inFullscreenMode_|.
-- (void)setTestFullscreenMode:(BOOL)isInFullscreen;
-
 @end
 
 #endif  // CHROME_BROWSER_UI_COCOA_FULLSCREEN_FULLSCREEN_TOOLBAR_CONTROLLER_H_
diff --git a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller.mm b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller.mm
index d7a1174..fe1fb04 100644
--- a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller.mm
+++ b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller.mm
@@ -6,28 +6,46 @@
 
 #include "base/command_line.h"
 #include "base/feature_list.h"
+#include "base/mac/scoped_nsobject.h"
 #include "chrome/browser/profiles/profile.h"
 #import "chrome/browser/ui/cocoa/fullscreen/fullscreen_menubar_tracker.h"
 #import "chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_animation_controller.h"
 #import "chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_mouse_tracker.h"
-#import "chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_visibility_lock_controller.h"
+#import "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
+#include "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h"
+#include "ui/views/cocoa/native_widget_mac_ns_window_host.h"
 
-@implementation FullscreenToolbarController
+@implementation FullscreenToolbarController {
+  // Whether or not we are in fullscreen mode.
+  BOOL _inFullscreenMode;
 
-- (id)initWithDelegate:(id<FullscreenToolbarContextDelegate>)delegate {
+  // Updates the fullscreen toolbar layout for changes in the menubar. This
+  // object is only set when the browser is in fullscreen mode.
+  base::scoped_nsobject<FullscreenMenubarTracker> _menubarTracker;
+
+  // Manages the toolbar animations for the TOOLBAR_HIDDEN style.
+  std::unique_ptr<FullscreenToolbarAnimationController> _animationController;
+
+  // When the menu bar and toolbar are visible, creates a tracking area which
+  // is used to keep them visible until the mouse moves far enough away from
+  // them. Only set when the browser is in fullscreen mode.
+  base::scoped_nsobject<FullscreenToolbarMouseTracker> _mouseTracker;
+
+  // The style of the fullscreen toolbar.
+  FullscreenToolbarStyle _toolbarStyle;
+
+  BrowserView* _browserView;  // weak
+}
+
+- (id)initWithBrowserView:(BrowserView*)browserView {
   if ((self = [super init])) {
+    _browserView = browserView;
     _animationController =
         std::make_unique<FullscreenToolbarAnimationController>(self);
-    _visibilityLockController.reset(
-        [[FullscreenToolbarVisibilityLockController alloc]
-            initWithFullscreenToolbarController:self
-                            animationController:_animationController.get()]);
   }
-
-  _delegate = delegate;
   return self;
 }
 
@@ -101,9 +119,7 @@
   if (_toolbarStyle == FullscreenToolbarStyle::TOOLBAR_NONE)
     return NO;
 
-  FullscreenMenubarState menubarState = [_menubarTracker state];
-  return menubarState == FullscreenMenubarState::SHOWN ||
-         [_visibilityLockController isToolbarVisibilityLocked];
+  return [_menubarTracker state] == FullscreenMenubarState::SHOWN;
 }
 
 - (void)updateToolbarFrame:(NSRect)frame {
@@ -112,6 +128,7 @@
 }
 
 - (void)layoutToolbar {
+  _browserView->Layout();
   _animationController->ToolbarDidUpdate();
   [_mouseTracker updateTrackingArea];
 }
@@ -124,36 +141,26 @@
   return _menubarTracker.get();
 }
 
-- (FullscreenToolbarVisibilityLockController*)visibilityLockController {
-  return _visibilityLockController.get();
-}
-
 - (void)setToolbarStyle:(FullscreenToolbarStyle)style {
   _toolbarStyle = style;
 }
 
-- (id<FullscreenToolbarContextDelegate>)delegate {
-  return _delegate;
+- (BOOL)isInAnyFullscreenMode {
+  return _browserView->IsFullscreen();
 }
 
-@end
-
-@implementation FullscreenToolbarController (ExposedForTesting)
-
-- (FullscreenToolbarAnimationController*)animationController {
-  return _animationController.get();
+- (BOOL)isFullscreenTransitionInProgress {
+  auto* host =
+      views::NativeWidgetMacNSWindowHost::GetFromNativeWindow([self window]);
+  if (auto* bridge = host->GetInProcessNSWindowBridge())
+    return bridge->in_fullscreen_transition();
+  DLOG(ERROR) << "TODO(https://crbug.com/915110): Support fullscreen "
+                 "transitions for RemoteMacViews PWA windows.";
+  return false;
 }
 
-- (void)setMenubarTracker:(FullscreenMenubarTracker*)tracker {
-  _menubarTracker.reset([tracker retain]);
-}
-
-- (void)setMouseTracker:(FullscreenToolbarMouseTracker*)tracker {
-  _mouseTracker.reset([tracker retain]);
-}
-
-- (void)setTestFullscreenMode:(BOOL)isInFullscreen {
-  _inFullscreenMode = isInFullscreen;
+- (NSWindow*)window {
+  return _browserView->GetNativeWindow().GetNativeNSWindow();
 }
 
 @end
diff --git a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.h b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.h
deleted file mode 100644
index 1317bb2..0000000
--- a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_COCOA_FULLSCREEN_FULLSCREEN_TOOLBAR_CONTROLLER_VIEWS_H_
-#define CHROME_BROWSER_UI_COCOA_FULLSCREEN_FULLSCREEN_TOOLBAR_CONTROLLER_VIEWS_H_
-
-#import "chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller.h"
-
-#include "base/mac/scoped_nsobject.h"
-
-@class BridgedContentView;
-class BrowserView;
-
-// Provides a controller to the fullscreen toolbar for a single Views based
-// browser window.
-@interface FullscreenToolbarControllerViews
-    : FullscreenToolbarController<FullscreenToolbarContextDelegate> {
- @private
-  BrowserView* _browserView;  // weak
-
-  // Since dealloc() may need to access the native view, we retain it here.
-  base::scoped_nsobject<BridgedContentView> _ns_view;
-}
-
-// Designated initializer.
-- (id)initWithBrowserView:(BrowserView*)browserView;
-
-// Updates the toolbar by updating the layout.
-- (void)layoutToolbar;
-
-@end
-
-#endif  //
-CHROME_BROWSER_UI_COCOA_FULLSCREEN_FULLSCREEN_TOOLBAR_CONTROLLER_VIEWS_H_
diff --git a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm
deleted file mode 100644
index 5064150..0000000
--- a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.h"
-
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h"
-#include "ui/views/cocoa/native_widget_mac_ns_window_host.h"
-
-@implementation FullscreenToolbarControllerViews
-
-- (id)initWithBrowserView:(BrowserView*)browserView {
-  if ((self = [super initWithDelegate:self]))
-    _browserView = browserView;
-
-  return self;
-}
-
-- (void)layoutToolbar {
-  _browserView->Layout();
-  [super layoutToolbar];
-}
-
-- (BOOL)isInAnyFullscreenMode {
-  return _browserView->IsFullscreen();
-}
-
-- (BOOL)isFullscreenTransitionInProgress {
-  auto* host =
-      views::NativeWidgetMacNSWindowHost::GetFromNativeWindow([self window]);
-  if (auto* bridge = host->GetInProcessNSWindowBridge())
-    return bridge->in_fullscreen_transition();
-  DLOG(ERROR) << "TODO(https://crbug.com/915110): Support fullscreen "
-                 "transitions for RemoteMacViews PWA windows.";
-  return false;
-}
-
-- (NSWindow*)window {
-  NSWindow* ns_window = _browserView->GetNativeWindow().GetNativeNSWindow();
-  if (!_ns_view) {
-    auto* host =
-        views::NativeWidgetMacNSWindowHost::GetFromNativeWindow(ns_window);
-    if (host) {
-      if (auto* bridge = host->GetInProcessNSWindowBridge())
-        _ns_view.reset([bridge->ns_view() retain]);
-      else
-        DLOG(ERROR) << "Cannot retain remote NSView.";
-    }
-  }
-  return ns_window;
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_mouse_tracker.mm b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_mouse_tracker.mm
index 726b453..4573fe8 100644
--- a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_mouse_tracker.mm
+++ b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_mouse_tracker.mm
@@ -70,7 +70,7 @@
     [self removeTrackingArea];
   }
 
-  _contentView = [[[_controller delegate] window] contentView];
+  _contentView = [[_controller window] contentView];
 
   _trackingArea.reset([[CrTrackingArea alloc]
       initWithRect:_trackingAreaFrame
@@ -82,7 +82,7 @@
 }
 
 - (void)updateToolbarFrame:(NSRect)frame {
-  NSRect contentBounds = [[[[_controller delegate] window] contentView] bounds];
+  NSRect contentBounds = [[[_controller window] contentView] bounds];
   _trackingAreaFrame = frame;
   _trackingAreaFrame.origin.y -= kTrackingAreaAdditionalThreshold;
   _trackingAreaFrame.size.height =
diff --git a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_visibility_lock_controller.h b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_visibility_lock_controller.h
deleted file mode 100644
index 510ae183..0000000
--- a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_visibility_lock_controller.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_COCOA_FULLSCREEN_FULLSCREEN_TOOLBAR_VISIBILITY_LOCK_CONTROLLER_H_
-#define CHROME_BROWSER_UI_COCOA_FULLSCREEN_FULLSCREEN_TOOLBAR_VISIBILITY_LOCK_CONTROLLER_H_
-
-#import <Cocoa/Cocoa.h>
-
-class FullscreenToolbarAnimationController;
-@class FullscreenToolbarController;
-
-// Various UI elements/events may want to ensure that the toolbar is visible in
-// fullscreen mode. Whenever an object requires toolbar visibility, it locks
-// it. When it no longer requires it, it releases it. This class manages the
-// toolbar visibility locks.
-@interface FullscreenToolbarVisibilityLockController : NSObject
-
-// The designated initializer.
-- (instancetype)
-initWithFullscreenToolbarController:(FullscreenToolbarController*)controller
-                animationController:
-                    (FullscreenToolbarAnimationController*)animationController;
-
-// Returns true if the toolbar visibility is locked.
-- (BOOL)isToolbarVisibilityLocked;
-
-// Returns true if the toolbar visibility is locked by |owner|.
-- (BOOL)isToolbarVisibilityLockedForOwner:(id)owner;
-
-// Methods for locking and releasing the toolbar visibility. If |animate| is
-// true, the toolbar will animate in/out.
-- (void)lockToolbarVisibilityForOwner:(id)owner withAnimation:(BOOL)animate;
-- (void)releaseToolbarVisibilityForOwner:(id)owner withAnimation:(BOOL)animate;
-
-@end
-
-#endif  // CHROME_BROWSER_UI_COCOA_FULLSCREEN_FULLSCREEN_TOOLBAR_VISIBILITY_LOCK_CONTROLLER_H_
diff --git a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_visibility_lock_controller.mm b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_visibility_lock_controller.mm
deleted file mode 100644
index 5b969a9..0000000
--- a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_visibility_lock_controller.mm
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_visibility_lock_controller.h"
-
-#include "base/mac/scoped_nsobject.h"
-
-#import "chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_animation_controller.h"
-#import "chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller.h"
-
-@interface FullscreenToolbarVisibilityLockController () {
-  // Stores the objects that are locking the toolbar visibility.
-  base::scoped_nsobject<NSMutableSet> _visibilityLocks;
-
-  // Our owner.
-  FullscreenToolbarController* _owner;  // weak
-
-  // The object managing the fullscreen toolbar's animations.
-  FullscreenToolbarAnimationController* _animationController;  // weak
-}
-
-@end
-
-@implementation FullscreenToolbarVisibilityLockController
-
-- (instancetype)
-initWithFullscreenToolbarController:(FullscreenToolbarController*)owner
-                animationController:
-                    (FullscreenToolbarAnimationController*)animationController {
-  if ((self = [super init])) {
-    _animationController = animationController;
-    _owner = owner;
-
-    // Create the toolbar visibility lock set; 10 is arbitrary, but should
-    // hopefully be big enough to hold all locks that'll ever be needed.
-    _visibilityLocks.reset([[NSMutableSet setWithCapacity:10] retain]);
-  }
-
-  return self;
-}
-
-- (BOOL)isToolbarVisibilityLocked {
-  return [_visibilityLocks count];
-}
-
-- (BOOL)isToolbarVisibilityLockedForOwner:(id)owner {
-  return [_visibilityLocks containsObject:owner];
-}
-
-- (void)lockToolbarVisibilityForOwner:(id)owner withAnimation:(BOOL)animate {
-  if ([self isToolbarVisibilityLockedForOwner:owner])
-    return;
-
-  [_visibilityLocks addObject:owner];
-
-  if (animate)
-    _animationController->AnimateToolbarIn();
-  else
-    [_owner layoutToolbar];
-}
-
-- (void)releaseToolbarVisibilityForOwner:(id)owner withAnimation:(BOOL)animate {
-  if (![self isToolbarVisibilityLockedForOwner:owner])
-    return;
-
-  [_visibilityLocks removeObject:owner];
-
-  if (animate)
-    _animationController->AnimateToolbarOutIfPossible();
-  else
-    [_owner layoutToolbar];
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller_unittest.mm b/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller_unittest.mm
index 00d6c0b5..48460d5 100644
--- a/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller_unittest.mm
@@ -51,7 +51,7 @@
   MOCK_CONST_METHOD0(container_view, gfx::NativeView());
   MOCK_CONST_METHOD0(element_bounds, const gfx::RectF&());
   MOCK_CONST_METHOD0(IsRTL, bool());
-  const std::vector<autofill::Suggestion> GetSuggestions() override {
+  std::vector<autofill::Suggestion> GetSuggestions() const override {
     return suggestions_;
   }
 
diff --git a/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.cc b/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.cc
index a8f64098..825918f 100644
--- a/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.cc
+++ b/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.cc
@@ -6,8 +6,11 @@
 
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
+#include "chrome/browser/ui/tabs/tab_group_theme.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "components/tab_groups/tab_group_color.h"
 #include "components/tab_groups/tab_group_id.h"
@@ -29,42 +32,39 @@
   // constrained to a menu icon size.
   static constexpr int kIconSize = 14;
 
-  explicit TabGroupIconImageSource(
-      const tab_groups::TabGroupVisualData* visual_data);
+  TabGroupIconImageSource(Profile* profile,
+                          const tab_groups::TabGroupVisualData* visual_data);
   ~TabGroupIconImageSource() override;
 
  private:
-  SkColor GetColor();
-
   // gfx::CanvasImageSource overrides:
   void Draw(gfx::Canvas* canvas) override;
 
-  ui::NativeTheme* native_theme_;
+  Profile* profile_;
   const tab_groups::TabGroupVisualData* visual_data_;
 
   DISALLOW_COPY_AND_ASSIGN(TabGroupIconImageSource);
 };
 
 TabGroupIconImageSource::TabGroupIconImageSource(
+    Profile* profile,
     const tab_groups::TabGroupVisualData* visual_data)
     : CanvasImageSource(gfx::Size(kIconSize, kIconSize)),
-      native_theme_(ui::NativeTheme::GetInstanceForNativeUi()),
+      profile_(profile),
       visual_data_(visual_data) {}
 
 TabGroupIconImageSource::~TabGroupIconImageSource() = default;
 
-SkColor TabGroupIconImageSource::GetColor() {
-  const tab_groups::TabGroupColor color_data =
-      tab_groups::GetTabGroupColorSet().at(visual_data_->color());
-  return native_theme_->ShouldUseDarkColors() ? color_data.dark_theme_color
-                                              : color_data.light_theme_color;
-}
-
 void TabGroupIconImageSource::Draw(gfx::Canvas* canvas) {
+  const ui::ThemeProvider& tp =
+      ThemeService::GetThemeProviderForProfile(profile_);
+  const SkColor color =
+      tp.GetColor(GetTabGroupContextMenuColorId(visual_data_->color()));
+
   cc::PaintFlags flags;
   flags.setStyle(cc::PaintFlags::kFill_Style);
   flags.setAntiAlias(true);
-  flags.setColor(GetColor());
+  flags.setColor(color);
   canvas->DrawCircle(gfx::PointF(kIconSize / 2, kIconSize / 2), kIconSize / 2,
                      flags);
 }
@@ -94,7 +94,7 @@
       AddItemWithIcon(
           group_index, displayed_title,
           gfx::ImageSkia(std::make_unique<TabGroupIconImageSource>(
-                             tab_group->visual_data()),
+                             model_->profile(), tab_group->visual_data()),
                          gfx::Size(TabGroupIconImageSource::kIconSize,
                                    TabGroupIconImageSource::kIconSize)));
     }
diff --git a/chrome/browser/ui/tabs/tab_group_model.cc b/chrome/browser/ui/tabs/tab_group_model.cc
index 3aeb6eab..ca12b58 100644
--- a/chrome/browser/ui/tabs/tab_group_model.cc
+++ b/chrome/browser/ui/tabs/tab_group_model.cc
@@ -58,7 +58,7 @@
 tab_groups::TabGroupColorId TabGroupModel::GetNextColor() const {
   // Count the number of times each available color is used.
   std::map<tab_groups::TabGroupColorId, int> color_usage_counts;
-  for (const auto& id_color_pair : tab_groups::GetTabGroupColorSet())
+  for (const auto& id_color_pair : tab_groups::GetTabGroupColorLabelMap())
     color_usage_counts[id_color_pair.first] = 0;
   for (const auto& id_group_pair : groups_)
     color_usage_counts[id_group_pair.second->visual_data()->color()]++;
diff --git a/chrome/browser/ui/tabs/tab_group_theme.cc b/chrome/browser/ui/tabs/tab_group_theme.cc
new file mode 100644
index 0000000..8278018
--- /dev/null
+++ b/chrome/browser/ui/tabs/tab_group_theme.cc
@@ -0,0 +1,77 @@
+// 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 "chrome/browser/ui/tabs/tab_group_theme.h"
+
+#include <array>
+
+#include "base/containers/flat_map.h"
+#include "base/no_destructor.h"
+#include "chrome/browser/themes/theme_properties.h"
+
+using TP = ThemeProperties;
+using TabGroupColorId = tab_groups::TabGroupColorId;
+
+int GetTabGroupTabStripColorId(TabGroupColorId group_color_id,
+                               bool active_frame) {
+  static const base::NoDestructor<
+      base::flat_map<TabGroupColorId, std::array<int, 2>>>
+      group_id_map({
+          {TabGroupColorId::kGrey,
+           {TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_GREY,
+            TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_GREY}},
+          {TabGroupColorId::kBlue,
+           {TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_BLUE,
+            TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_BLUE}},
+          {TabGroupColorId::kRed,
+           {TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_RED,
+            TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_RED}},
+          {TabGroupColorId::kYellow,
+           {TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_YELLOW,
+            TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_YELLOW}},
+          {TabGroupColorId::kGreen,
+           {TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_GREEN,
+            TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_GREEN}},
+          {TabGroupColorId::kPink,
+           {TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_PINK,
+            TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_PINK}},
+          {TabGroupColorId::kPurple,
+           {TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_PURPLE,
+            TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_PURPLE}},
+          {TabGroupColorId::kCyan,
+           {TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_INACTIVE_CYAN,
+            TP::COLOR_TAB_GROUP_TABSTRIP_FRAME_ACTIVE_CYAN}},
+      });
+  return group_id_map->at(group_color_id)[active_frame];
+}
+
+int GetTabGroupDialogColorId(TabGroupColorId group_color_id) {
+  static const base::NoDestructor<base::flat_map<TabGroupColorId, int>>
+      group_id_map({
+          {TabGroupColorId::kGrey, TP::COLOR_TAB_GROUP_DIALOG_GREY},
+          {TabGroupColorId::kBlue, TP::COLOR_TAB_GROUP_DIALOG_BLUE},
+          {TabGroupColorId::kRed, TP::COLOR_TAB_GROUP_DIALOG_RED},
+          {TabGroupColorId::kYellow, TP::COLOR_TAB_GROUP_DIALOG_YELLOW},
+          {TabGroupColorId::kGreen, TP::COLOR_TAB_GROUP_DIALOG_GREEN},
+          {TabGroupColorId::kPink, TP::COLOR_TAB_GROUP_DIALOG_PINK},
+          {TabGroupColorId::kPurple, TP::COLOR_TAB_GROUP_DIALOG_PURPLE},
+          {TabGroupColorId::kCyan, TP::COLOR_TAB_GROUP_DIALOG_CYAN},
+      });
+  return group_id_map->at(group_color_id);
+}
+
+int GetTabGroupContextMenuColorId(TabGroupColorId group_color_id) {
+  static const base::NoDestructor<base::flat_map<TabGroupColorId, int>>
+      group_id_map({
+          {TabGroupColorId::kGrey, TP::COLOR_TAB_GROUP_CONTEXT_MENU_GREY},
+          {TabGroupColorId::kBlue, TP::COLOR_TAB_GROUP_CONTEXT_MENU_BLUE},
+          {TabGroupColorId::kRed, TP::COLOR_TAB_GROUP_CONTEXT_MENU_RED},
+          {TabGroupColorId::kYellow, TP::COLOR_TAB_GROUP_CONTEXT_MENU_YELLOW},
+          {TabGroupColorId::kGreen, TP::COLOR_TAB_GROUP_CONTEXT_MENU_GREEN},
+          {TabGroupColorId::kPink, TP::COLOR_TAB_GROUP_CONTEXT_MENU_PINK},
+          {TabGroupColorId::kPurple, TP::COLOR_TAB_GROUP_CONTEXT_MENU_PURPLE},
+          {TabGroupColorId::kCyan, TP::COLOR_TAB_GROUP_CONTEXT_MENU_CYAN},
+      });
+  return group_id_map->at(group_color_id);
+}
diff --git a/chrome/browser/ui/tabs/tab_group_theme.h b/chrome/browser/ui/tabs/tab_group_theme.h
new file mode 100644
index 0000000..918363b
--- /dev/null
+++ b/chrome/browser/ui/tabs/tab_group_theme.h
@@ -0,0 +1,17 @@
+// 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 CHROME_BROWSER_UI_TABS_TAB_GROUP_THEME_H_
+#define CHROME_BROWSER_UI_TABS_TAB_GROUP_THEME_H_
+
+#include "components/tab_groups/tab_group_color.h"
+
+int GetTabGroupTabStripColorId(tab_groups::TabGroupColorId group_color_id,
+                               bool active_frame);
+
+int GetTabGroupDialogColorId(tab_groups::TabGroupColorId group_color_id);
+
+int GetTabGroupContextMenuColorId(tab_groups::TabGroupColorId group_color_id);
+
+#endif  // CHROME_BROWSER_UI_TABS_TAB_GROUP_THEME_H_
diff --git a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
index 9b89fd6..ee5d9cd 100644
--- a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
@@ -3534,8 +3534,7 @@
 TEST_F(TabStripModelTest, CreateGroupSetsVisualData) {
   TestTabStripModelDelegate delegate;
   TabStripModel strip(&delegate, profile());
-  base::flat_map<tab_groups::TabGroupColorId, tab_groups::TabGroupColor>
-      all_colors = tab_groups::GetTabGroupColorSet();
+  tab_groups::ColorLabelMap all_colors = tab_groups::GetTabGroupColorLabelMap();
   PrepareTabs(&strip, all_colors.size() + 1);
 
   // Expect groups to cycle through the available color set.
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
index adfbdf3..1edc338 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
@@ -508,7 +508,7 @@
   layout_manager->set_cross_axis_alignment(
       views::BoxLayout::CrossAxisAlignment::kCenter);
 
-  std::vector<autofill::Suggestion> suggestions = controller->GetSuggestions();
+  std::vector<Suggestion> suggestions = controller->GetSuggestions();
 
   const gfx::ImageSkia icon = GetIconImage(suggestions[line_number()]);
 
diff --git a/chrome/browser/ui/views/first_run_dialog.cc b/chrome/browser/ui/views/first_run_dialog.cc
index 9cd1d70b..8e4e255 100644
--- a/chrome/browser/ui/views/first_run_dialog.cc
+++ b/chrome/browser/ui/views/first_run_dialog.cc
@@ -17,7 +17,7 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/strings/grit/components_strings.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
@@ -32,7 +32,7 @@
 #if defined(OS_WIN)
 #include "components/crash/content/app/breakpad_win.h"
 #elif defined(OS_LINUX)
-#include "components/crash/content/app/breakpad_linux.h"
+#include "components/crash/core/app/breakpad_linux.h"
 #endif
 
 namespace {
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h
index 05cac09..8fd7cc7 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h
@@ -16,7 +16,7 @@
 class Label;
 }
 
-@class FullscreenToolbarControllerViews;
+@class FullscreenToolbarController;
 
 class BrowserNonClientFrameViewMac : public BrowserNonClientFrameView {
  public:
@@ -77,7 +77,7 @@
 
   views::Label* window_title_ = nullptr;
 
-  base::scoped_nsobject<FullscreenToolbarControllerViews>
+  base::scoped_nsobject<FullscreenToolbarController>
       fullscreen_toolbar_controller_;
 
   DISALLOW_COPY_AND_ASSIGN(BrowserNonClientFrameViewMac);
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
index 97aec5d..617bcb6 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
@@ -12,7 +12,7 @@
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/ui/cocoa/fullscreen/fullscreen_menubar_tracker.h"
-#include "chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.h"
+#include "chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/view_ids.h"
@@ -61,8 +61,7 @@
                           base::Unretained(this), true));
   if (!base::FeatureList::IsEnabled(features::kImmersiveFullscreen)) {
     fullscreen_toolbar_controller_.reset(
-        [[FullscreenToolbarControllerViews alloc]
-            initWithBrowserView:browser_view]);
+        [[FullscreenToolbarController alloc] initWithBrowserView:browser_view]);
     [fullscreen_toolbar_controller_
         setToolbarStyle:GetUserPreferredToolbarStyle(
                             *show_fullscreen_toolbar_)];
diff --git a/chrome/browser/ui/views/tabs/color_picker_view.cc b/chrome/browser/ui/views/tabs/color_picker_view.cc
index ab8e1ac1..4e87b49 100644
--- a/chrome/browser/ui/views/tabs/color_picker_view.cc
+++ b/chrome/browser/ui/views/tabs/color_picker_view.cc
@@ -10,10 +10,14 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/containers/span.h"
+#include "chrome/browser/themes/theme_properties.h"
+#include "chrome/browser/ui/tabs/tab_group_theme.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "components/tab_groups/tab_group_color.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/pointer/touch_ui_controller.h"
+#include "ui/base/theme_provider.h"
 #include "ui/gfx/animation/tween.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
@@ -55,13 +59,13 @@
  public:
   ColorPickerElementView(
       base::RepeatingCallback<void(ColorPickerElementView*)> selected_callback,
-      SkColor background_color,
-      SkColor color,
+      const views::BubbleDialogDelegateView* bubble_view,
+      tab_groups::TabGroupColorId color_id,
       base::string16 color_name)
       : Button(this),
         selected_callback_(std::move(selected_callback)),
-        background_color_(background_color),
-        color_(color),
+        bubble_view_(bubble_view),
+        color_id_(color_id),
         color_name_(color_name) {
     DCHECK(selected_callback_);
 
@@ -87,8 +91,6 @@
     set_animate_on_state_change(true);
   }
 
-  SkColor color() const { return color_; }
-
   void SetSelected(bool selected) {
     if (selected_ == selected)
       return;
@@ -141,9 +143,12 @@
     // We should be a circle.
     DCHECK_EQ(bounds.width(), bounds.height());
 
+    const SkColor color =
+        GetThemeProvider()->GetColor(GetTabGroupDialogColorId(color_id_));
+
     cc::PaintFlags flags;
     flags.setStyle(cc::PaintFlags::kFill_Style);
-    flags.setColor(color_);
+    flags.setColor(color);
     flags.setAntiAlias(true);
     canvas->DrawCircle(bounds.CenterPoint(), bounds.width() / 2.0f, flags);
 
@@ -177,7 +182,7 @@
     flags.setStyle(cc::PaintFlags::kStroke_Style);
     flags.setStrokeWidth(kThickness);
     flags.setAntiAlias(true);
-    flags.setColor(background_color_);
+    flags.setColor(bubble_view_->color());
 
     gfx::RectF indicator_bounds(GetContentsBounds());
     indicator_bounds.Inset(gfx::InsetsF(kInset));
@@ -188,16 +193,16 @@
 
   const base::RepeatingCallback<void(ColorPickerElementView*)>
       selected_callback_;
-  const SkColor background_color_;
-  const SkColor color_;
+  const views::BubbleDialogDelegateView* bubble_view_;
+  const tab_groups::TabGroupColorId color_id_;
   const base::string16 color_name_;
   bool selected_ = false;
 };
 
 ColorPickerView::ColorPickerView(
-    base::span<const std::pair<SkColor, base::string16>> colors,
-    SkColor background_color,
-    SkColor initial_color,
+    const views::BubbleDialogDelegateView* bubble_view,
+    const TabGroupEditorBubbleView::Colors& colors,
+    tab_groups::TabGroupColorId initial_color_id,
     ColorSelectedCallback callback)
     : callback_(std::move(callback)) {
   DCHECK(!colors.empty());
@@ -209,8 +214,8 @@
     // views in our destructor, ensuring we outlive them.
     elements_.push_back(AddChildView(std::make_unique<ColorPickerElementView>(
         base::Bind(&ColorPickerView::OnColorSelected, base::Unretained(this)),
-        background_color, color.first, color.second)));
-    if (initial_color == color.first)
+        bubble_view, color.first, color.second)));
+    if (initial_color_id == color.first)
       elements_.back()->SetSelected(true);
   }
 
diff --git a/chrome/browser/ui/views/tabs/color_picker_view.h b/chrome/browser/ui/views/tabs/color_picker_view.h
index 8a3160a..e58d5766 100644
--- a/chrome/browser/ui/views/tabs/color_picker_view.h
+++ b/chrome/browser/ui/views/tabs/color_picker_view.h
@@ -11,10 +11,14 @@
 #include "base/callback.h"
 #include "base/containers/span.h"
 #include "base/optional.h"
+#include "chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h"
+#include "components/tab_groups/tab_group_color.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/view.h"
 
 namespace views {
 class Button;
+class BubbleDialogDelegateView;
 }
 
 class ColorPickerElementView;
@@ -25,13 +29,14 @@
 class ColorPickerView : public views::View {
  public:
   using ColorSelectedCallback = base::RepeatingCallback<void()>;
+
   // |colors| should contain the color values and accessible names. There should
   // not be duplicate colors.
-  explicit ColorPickerView(
-      base::span<const std::pair<SkColor, base::string16>> colors,
-      SkColor background_color,
-      SkColor initial_color,
-      ColorSelectedCallback callback);
+  explicit ColorPickerView(const views::BubbleDialogDelegateView* bubble_view,
+                           const TabGroupEditorBubbleView::Colors& colors,
+                           tab_groups::TabGroupColorId initial_color_id,
+                           ColorSelectedCallback callback);
+
   ~ColorPickerView() override;
 
   // Returns the index of the selected element, if any.
diff --git a/chrome/browser/ui/views/tabs/color_picker_view_unittest.cc b/chrome/browser/ui/views/tabs/color_picker_view_unittest.cc
index a572956..db0aafe 100644
--- a/chrome/browser/ui/views/tabs/color_picker_view_unittest.cc
+++ b/chrome/browser/ui/views/tabs/color_picker_view_unittest.cc
@@ -12,31 +12,44 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/mock_callback.h"
 #include "base/time/time.h"
+#include "chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h"
 #include "chrome/test/views/chrome_views_test_base.h"
+#include "components/tab_groups/tab_group_color.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/events/event.h"
 #include "ui/events/keycodes/dom/dom_codes.h"
 #include "ui/events/keycodes/keyboard_code_conversion.h"
 #include "ui/gfx/canvas.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/test/widget_test.h"
 #include "ui/views/widget/widget.h"
 
+namespace {
+
+static const TabGroupEditorBubbleView::Colors kTestColors({
+    {tab_groups::TabGroupColorId::kRed, base::ASCIIToUTF16("Red")},
+    {tab_groups::TabGroupColorId::kGreen, base::ASCIIToUTF16("Green")},
+    {tab_groups::TabGroupColorId::kBlue, base::ASCIIToUTF16("Blue")},
+});
+
+}  // namespace
+
 class ColorPickerViewTest : public ChromeViewsTestBase {
  protected:
-  static const std::vector<std::pair<SkColor, base::string16>> kTestColors;
-
   void SetUp() override {
     ChromeViewsTestBase::SetUp();
 
     widget_ = CreateTestWidget();
+    bubble_view_ = std::make_unique<views::BubbleDialogDelegateView>();
 
-    color_picker_ =
-        new ColorPickerView(kTestColors, SK_ColorWHITE, SK_ColorCYAN,
-                            color_selected_callback_.Get());
-    widget_->SetContentsView(color_picker_);
+    auto color_picker = std::make_unique<ColorPickerView>(
+        bubble_view(), kTestColors, tab_groups::TabGroupColorId::kBlue,
+        color_selected_callback_.Get());
+    color_picker_ = color_picker.get();
+    widget_->SetContentsView(color_picker.release());
 
     color_picker_->SizeToPreferredSize();
   }
@@ -67,6 +80,10 @@
     ClickColorElement(color_picker_->GetElementAtIndexForTesting(index));
   }
 
+  const views::BubbleDialogDelegateView* bubble_view() {
+    return bubble_view_.get();
+  }
+
   ::testing::NiceMock<
       base::MockCallback<ColorPickerView::ColorSelectedCallback>>
       color_selected_callback_;
@@ -74,35 +91,23 @@
 
  private:
   std::unique_ptr<views::Widget> widget_;
+  std::unique_ptr<views::BubbleDialogDelegateView> bubble_view_;
 };
 
-// static
-const std::vector<std::pair<SkColor, base::string16>>
-    ColorPickerViewTest::kTestColors =
-        std::vector<std::pair<SkColor, base::string16>>{
-            {SK_ColorRED, base::ASCIIToUTF16("Red")},
-            {SK_ColorGREEN, base::ASCIIToUTF16("Green")},
-            {SK_ColorBLUE, base::ASCIIToUTF16("Blue")},
-        };
-
-TEST_F(ColorPickerViewTest, NoColorSelectedByDefaultIfNotMatching) {
-  EXPECT_FALSE(color_picker_->GetSelectedElement().has_value());
-}
-
 TEST_F(ColorPickerViewTest, ColorSelectedByDefaultIfMatching) {
-  SkColor initial_color = SK_ColorRED;
-
   std::unique_ptr<views::Widget> widget = CreateTestWidget();
 
-  ColorPickerView* color_picker =
-      new ColorPickerView(kTestColors, SK_ColorWHITE, initial_color,
-                          color_selected_callback_.Get());
-  widget->SetContentsView(color_picker);
+  auto owned_color_picker = std::make_unique<ColorPickerView>(
+      bubble_view(), kTestColors, tab_groups::TabGroupColorId::kRed,
+      color_selected_callback_.Get());
+
+  ColorPickerView* color_picker = owned_color_picker.get();
+  widget->SetContentsView(owned_color_picker.release());
 
   color_picker->SizeToPreferredSize();
 
   EXPECT_TRUE(color_picker->GetSelectedElement().has_value());
-  // Expect the index to match that of SK_ColorRED in kTestColors.
+  // Expect the index to match that of TabGroupId::kRed.
   EXPECT_EQ(color_picker->GetSelectedElement().value(), 0);
 }
 
@@ -137,9 +142,9 @@
 TEST_F(ColorPickerViewTest, KeyboardFocusBehavesLikeRadioButtons) {
   views::FocusManager* focus_manager = color_picker_->GetFocusManager();
 
-  // When no color is selected, focus should start on the first.
+  // Focus should start at the selected element.
   focus_manager->AdvanceFocus(false);
-  EXPECT_EQ(color_picker_->GetElementAtIndexForTesting(0),
+  EXPECT_EQ(color_picker_->GetElementAtIndexForTesting(2),
             focus_manager->GetFocusedView());
 
   // Pressing arrow keys should cycle through the elements.
@@ -148,7 +153,7 @@
       ui::DomCodeToUsLayoutKeyboardCode(ui::DomCode::ARROW_RIGHT),
       ui::DomCode::ARROW_RIGHT, ui::EF_NONE);
   EXPECT_FALSE(focus_manager->OnKeyEvent(arrow_event));
-  EXPECT_EQ(color_picker_->GetElementAtIndexForTesting(1),
+  EXPECT_EQ(color_picker_->GetElementAtIndexForTesting(0),
             focus_manager->GetFocusedView());
 
   focus_manager->ClearFocus();
diff --git a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.cc
index 96c4548e..a782f51 100644
--- a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.cc
+++ b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.cc
@@ -169,10 +169,10 @@
       ->SetOrientation(views::LayoutOrientation::kVertical)
       .SetIgnoreDefaultMainAxisMargins(true);
 
-  const SkColor initial_color = InitColorSet();
+  const tab_groups::TabGroupColorId initial_color_id = InitColorSet();
   color_selector_ =
       group_modifier_container->AddChildView(std::make_unique<ColorPickerView>(
-          colors_, background_color(), initial_color,
+          this, colors_, initial_color_id,
           base::Bind(&TabGroupEditorBubbleView::UpdateGroup,
                      base::Unretained(this))));
   color_selector_->SetProperty(
@@ -236,35 +236,18 @@
 
 TabGroupEditorBubbleView::~TabGroupEditorBubbleView() = default;
 
-SkColor TabGroupEditorBubbleView::InitColorSet() {
-  base::flat_map<tab_groups::TabGroupColorId, tab_groups::TabGroupColor>
-      all_colors = tab_groups::GetTabGroupColorSet();
-  ui::NativeTheme* native_theme = ui::NativeTheme::GetInstanceForNativeUi();
+tab_groups::TabGroupColorId TabGroupEditorBubbleView::InitColorSet() {
+  const tab_groups::ColorLabelMap& color_map =
+      tab_groups::GetTabGroupColorLabelMap();
+
+  // TODO(tluk) remove the reliance on the ordering of the color pairs in the
+  // vector and use the ColorLabelMap structure instead.
+  std::copy(color_map.begin(), color_map.end(), std::back_inserter(colors_));
 
   // Keep track of the current group's color, to be returned as the initial
   // selected value.
-  const tab_groups::TabGroupColorId initial_color_id =
-      browser_->tab_strip_model()
-          ->group_model()
-          ->GetTabGroup(group_)
-          ->visual_data()
-          ->color();
-  SkColor initial_color;
-
-  color_ids_.reserve(all_colors.size());
-  colors_.reserve(all_colors.size());
-  for (auto const& color_pair : all_colors) {
-    color_ids_.push_back(color_pair.first);
-    SkColor color = native_theme->ShouldUseDarkColors()
-                        ? color_pair.second.dark_theme_color
-                        : color_pair.second.light_theme_color;
-    colors_.push_back({color, color_pair.second.label});
-
-    if (color_pair.first == initial_color_id)
-      initial_color = color;
-  }
-
-  return initial_color;
+  auto* const group_model = browser_->tab_strip_model()->group_model();
+  return group_model->GetTabGroup(group_)->visual_data()->color();
 }
 
 void TabGroupEditorBubbleView::UpdateGroup() {
@@ -275,7 +258,7 @@
   const tab_groups::TabGroupColorId current_color =
       tab_group->visual_data()->color();
   const tab_groups::TabGroupColorId updated_color =
-      selected_element.has_value() ? color_ids_[selected_element.value()]
+      selected_element.has_value() ? colors_[selected_element.value()].first
                                    : current_color;
 
   if (current_color != updated_color) {
diff --git a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h
index 06597c6..d01ea6f 100644
--- a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h
+++ b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h
@@ -8,6 +8,7 @@
 #include "base/optional.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/ui/views/tabs/tab_group_header.h"
+#include "components/tab_groups/tab_group_color.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/textfield/textfield.h"
@@ -32,6 +33,9 @@
   static constexpr int TAB_GROUP_HEADER_CXMENU_MOVE_GROUP_TO_NEW_WINDOW = 16;
   static constexpr int TAB_GROUP_HEADER_CXMENU_FEEDBACK = 17;
 
+  using Colors =
+      std::vector<std::pair<tab_groups::TabGroupColorId, base::string16>>;
+
   // Shows the editor for |group|. Returns a *non-owning* pointer to the
   // bubble's widget.
   static views::Widget* Show(
@@ -59,8 +63,6 @@
 
   void UpdateGroup();
 
-  SkColor background_color() const { return color(); }
-
   void OnBubbleClose();
 
   const Browser* const browser_;
@@ -121,8 +123,7 @@
 
   ButtonListener button_listener_;
 
-  std::vector<tab_groups::TabGroupColorId> color_ids_;
-  std::vector<std::pair<SkColor, base::string16>> colors_;
+  Colors colors_;
   ColorPickerView* color_selector_;
 
   // If true will use the |anchor_rect_| provided in the constructor, otherwise
@@ -131,7 +132,7 @@
 
   // Creates the set of tab group colors to display and returns the color that
   // is initially selected.
-  SkColor InitColorSet();
+  tab_groups::TabGroupColorId InitColorSet();
 
   base::string16 title_at_opening_;
 };
diff --git a/chrome/browser/ui/views/tabs/tab_group_header.cc b/chrome/browser/ui/views/tabs/tab_group_header.cc
index d8fdce31..46ec838a 100644
--- a/chrome/browser/ui/views/tabs/tab_group_header.cc
+++ b/chrome/browser/ui/views/tabs/tab_group_header.cc
@@ -90,8 +90,6 @@
   title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   title_->SetElideBehavior(gfx::FADE_TAIL);
 
-  VisualsChanged();
-
   // Enable keyboard focus.
   SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
   focus_ring_ = views::FocusRing::Install(this);
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 706a594..a5db2af 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/layout_constants.h"
+#include "chrome/browser/ui/tabs/tab_group_theme.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_types.h"
 #include "chrome/browser/ui/ui_features.h"
@@ -2025,14 +2026,8 @@
 
 SkColor TabStrip::GetPaintedGroupColor(
     const tab_groups::TabGroupColorId& color_id) const {
-  const tab_groups::TabGroupColor color_data =
-      tab_groups::GetTabGroupColorSet().at(color_id);
-
-  const SkColor background = GetTabBackgroundColor(
-      TabActive::kInactive, BrowserFrameActiveState::kUseCurrent);
-
-  return color_utils::IsDark(background) ? color_data.dark_theme_color
-                                         : color_data.light_theme_color;
+  return GetThemeProvider()->GetColor(
+      GetTabGroupTabStripColorId(color_id, ShouldPaintAsActiveFrame()));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/webui/crashes_ui.cc b/chrome/browser/ui/webui/crashes_ui.cc
index 4a5e20c..dbb7da1 100644
--- a/chrome/browser/ui/webui/crashes_ui.cc
+++ b/chrome/browser/ui/webui/crashes_ui.cc
@@ -43,7 +43,7 @@
 #endif
 
 #if defined(OS_LINUX)
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #endif
 
 using content::WebContents;
diff --git a/chrome/browser/ui/webui/media/media_feeds_ui.cc b/chrome/browser/ui/webui/media/media_feeds_ui.cc
index 505ce98..4e73a69 100644
--- a/chrome/browser/ui/webui/media/media_feeds_ui.cc
+++ b/chrome/browser/ui/webui/media/media_feeds_ui.cc
@@ -27,6 +27,11 @@
       content::WebUIDataSource::Create(chrome::kChromeUIMediaFeedsHost));
   source->AddResourcePath("media-feeds.js", IDR_MEDIA_FEEDS_JS);
   source->AddResourcePath(
+      "services/media_session/public/mojom/media_session.mojom-lite.js",
+      IDR_MEDIA_SESSION_MOJOM_LITE_JS);
+  source->AddResourcePath("ui/gfx/geometry/mojom/geometry.mojom-lite.js",
+                          IDR_UI_GEOMETRY_MOJOM_LITE_JS);
+  source->AddResourcePath(
       "chrome/browser/media/feeds/media_feeds_store.mojom-lite.js",
       IDR_MEDIA_FEEDS_STORE_MOJOM_LITE_JS);
   source->SetDefaultResource(IDR_MEDIA_FEEDS_HTML);
diff --git a/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.cc b/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.cc
new file mode 100644
index 0000000..e3c7e6d
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.cc
@@ -0,0 +1,32 @@
+// 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 "chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+
+namespace chromeos {
+namespace settings {
+
+AmbientModeHandler::AmbientModeHandler() = default;
+
+AmbientModeHandler::~AmbientModeHandler() = default;
+
+void AmbientModeHandler::RegisterMessages() {
+  web_ui()->RegisterMessageCallback(
+      "onAmbientModePageReady",
+      base::BindRepeating(&AmbientModeHandler::HandleInitialized,
+                          base::Unretained(this)));
+}
+
+void AmbientModeHandler::HandleInitialized(const base::ListValue* args) {
+  CHECK(args);
+  CHECK(args->empty());
+
+  AllowJavascript();
+}
+
+}  // namespace settings
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.h b/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.h
new file mode 100644
index 0000000..a52b109
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.h
@@ -0,0 +1,39 @@
+// 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 CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_AMBIENT_MODE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_AMBIENT_MODE_HANDLER_H_
+
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+namespace base {
+class ListValue;
+}  // namespace base
+
+namespace chromeos {
+namespace settings {
+
+// Chrome OS ambient mode settings page UI handler, to allow users to customize
+// photo frame and other related functionalities.
+class AmbientModeHandler : public ::settings::SettingsPageUIHandler {
+ public:
+  AmbientModeHandler();
+  AmbientModeHandler(const AmbientModeHandler&) = delete;
+  AmbientModeHandler& operator=(const AmbientModeHandler&) = delete;
+  ~AmbientModeHandler() override;
+
+  // settings::SettingsPageUIHandler:
+  void RegisterMessages() override;
+  void OnJavascriptAllowed() override {}
+  void OnJavascriptDisallowed() override {}
+
+ private:
+  // WebUI call to signal js side is ready.
+  void HandleInitialized(const base::ListValue* args);
+};
+
+}  // namespace settings
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_AMBIENT_MODE_HANDLER_H_
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.cc
index 02f197c..05da5cb 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.cc
@@ -423,6 +423,17 @@
 
 void AddPersonalizationStrings(content::WebUIDataSource* html_source) {
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
+      {"ambientModeTitle", IDS_OS_SETTINGS_AMBIENT_MODE_TITLE},
+      {"ambientModeEnabled", IDS_OS_SETTINGS_AMBIENT_MODE_ENABLED},
+      {"ambientModeDisabled", IDS_OS_SETTINGS_AMBIENT_MODE_DISABLED},
+      {"ambientModeOn", IDS_OS_SETTINGS_AMBIENT_MODE_ON},
+      {"ambientModeOff", IDS_OS_SETTINGS_AMBIENT_MODE_OFF},
+      {"ambientModeTopicSourceTitle",
+       IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_TITLE},
+      {"ambientModeTopicSourceGooglePhotos",
+       IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS},
+      {"ambientModeTopicSourceArtGallery",
+       IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY},
       {"changePictureTitle", IDS_OS_SETTINGS_CHANGE_PICTURE_TITLE},
       {"openWallpaperApp", IDS_OS_SETTINGS_OPEN_WALLPAPER_APP},
       {"personalizationPageTitle", IDS_OS_SETTINGS_PERSONALIZATION},
@@ -451,6 +462,8 @@
   html_source->AddBoolean(
       "changePictureVideoModeEnabled",
       base::FeatureList::IsEnabled(::features::kChangePictureVideoMode));
+  html_source->AddBoolean("isAmbientModeEnabled",
+                          chromeos::features::IsAmbientModeEnabled());
 }
 
 void AddFingerprintListStrings(content::WebUIDataSource* html_source) {
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
index be78887..5d5bf1e9 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
@@ -46,6 +46,7 @@
 #include "chrome/browser/ui/webui/settings/browser_lifetime_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/android_apps_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/crostini_handler.h"
@@ -384,6 +385,11 @@
           std::make_unique<chromeos::settings::ParentalControlsHandler>(
               profile));
     }
+
+    if (chromeos::features::IsAmbientModeEnabled()) {
+      web_ui()->AddMessageHandler(
+          std::make_unique<chromeos::settings::AmbientModeHandler>());
+    }
   }
 
   html_source->AddBoolean(
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index fa641d7e..1c2d6ba9 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -845,6 +845,10 @@
       {"noExceptionsFound", IDS_SETTINGS_PASSWORDS_EXCEPTIONS_NONE},
       {"import", IDS_PASSWORD_MANAGER_IMPORT_BUTTON},
       {"exportMenuItem", IDS_SETTINGS_PASSWORDS_EXPORT_MENU_ITEM},
+      {"optInAccountStorageLabel",
+       IDS_SETTINGS_PASSWORDS_OPT_IN_ACCOUNT_STORAGE_LABEL},
+      {"optOutAccountStorageLabel",
+       IDS_SETTINGS_PASSWORDS_OPT_OUT_ACCOUNT_STORAGE_LABEL},
       {"undoRemovePassword", IDS_SETTINGS_PASSWORD_UNDO},
       {"passwordDeleted", IDS_SETTINGS_PASSWORD_DELETED_PASSWORD},
       {"passwordRowMoreActionsButton", IDS_SETTINGS_PASSWORD_ROW_MORE_ACTIONS},
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index 5af6118d..53bb621 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -269,6 +269,11 @@
                               password_manager::features::kPasswordImport));
 
   html_source->AddBoolean(
+      "enableAccountStorage",
+      base::FeatureList::IsEnabled(
+          password_manager::features::kEnablePasswordsAccountStorage));
+
+  html_source->AddBoolean(
       "syncSetupFriendlySettings",
       base::FeatureList::IsEnabled(features::kSyncSetupFriendlySettings));
 
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui_handler.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_ui_handler.cc
index c57474f..3a0837a 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui_handler.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui_handler.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
+#include "chrome/browser/ui/tabs/tab_group_theme.h"
 #include "chrome/browser/ui/tabs/tab_menu_model.h"
 #include "chrome/browser/ui/tabs/tab_renderer_data.h"
 #include "chrome/browser/ui/tabs/tab_utils.h"
@@ -471,12 +472,11 @@
   base::DictionaryValue visual_data_dict;
   visual_data_dict.SetString("title", visual_data->title());
 
-  bool is_dark_frame = color_utils::IsDark(
-      embedder_->GetColor(ThemeProperties::COLOR_FRAME_ACTIVE));
-  const tab_groups::TabGroupColor tab_group_color =
-      tab_groups::GetTabGroupColorSet().at(visual_data->color());
-  const SkColor group_color = is_dark_frame ? tab_group_color.dark_theme_color
-                                            : tab_group_color.light_theme_color;
+  // TODO the tab strip should support toggles between inactive and active frame
+  // states. Currently the webui tab strip only uses active frame colors
+  // (https://crbug.com/1060398).
+  const int color_id = GetTabGroupTabStripColorId(visual_data->color(), true);
+  const SkColor group_color = embedder_->GetColor(color_id);
   visual_data_dict.SetString("color",
                              color_utils::SkColorToRgbString(group_color));
   visual_data_dict.SetString(
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui_handler_unittest.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_ui_handler_unittest.cc
index 7eddb6f..1e37827 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui_handler_unittest.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui_handler_unittest.cc
@@ -82,10 +82,7 @@
 
     std::string group_color;
     ASSERT_TRUE(visual_data_dict->GetString("color", &group_color));
-    EXPECT_EQ(color_utils::SkColorToRgbString(tab_groups::GetTabGroupColorSet()
-                                                  .at(visual_data.color())
-                                                  .light_theme_color),
-              group_color);
+    EXPECT_EQ(color_utils::SkColorToRgbString(SK_ColorWHITE), group_color);
   }
 
  private:
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 42056e2..fcfdc3d 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -93,6 +93,7 @@
     ":web_app_group",
     "//chrome/browser/web_applications/components",
     "//chrome/common",
+    "//components/performance_manager",
     "//components/sync",
     "//content/public/browser",
     "//skia",
diff --git a/chrome/browser/web_applications/pending_app_install_task.cc b/chrome/browser/web_applications/pending_app_install_task.cc
index 9cc3b6f..5504fe5 100644
--- a/chrome/browser/web_applications/pending_app_install_task.cc
+++ b/chrome/browser/web_applications/pending_app_install_task.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
 #include "chrome/browser/web_applications/components/web_app_ui_manager.h"
 #include "chrome/common/web_application_info.h"
+#include "components/performance_manager/embedder/performance_manager_registry.h"
 #include "content/public/browser/browser_thread.h"
 
 namespace web_app {
@@ -45,6 +46,10 @@
   InstallableManager::CreateForWebContents(web_contents);
   SecurityStateTabHelper::CreateForWebContents(web_contents);
   favicon::CreateContentFaviconDriverForWebContents(web_contents);
+  if (auto* performance_manager_registry =
+          performance_manager::PerformanceManagerRegistry::GetInstance()) {
+    performance_manager_registry->CreatePageNodeForWebContents(web_contents);
+  }
 }
 
 PendingAppInstallTask::PendingAppInstallTask(
diff --git a/chrome/browser/web_applications/web_app_install_task.cc b/chrome/browser/web_applications/web_app_install_task.cc
index e66f1cc2..d34e0dd 100644
--- a/chrome/browser/web_applications/web_app_install_task.cc
+++ b/chrome/browser/web_applications/web_app_install_task.cc
@@ -27,6 +27,7 @@
 #include "chrome/browser/web_applications/components/web_app_url_loader.h"
 #include "chrome/browser/web_applications/components/web_app_utils.h"
 #include "chrome/common/web_application_info.h"
+#include "components/performance_manager/embedder/performance_manager_registry.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 #include "third_party/blink/public/common/manifest/manifest.h"
@@ -304,6 +305,11 @@
   InstallableManager::CreateForWebContents(web_contents.get());
   SecurityStateTabHelper::CreateForWebContents(web_contents.get());
   favicon::CreateContentFaviconDriverForWebContents(web_contents.get());
+  if (auto* performance_manager_registry =
+          performance_manager::PerformanceManagerRegistry::GetInstance()) {
+    performance_manager_registry->CreatePageNodeForWebContents(
+        web_contents.get());
+  }
 
   return web_contents;
 }
diff --git a/chrome/child/BUILD.gn b/chrome/child/BUILD.gn
index a6f9c65..f877dad 100644
--- a/chrome/child/BUILD.gn
+++ b/chrome/child/BUILD.gn
@@ -19,7 +19,7 @@
       "v8_crashpad_support_win.h",
     ]
     deps += [
-      "//components/crash/content/app:crash_export_thunk_include",
+      "//components/crash/core/app:crash_export_thunk_include",
       "//gin",
       "//v8",
     ]
diff --git a/chrome/child/DEPS b/chrome/child/DEPS
index 95f096e..696396ff 100644
--- a/chrome/child/DEPS
+++ b/chrome/child/DEPS
@@ -1,5 +1,5 @@
 include_rules = [
-  "+components/crash/content/app",
+  "+components/crash/core/app",
   "+content/public/child",
   "+gin/public",
   "+v8",
diff --git a/chrome/child/v8_crashpad_support_win.cc b/chrome/child/v8_crashpad_support_win.cc
index e110b18..916520b 100644
--- a/chrome/child/v8_crashpad_support_win.cc
+++ b/chrome/child/v8_crashpad_support_win.cc
@@ -5,7 +5,7 @@
 #include "chrome/child/v8_crashpad_support_win.h"
 
 #include "build/build_config.h"
-#include "components/crash/content/app/crash_export_thunks.h"
+#include "components/crash/core/app/crash_export_thunks.h"
 #include "gin/public/debug.h"
 
 namespace v8_crashpad_support {
diff --git a/chrome/chrome_elf/BUILD.gn b/chrome/chrome_elf/BUILD.gn
index de079e9..9b08e23e 100644
--- a/chrome/chrome_elf/BUILD.gn
+++ b/chrome/chrome_elf/BUILD.gn
@@ -74,7 +74,7 @@
     ":third_party_dlls",
     "//chrome/install_static:install_static_util",
     "//chrome/install_static:primary_module",
-    "//components/crash/content/app:crash_export_thunks",
+    "//components/crash/core/app:crash_export_thunks",
   ]
 
   configs += [ "//build/config/win:windowed" ]
@@ -115,7 +115,7 @@
     "//base",  # This needs to go.  DEP of app, crash_keys, client.
     "//base:base_static",  # pe_image
     "//chrome/install_static:install_static_util",
-    "//components/crash/content/app",
+    "//components/crash/core/app",
     "//components/crash/core/common",  # crash_keys
     "//components/version_info:channel",
     "//content/public/common:result_codes",
@@ -270,7 +270,7 @@
     "//chrome/common:version_header",
     "//chrome/install_static:install_static_util",
     "//chrome/install_static/test:test_support",
-    "//components/crash/content/app:test_support",
+    "//components/crash/core/app:test_support",
     "//sandbox",
     "//testing/gtest",
   ]
@@ -335,6 +335,6 @@
     "//base/test:test_support",
     "//build/win:default_exe_manifest",
     "//chrome/install_static:install_static_util",
-    "//components/crash/content/app:crash_export_stubs",
+    "//components/crash/core/app:crash_export_stubs",
   ]
 }
diff --git a/chrome/chrome_elf/DEPS b/chrome/chrome_elf/DEPS
index 7e168ba..bb2601f7 100644
--- a/chrome/chrome_elf/DEPS
+++ b/chrome/chrome_elf/DEPS
@@ -3,7 +3,7 @@
   "+chrome/chrome_elf",
   "+chrome/common/chrome_switches.h",
   "+chrome/install_static",
-  "+components/crash/content/app/crashpad.h",
+  "+components/crash/core/app/crashpad.h",
   "+components/crash/core/common/crash_keys.h",
   "+sandbox",
   "+third_party/breakpad/breakpad/src/client",
diff --git a/chrome/chrome_elf/chrome_elf_arm64.def b/chrome/chrome_elf/chrome_elf_arm64.def
index 1cdc2c2..56fd82b8 100644
--- a/chrome/chrome_elf/chrome_elf_arm64.def
+++ b/chrome/chrome_elf/chrome_elf_arm64.def
@@ -7,7 +7,7 @@
   ; When functions are added to this file, they must also be added to
   ; chrome_elf_x86.def and chrome_elf_x64.def
 
-  ; From components/crash/content/app/crash_export_stubs.cc
+  ; From components/crash/core/app/crash_export_stubs.cc
   CrashForException_ExportThunk
   DumpHungProcessWithPtype_ExportThunk
   GetCrashpadDatabasePath_ExportThunk
diff --git a/chrome/chrome_elf/chrome_elf_x64.def b/chrome/chrome_elf/chrome_elf_x64.def
index bf953ec..1def747 100644
--- a/chrome/chrome_elf/chrome_elf_x64.def
+++ b/chrome/chrome_elf/chrome_elf_x64.def
@@ -7,7 +7,7 @@
   ; When functions are added to this file, they must also be added to
   ; chrome_elf_x86.def and chrome_elf_arm64.def
 
-  ; From components/crash/content/app/crash_export_stubs.cc
+  ; From components/crash/core/app/crash_export_stubs.cc
   CrashForException_ExportThunk
   DumpHungProcessWithPtype_ExportThunk
   GetCrashpadDatabasePath_ExportThunk
diff --git a/chrome/chrome_elf/chrome_elf_x86.def b/chrome/chrome_elf/chrome_elf_x86.def
index 71118659..de35356 100644
--- a/chrome/chrome_elf/chrome_elf_x86.def
+++ b/chrome/chrome_elf/chrome_elf_x86.def
@@ -7,7 +7,7 @@
   ; When functions are added to this file, they must also be added to
   ; chrome_elf_x64.def and chrome_elf_arm64.def
 
-  ; From components/crash/content/app/crash_export_stubs.cc
+  ; From components/crash/core/app/crash_export_stubs.cc
   CrashForException_ExportThunk
   DumpHungProcessWithPtype_ExportThunk
   GetCrashpadDatabasePath_ExportThunk
diff --git a/chrome/chrome_elf/crash/crash_helper.cc b/chrome/chrome_elf/crash/crash_helper.cc
index a02aa37c..42a4bfc 100644
--- a/chrome/chrome_elf/crash/crash_helper.cc
+++ b/chrome/chrome_elf/crash/crash_helper.cc
@@ -13,7 +13,7 @@
 
 #include "chrome/app/chrome_crash_reporter_client_win.h"
 #include "chrome/chrome_elf/hook_util/hook_util.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/crash/core/common/crash_keys.h"
 #include "third_party/crashpad/crashpad/client/crashpad_client.h"
 
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 0ee5857b..03336cf 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -267,7 +267,7 @@
 
   deps = [
     "//build:branding_buildflags",
-    "//components/crash/content/app",
+    "//components/crash/core/app",
     "//components/google/core/common",
     "//components/metrics:call_stack_profile_builder",
     "//components/page_load_metrics/common:common",
@@ -435,7 +435,7 @@
     deps += [
       "//chrome/chrome_elf:chrome_elf_main_include",
       "//chrome/common/win:eventlog_messages",
-      "//components/crash/content/app:crash_export_thunk_include",
+      "//components/crash/core/app:crash_export_thunk_include",
     ]
 
     public_deps += [
diff --git a/chrome/common/DEPS b/chrome/common/DEPS
index 0011da4..2964bc1 100644
--- a/chrome/common/DEPS
+++ b/chrome/common/DEPS
@@ -11,7 +11,7 @@
   "+components/cdm/common",
   "+components/cloud_devices/common",
   "+components/content_settings/core/common",
-  "+components/crash/content/app",
+  "+components/crash/core/app",
   "+components/crash/core/common",
   "+components/crx_file",
   "+components/data_reduction_proxy/core/common",
diff --git a/chrome/common/crash_keys.cc b/chrome/common/crash_keys.cc
index 4b9d1c3..f9a6c6b 100644
--- a/chrome/common/crash_keys.cc
+++ b/chrome/common/crash_keys.cc
@@ -20,7 +20,7 @@
 
 #if defined(OS_CHROMEOS)
 #include "chrome/common/chrome_switches.h"
-#include "components/crash/content/app/crash_switches.h"
+#include "components/crash/core/app/crash_switches.h"
 #include "gpu/command_buffer/service/gpu_switches.h"
 #include "ui/gl/gl_switches.h"
 #endif
diff --git a/chrome/common/extensions/api/passwords_private.idl b/chrome/common/extensions/api/passwords_private.idl
index e0def107..c4e4ec7 100644
--- a/chrome/common/extensions/api/passwords_private.idl
+++ b/chrome/common/extensions/api/passwords_private.idl
@@ -73,10 +73,6 @@
     // The username used in conjunction with the saved password.
     DOMString username;
 
-    // The number of characters in the password; used to display placeholder
-    // dots in the UI.
-    long numCharactersInPassword;
-
     // Text shown if the password was obtained via a federated identity.
     DOMString? federationText;
 
diff --git a/chrome/common/service_process_util.cc b/chrome/common/service_process_util.cc
index dfaa1b3..3c48f84111 100644
--- a/chrome/common/service_process_util.cc
+++ b/chrome/common/service_process_util.cc
@@ -28,8 +28,8 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "components/cloud_devices/common/cloud_devices_switches.h"
-#include "components/crash/content/app/crashpad.h"
-#include "components/crash/content/app/crash_switches.h"
+#include "components/crash/core/app/crash_switches.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/network_session_configurator/common/network_switches.h"
 #include "components/version_info/version_info.h"
 #include "content/public/common/content_paths.h"
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 1301c421..9d4f930 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -392,6 +392,7 @@
 const char kAccessibilitySubPage[] = "accessibility";
 const char kAccountManagerSubPage[] = "accountManager";
 const char kAccountSubPage[] = "accounts";
+const char kAmbientModeSubPage[] = "ambientMode";
 const char kAndroidAppsDetailsSubPage[] = "androidAppsDetails";
 const char kAndroidAppsDetailsSubPageInBrowserSettings[] =
     "androidApps/details";
@@ -445,6 +446,7 @@
       kAccessibilitySubPage,
       kAccountManagerSubPage,
       kAccountSubPage,
+      kAmbientModeSubPage,
       kAndroidAppsDetailsSubPage,
       kAndroidAppsDetailsSubPageInBrowserSettings,
       kAppManagementDetailSubPage,
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 120747cb..3976e0b 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -348,6 +348,7 @@
 extern const char kAccessibilitySubPage[];
 extern const char kAccountManagerSubPage[];
 extern const char kAccountSubPage[];
+extern const char kAmbientModeSubPage[];
 extern const char kAndroidAppsDetailsSubPage[];
 extern const char kAndroidAppsDetailsSubPageInBrowserSettings[];
 extern const char kAppManagementDetailSubPage[];
diff --git a/chrome/credential_provider/gaiacp/BUILD.gn b/chrome/credential_provider/gaiacp/BUILD.gn
index 1c3a54b..6190e9e 100644
--- a/chrome/credential_provider/gaiacp/BUILD.gn
+++ b/chrome/credential_provider/gaiacp/BUILD.gn
@@ -118,9 +118,9 @@
     "//chrome/common:non_code_constants",
     "//chrome/common:version_header",
     "//chrome/installer/launcher_support",
-    "//components/crash/content/app:app",
-    "//components/crash/content/app:crash_export_thunks",
-    "//components/crash/content/app:run_as_crashpad_handler",
+    "//components/crash/core/app:app",
+    "//components/crash/core/app:crash_export_thunks",
+    "//components/crash/core/app:run_as_crashpad_handler",
     "//third_party/boringssl",
     "//third_party/re2",
   ]
diff --git a/chrome/credential_provider/gaiacp/dllmain.cc b/chrome/credential_provider/gaiacp/dllmain.cc
index 2dcf68a..323bfb7 100644
--- a/chrome/credential_provider/gaiacp/dllmain.cc
+++ b/chrome/credential_provider/gaiacp/dllmain.cc
@@ -38,8 +38,8 @@
 #include "chrome/credential_provider/gaiacp/os_user_manager.h"
 #include "chrome/credential_provider/gaiacp/reauth_credential.h"
 #include "chrome/credential_provider/gaiacp/reg_utils.h"
-#include "components/crash/content/app/crash_switches.h"
-#include "components/crash/content/app/run_as_crashpad_handler_win.h"
+#include "components/crash/core/app/crash_switches.h"
+#include "components/crash/core/app/run_as_crashpad_handler_win.h"
 #include "content/public/common/content_switches.h"
 
 using credential_provider::putHR;
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider_module.cc b/chrome/credential_provider/gaiacp/gaia_credential_provider_module.cc
index d9886b311..97f3627 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_provider_module.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_provider_module.cc
@@ -22,7 +22,7 @@
 #include "chrome/credential_provider/gaiacp/logging.h"
 #include "chrome/credential_provider/gaiacp/mdm_utils.h"
 #include "chrome/credential_provider/gaiacp/reg_utils.h"
-#include "components/crash/content/app/crash_switches.h"
+#include "components/crash/core/app/crash_switches.h"
 #include "content/public/common/content_switches.h"
 
 namespace credential_provider {
diff --git a/chrome/credential_provider/gaiacp/gcp_crash_reporter_client.h b/chrome/credential_provider/gaiacp/gcp_crash_reporter_client.h
index 5aca0bb..e53998ec 100644
--- a/chrome/credential_provider/gaiacp/gcp_crash_reporter_client.h
+++ b/chrome/credential_provider/gaiacp/gcp_crash_reporter_client.h
@@ -6,7 +6,7 @@
 #define CHROME_CREDENTIAL_PROVIDER_GAIACP_GCP_CRASH_REPORTER_CLIENT_H_
 
 #include "base/macros.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 
 namespace base {
 class FilePath;
diff --git a/chrome/credential_provider/gaiacp/gcp_crash_reporting.cc b/chrome/credential_provider/gaiacp/gcp_crash_reporting.cc
index 8b9e4ad..c6d9379 100644
--- a/chrome/credential_provider/gaiacp/gcp_crash_reporting.cc
+++ b/chrome/credential_provider/gaiacp/gcp_crash_reporting.cc
@@ -14,7 +14,7 @@
 #include "chrome/credential_provider/gaiacp/gcp_crash_reporting_utils.h"
 #include "chrome/credential_provider/gaiacp/gcp_utils.h"
 #include "chrome/credential_provider/gaiacp/logging.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #include "content/public/common/content_switches.h"
 
 namespace {
diff --git a/chrome/credential_provider/setup/BUILD.gn b/chrome/credential_provider/setup/BUILD.gn
index 182c87b..9f7be20 100644
--- a/chrome/credential_provider/setup/BUILD.gn
+++ b/chrome/credential_provider/setup/BUILD.gn
@@ -68,9 +68,9 @@
     "../gaiacp:common",
     "//build:branding_buildflags",
     "//chrome/common:version_header",
-    "//components/crash/content/app:app",
-    "//components/crash/content/app:crash_export_thunks",
-    "//components/crash/content/app:run_as_crashpad_handler",
+    "//components/crash/core/app:app",
+    "//components/crash/core/app:crash_export_thunks",
+    "//components/crash/core/app:run_as_crashpad_handler",
     "//components/crash/core/common",
     "//content/public/common:static_switches",
   ]
diff --git a/chrome/credential_provider/setup/gcp_installer_crash_reporting.cc b/chrome/credential_provider/setup/gcp_installer_crash_reporting.cc
index acbabe55..3b38783 100644
--- a/chrome/credential_provider/setup/gcp_installer_crash_reporting.cc
+++ b/chrome/credential_provider/setup/gcp_installer_crash_reporting.cc
@@ -19,7 +19,7 @@
 #include "chrome/credential_provider/gaiacp/gcp_crash_reporter_client.h"
 #include "chrome/credential_provider/gaiacp/gcp_crash_reporting_utils.h"
 #include "chrome/credential_provider/setup/setup_lib.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/crash/core/common/crash_key.h"
 #include "components/crash/core/common/crash_keys.h"
 
diff --git a/chrome/credential_provider/setup/setup.cc b/chrome/credential_provider/setup/setup.cc
index 62c4259..04f2446 100644
--- a/chrome/credential_provider/setup/setup.cc
+++ b/chrome/credential_provider/setup/setup.cc
@@ -39,8 +39,8 @@
 #include "chrome/credential_provider/gaiacp/reg_utils.h"
 #include "chrome/credential_provider/setup/gcp_installer_crash_reporting.h"
 #include "chrome/credential_provider/setup/setup_lib.h"
-#include "components/crash/content/app/crash_switches.h"
-#include "components/crash/content/app/run_as_crashpad_handler_win.h"
+#include "components/crash/core/app/crash_switches.h"
+#include "components/crash/core/app/run_as_crashpad_handler_win.h"
 #include "content/public/common/content_switches.h"
 
 using credential_provider::GetGlobalFlagOrDefault;
diff --git a/chrome/installer/setup/BUILD.gn b/chrome/installer/setup/BUILD.gn
index f2ec4e242..08a7f46 100644
--- a/chrome/installer/setup/BUILD.gn
+++ b/chrome/installer/setup/BUILD.gn
@@ -38,9 +38,9 @@
       "//chrome/common:constants",
       "//chrome/install_static:install_static_util",
       "//chrome/installer/util:with_rc_strings",
-      "//components/crash/content/app:app",
-      "//components/crash/content/app:crash_export_thunks",
-      "//components/crash/content/app:run_as_crashpad_handler",
+      "//components/crash/core/app:app",
+      "//components/crash/core/app:crash_export_thunks",
+      "//components/crash/core/app:run_as_crashpad_handler",
       "//content/public/common:static_switches",
       "//rlz:rlz_lib_no_network",
     ]
@@ -98,7 +98,7 @@
       "//chrome/common:version_header",
       "//chrome/install_static:install_static_util",
       "//components/base32",
-      "//components/crash/content/app",
+      "//components/crash/core/app",
       "//components/crash/core/common",
       "//components/metrics",
       "//components/zucchini:zucchini_io",
@@ -150,7 +150,7 @@
       "//chrome/installer/mini_installer:unit_tests",
       "//chrome/installer/util:serial_unittests",
       "//chrome/installer/util:test_support",
-      "//components/crash/content/app:test_support",
+      "//components/crash/core/app:test_support",
       "//testing/gmock",
       "//testing/gtest",
     ]
diff --git a/chrome/installer/setup/installer_crash_reporter_client.h b/chrome/installer/setup/installer_crash_reporter_client.h
index ad7c284b..8584256f 100644
--- a/chrome/installer/setup/installer_crash_reporter_client.h
+++ b/chrome/installer/setup/installer_crash_reporter_client.h
@@ -8,7 +8,7 @@
 #include <stddef.h>
 
 #include "base/macros.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 
 class InstallerCrashReporterClient
     : public crash_reporter::CrashReporterClient {
diff --git a/chrome/installer/setup/installer_crash_reporting.cc b/chrome/installer/setup/installer_crash_reporting.cc
index f3c5dd1e..daf843c 100644
--- a/chrome/installer/setup/installer_crash_reporting.cc
+++ b/chrome/installer/setup/installer_crash_reporting.cc
@@ -22,7 +22,7 @@
 #include "chrome/installer/setup/installer_crash_reporter_client.h"
 #include "chrome/installer/setup/installer_state.h"
 #include "chrome/installer/util/google_update_settings.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/crash/core/common/crash_key.h"
 #include "components/crash/core/common/crash_keys.h"
 
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc
index 2ea9e43..8977ca8 100644
--- a/chrome/installer/setup/setup_main.cc
+++ b/chrome/installer/setup/setup_main.cc
@@ -86,8 +86,8 @@
 #include "chrome/installer/util/self_cleaning_temp_dir.h"
 #include "chrome/installer/util/shell_util.h"
 #include "chrome/installer/util/util_constants.h"
-#include "components/crash/content/app/crash_switches.h"
-#include "components/crash/content/app/run_as_crashpad_handler_win.h"
+#include "components/crash/core/app/crash_switches.h"
+#include "components/crash/core/app/run_as_crashpad_handler_win.h"
 #include "content/public/common/content_switches.h"
 
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
diff --git a/chrome/notification_helper/BUILD.gn b/chrome/notification_helper/BUILD.gn
index aae3153..21828dd 100644
--- a/chrome/notification_helper/BUILD.gn
+++ b/chrome/notification_helper/BUILD.gn
@@ -25,8 +25,8 @@
     "//build/win:default_exe_manifest",
     "//chrome/common:version_header",
     "//chrome/install_static:primary_module",
-    "//components/crash/content/app",
-    "//components/crash/content/app:crash_export_thunks",
+    "//components/crash/core/app",
+    "//components/crash/core/app:crash_export_thunks",
     "//components/version_info:channel",
   ]
 
diff --git a/chrome/notification_helper/DEPS b/chrome/notification_helper/DEPS
index 0eef3f0..45adb4c 100644
--- a/chrome/notification_helper/DEPS
+++ b/chrome/notification_helper/DEPS
@@ -1,8 +1,8 @@
 include_rules = [
   "+chrome/install_static",
   "+chrome/installer/util",
-  "+components/crash/content/app/crashpad.h",
-  "+components/crash/content/app/crash_reporter_client.h",
+  "+components/crash/core/app/crashpad.h",
+  "+components/crash/core/app/crash_reporter_client.h",
   "+components/version_info/channel.h",
 ]
 
diff --git a/chrome/notification_helper/notification_helper_crash_reporter_client.cc b/chrome/notification_helper/notification_helper_crash_reporter_client.cc
index 8ad401ed..504f71f 100644
--- a/chrome/notification_helper/notification_helper_crash_reporter_client.cc
+++ b/chrome/notification_helper/notification_helper_crash_reporter_client.cc
@@ -12,7 +12,7 @@
 #include "chrome/common/chrome_version.h"
 #include "chrome/install_static/install_util.h"
 #include "chrome/install_static/user_data_dir.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/version_info/channel.h"
 
 NotificationHelperCrashReporterClient::NotificationHelperCrashReporterClient() =
diff --git a/chrome/notification_helper/notification_helper_crash_reporter_client.h b/chrome/notification_helper/notification_helper_crash_reporter_client.h
index e39d1ee2..014afdc 100644
--- a/chrome/notification_helper/notification_helper_crash_reporter_client.h
+++ b/chrome/notification_helper/notification_helper_crash_reporter_client.h
@@ -7,7 +7,7 @@
 
 #include "base/files/file_path.h"
 #include "base/macros.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 
 class NotificationHelperCrashReporterClient
     : public crash_reporter::CrashReporterClient {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 86789d5..c83b39c 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -272,7 +272,7 @@
     ]
     deps += [ "//build:branding_buildflags" ]
     public_deps += [
-      "//components/crash/content/app",
+      "//components/crash/core/app",
       "//third_party/breakpad",
     ]
   }
@@ -281,7 +281,7 @@
     sources += [ "//chrome/app/chrome_crash_reporter_client_win.cc" ]
     public_deps += [
       "//chrome/install_static/test:test_support",
-      "//components/crash/content/app",
+      "//components/crash/core/app",
       "//third_party/wtl",
     ]
   }
@@ -3183,6 +3183,7 @@
     "../browser/net/chrome_network_delegate_unittest.cc",
     "../browser/net/dns_probe_runner_unittest.cc",
     "../browser/net/dns_probe_service_factory_unittest.cc",
+    "../browser/net/dns_util_unittest.cc",
     "../browser/net/file_downloader_unittest.cc",
     "../browser/net/net_error_tab_helper_unittest.cc",
     "../browser/net/probe_message_unittest.cc",
@@ -5937,7 +5938,6 @@
       # excluded from builds with sanitizer. See https://crbug.com/1057868.
       if (!using_sanitizer) {
         sources += [
-          "../browser/ui/ash/drag_to_overview_interactive_uitest.cc",
           "../browser/ui/ash/homescreen_interactive_uitest.cc",
           "../browser/ui/ash/launcher_animations_interactive_uitest.cc",
           "../browser/ui/ash/launcher_drag_interactive_uitest.cc",
diff --git a/chrome/test/DEPS b/chrome/test/DEPS
index 9cb820c..2e20ad7 100644
--- a/chrome/test/DEPS
+++ b/chrome/test/DEPS
@@ -13,7 +13,7 @@
   "+components/component_updater",
   "+components/constrained_window",
   "+components/content_settings/core/browser",
-  "+components/crash/content/app",
+  "+components/crash/core/app",
   "+components/domain_reliability",
   "+components/download/public/common",
   "+components/favicon/core",
diff --git a/chrome/test/base/chrome_test_launcher.cc b/chrome/test/base/chrome_test_launcher.cc
index f359888..ed174ed 100644
--- a/chrome/test/base/chrome_test_launcher.cc
+++ b/chrome/test/base/chrome_test_launcher.cc
@@ -29,7 +29,7 @@
 #include "chrome/install_static/test/scoped_install_details.h"
 #include "chrome/test/base/chrome_test_suite.h"
 #include "chrome/utility/chrome_content_utility_client.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #include "content/public/app/content_main.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/network_service_test_helper.h"
diff --git a/chrome/test/base/js2gtest.js b/chrome/test/base/js2gtest.js
index 93772a9..de0be48 100644
--- a/chrome/test/base/js2gtest.js
+++ b/chrome/test/base/js2gtest.js
@@ -17,27 +17,25 @@
 // Arguments from rules in chrome_tests.gypi are passed in through
 // python script gypv8sh.py.
 if (arguments.length != 6) {
-  print('usage: ' +
-      arguments[0] +
+  print(
+      'usage: ' + arguments[0] +
       ' path-to-testfile.js path-to-src-root/ path-to-deps.js output.cc' +
       ' test-type');
   quit(-1);
 }
 
-[
-  _,
-  // Full path to the test input file, relative to the current working
-  // directory.
-  fullTestFilePath,
-  // Path to source-root, relative to the current working directory.
-  srcRootPath,
-  // Path to Closure library style deps.js file.
-  depsFile,
-  // Path to C++ file generation is outputting to.
-  outputFile,
-  // Type of this test. One of 'extension', 'unit', 'webui', 'mojo_lite_webui'.
-  testType
-] = arguments;
+[_,
+ // Full path to the test input file, relative to the current working
+ // directory.
+ fullTestFilePath,
+ // Path to source-root, relative to the current working directory.
+ srcRootPath,
+ // Path to Closure library style deps.js file.
+ depsFile,
+ // Path to C++ file generation is outputting to.
+ outputFile,
+ // Type of this test. One of 'extension', 'unit', 'webui', 'mojo_lite_webui'.
+ testType] = arguments;
 
 
 if (!fullTestFilePath.startsWith(srcRootPath)) {
@@ -49,7 +47,7 @@
  * Path to test input file, relative to source root directory.
  * @type {string}
  */
-var testFile = fullTestFilePath.substr(srcRootPath.length);
+const testFile = fullTestFilePath.substr(srcRootPath.length);
 
 const TEST_TYPES = new Set(['extension', 'unit', 'webui', 'mojo_lite_webui']);
 
@@ -62,21 +60,21 @@
  * C++ gtest macro to use for TEST_F depending on |testType|.
  * @type {string} ('TEST_F'|'IN_PROC_BROWSER_TEST_F')
  */
-var testF;
+let testF;
 
 /**
  * Keeps track of whether a typedef has been generated for each test
  * fixture.
  * @type {!Map<string, string>}
  */
-var typedeffedCppFixtures = new Map();
+const typedeffedCppFixtures = new Map();
 
 /**
  * Maintains a list of file paths (relative to source-root directory) to add
  * to each gtest body for inclusion at runtime before running each JS test.
  * @type {Array<string>}
  */
-var genIncludes = [];
+const genIncludes = [];
 
 /**
  * When true, add calls to set_preload_test_(fixture|name). This is needed when
@@ -84,24 +82,24 @@
  * but is not required or supported by any other test type.
  * @type {boolean}
  */
-var addSetPreloadInfo;
+let addSetPreloadInfo;
 
 /**
  * Whether cc headers need to be generated.
  * @type {boolean}
  */
-var needGenHeader = true;
+let needGenHeader = true;
 
 /**
  * Helpful hint pointing back to the source js.
  * @type {string}
  */
-var argHint = '// ' + arguments.join(' ');
+const argHint = '// ' + arguments.join(' ');
 
 /**
  * @type {Array<string>}
  */
-var pendingOutput = [];
+const pendingOutput = [];
 
 /**
  * Adds a string followed by a newline to the pending output.
@@ -110,8 +108,9 @@
  */
 function output(opt_string) {
   opt_string = opt_string || '';
-  if (opt_string[0] == '\n')
+  if (opt_string[0] == '\n') {
     opt_string = opt_string.substring(1);
+  }
   pendingOutput.push(opt_string);
 }
 
@@ -120,8 +119,9 @@
  * @param {string?} testFixture Name of test fixture.
  */
 function maybeGenHeader(testFixture) {
-  if (!needGenHeader)
+  if (!needGenHeader) {
     return;
+  }
   needGenHeader = false;
   output(`
 // GENERATED FILE'
@@ -166,10 +166,12 @@
 #include "testing/gtest/include/gtest/gtest.h"`);
   // Add includes specified by test fixture.
   if (testFixture) {
-    if (this[testFixture].prototype.testGenCppIncludes)
+    if (this[testFixture].prototype.testGenCppIncludes) {
       this[testFixture].prototype.testGenCppIncludes();
-    if (this[testFixture].prototype.commandLineSwitches)
+    }
+    if (this[testFixture].prototype.commandLineSwitches) {
       output('#include "base/command_line.h"');
+    }
     if (this[testFixture].prototype.featureList ||
         this[testFixture].prototype.featuresWithParameters) {
       output('#include "base/test/scoped_feature_list.h"');
@@ -182,7 +184,7 @@
 /**
  * @type {Array<{path: string, base: string>}
  */
-var pathStack = [];
+const pathStack = [];
 
 
 /**
@@ -198,13 +200,14 @@
     return includeFile.substr(2);  // Path is already relative to source-root.
   } else if (includeFile.startsWith('/')) {
     print('Error including ' + includeFile);
-    print('Only relative "foo/bar" or source-absolute "//foo/bar" paths are '
-          + 'supported - not file-system absolute: "/foo/bar"');
+    print(
+        'Only relative "foo/bar" or source-absolute "//foo/bar" paths are ' +
+        'supported - not file-system absolute: "/foo/bar"');
     quit(-1);
   } else {
     // The include-file path is relative to the file that included it.
-    var currentPath = pathStack[pathStack.length - 1];
-    return currentPath.replace(/[^\/\\]+$/, includeFile)
+    const currentPath = pathStack[pathStack.length - 1];
+    return currentPath.replace(/[^\/\\]+$/, includeFile);
   }
 }
 
@@ -234,14 +237,14 @@
  * Populated from the |depsFile| if any.
  * @type {Object<string>}
  */
-var dependencyProvidesToPaths = {};
+const dependencyProvidesToPaths = {};
 
 /**
  * Maps dependency path names to object names required by the file.
  * Populated from the |depsFile| if any.
  * @type {Object<Array<string>>}
  */
-var dependencyPathsToRequires = {};
+const dependencyPathsToRequires = {};
 
 if (depsFile) {
   var goog = goog || {};
@@ -278,8 +281,8 @@
     print('Can\'t have closure dependencies without a deps file.');
     quit(-1);
   }
-  var resultPaths = [];
-  var addedPaths = {};
+  const resultPaths = [];
+  const addedPaths = {};
 
   function addPath(path) {
     addedPaths[path] = true;
@@ -293,7 +296,7 @@
     // Set before recursing to catch cycles.
     addedPaths[path] = true;
     dependencyPathsToRequires[path].forEach(function(require) {
-      var providingPath = dependencyProvidesToPaths[require];
+      const providingPath = dependencyProvidesToPaths[require];
       if (!providingPath) {
         print('Unknown object', require, 'required by', path);
         quit(-1);
@@ -304,13 +307,13 @@
   }
 
   // Always add closure library's base.js if provided by deps.
-  var basePath = dependencyProvidesToPaths['goog'];
+  const basePath = dependencyProvidesToPaths['goog'];
   if (basePath) {
     addPath(basePath);
   }
 
   deps.forEach(function(dep) {
-    var providingPath = dependencyProvidesToPaths[dep];
+    const providingPath = dependencyProvidesToPaths[dep];
     if (providingPath) {
       resolveAndAppend(providingPath);
     } else {
@@ -339,9 +342,9 @@
  *     include immediately and at runtime.
  */
 function GEN_INCLUDE(includes) {
-  for (var i = 0; i < includes.length; i++) {
-    var includePath = includeFileToPath(includes[i]);
-    var js = readSourceAbsoluteJsFile(includePath);
+  for (let i = 0; i < includes.length; i++) {
+    const includePath = includeFileToPath(includes[i]);
+    const js = readSourceAbsoluteJsFile(includePath);
     pathStack.push(includePath);
     ('global', eval)(js);
     pathStack.pop();
@@ -354,13 +357,13 @@
  * @return {Number} line number of TEST_F function call.
  */
 function getTestDeclarationLineNumber() {
-  var oldPrepareStackTrace = Error.prepareStackTrace;
+  const oldPrepareStackTrace = Error.prepareStackTrace;
   Error.prepareStackTrace = function(error, structuredStackTrace) {
     return structuredStackTrace;
   };
-  var error = Error('');
+  const error = Error('');
   Error.captureStackTrace(error, TEST_F);
-  var lineNumber = error.stack[0].getLineNumber();
+  const lineNumber = error.stack[0].getLineNumber();
   Error.prepareStackTrace = oldPrepareStackTrace;
   return lineNumber;
 }
@@ -376,27 +379,28 @@
  */
 function TEST_F(testFixture, testFunction, testBody, opt_preamble) {
   maybeGenHeader(testFixture);
-  var browsePreload = this[testFixture].prototype.browsePreload;
-  var browsePrintPreload = this[testFixture].prototype.browsePrintPreload;
-  var testGenPreamble = this[testFixture].prototype.testGenPreamble;
-  var testGenPostamble = this[testFixture].prototype.testGenPostamble;
-  var typedefCppFixture = this[testFixture].prototype.typedefCppFixture;
-  var isAsyncParam = testType === 'unit' ? '' :
+  const browsePreload = this[testFixture].prototype.browsePreload;
+  const browsePrintPreload = this[testFixture].prototype.browsePrintPreload;
+  const testGenPreamble = this[testFixture].prototype.testGenPreamble;
+  const testGenPostamble = this[testFixture].prototype.testGenPostamble;
+  const typedefCppFixture = this[testFixture].prototype.typedefCppFixture;
+  const isAsyncParam = testType === 'unit' ?
+      '' :
       this[testFixture].prototype.isAsync + ',\n          ';
-  var testShouldFail = this[testFixture].prototype.testShouldFail;
-  var testPredicate = testShouldFail ? 'ASSERT_FALSE' : 'ASSERT_TRUE';
-  var webuiHost = this[testFixture].prototype.webuiHost;
-  var extraLibraries = genIncludes.concat(
+  const testShouldFail = this[testFixture].prototype.testShouldFail;
+  const testPredicate = testShouldFail ? 'ASSERT_FALSE' : 'ASSERT_TRUE';
+  const webuiHost = this[testFixture].prototype.webuiHost;
+  const extraLibraries = genIncludes.concat(
       this[testFixture].prototype.extraLibraries.map(includeFileToPath),
       resolveClosureModuleDeps(this[testFixture].prototype.closureModuleDeps),
       [testFile]);
-  var testFLine = getTestDeclarationLineNumber();
+  const testFLine = getTestDeclarationLineNumber();
 
   if (typedefCppFixture && !typedeffedCppFixtures.has(testFixture)) {
-    var switches = this[testFixture].prototype.commandLineSwitches;
-    var hasSwitches = switches && switches.length;
-    var featureList = this[testFixture].prototype.featureList;
-    var featuresWithParameters =
+    const switches = this[testFixture].prototype.commandLineSwitches;
+    const hasSwitches = switches && switches.length;
+    const featureList = this[testFixture].prototype.featureList;
+    const featuresWithParameters =
         this[testFixture].prototype.featuresWithParameters;
     if ((!hasSwitches && !featureList && !featuresWithParameters) ||
         typedefCppFixture == 'V8UnitTest') {
@@ -423,32 +427,34 @@
                                           {${disabledFeatures}});`);
         }
         if (featuresWithParameters) {
-          for (var i = 0; i < featuresWithParameters.length; ++i) {
-            var feature = featuresWithParameters[i];
-            var featureName = feature.featureName;
+          for (let i = 0; i < featuresWithParameters.length; ++i) {
+            const feature = featuresWithParameters[i];
+            const featureName = feature.featureName;
             if (!featureName) {
               print('"featureName" key required for featuresWithParameters');
               quit(-1);
             }
-            var parameters = feature.parameters;
+            const parameters = feature.parameters;
             if (!parameters) {
               print('"parameters" key required for featuresWithParameters');
               quit(-1);
             }
-          output(`
+            output(`
     scoped_feature_list${i}_.InitAndEnableFeatureWithParameters(
         ${featureName}, {`);
-            for (var parameter of parameters) {
-              var parameterName = parameter.name;
+            for (const parameter of parameters) {
+              const parameterName = parameter.name;
               if (!parameterName) {
-                print('"name" key required for parameter in ' +
-                      'featuresWithParameters');
+                print(
+                    '"name" key required for parameter in ' +
+                    'featuresWithParameters');
                 quit(-1);
               }
-              var parameterValue = parameter.value;
+              const parameterValue = parameter.value;
               if (!parameterValue) {
-                print('"value" key required for parameter in ' +
-                      'featuresWithParameters');
+                print(
+                    '"value" key required for parameter in ' +
+                    'featuresWithParameters');
                 quit(-1);
               }
               output(`
@@ -468,26 +474,26 @@
   ~${testFixture}() override {}
  private:`);
       if (hasSwitches) {
-      // Override SetUpCommandLine and add each switch.
-      output(`
+        // Override SetUpCommandLine and add each switch.
+        output(`
   void SetUpCommandLine(base::CommandLine* command_line) override {
     ${typedefCppFixture}::SetUpCommandLine(command_line);`);
-      for (var i = 0; i < switches.length; i++) {
-        output(`
+        for (let i = 0; i < switches.length; i++) {
+          output(`
     command_line->AppendSwitchASCII(
         "${switches[i].switchName}",
         "${(switches[i].switchValue || '')}");`);
-      }
-      output(`
+        }
+        output(`
   }`);
       }
       if (featureList || featuresWithParameters) {
         if (featureList) {
-        output(`
+          output(`
   base::test::ScopedFeatureList scoped_feature_list_;`);
         }
         if (featuresWithParameters) {
-          for (var i = 0; i < featuresWithParameters.length; ++i) {
+          for (let i = 0; i < featuresWithParameters.length; ++i) {
             output(`
   base::test::ScopedFeatureList scoped_feature_list${i}_;`);
           }
@@ -504,14 +510,14 @@
     GEN(opt_preamble);
   }
 
-  var outputLine = pendingOutput.length + 3;
+  const outputLine = pendingOutput.length + 3;
   output(`
 #line ${testFLine} "${fullTestFilePath}"
 ${testF}(${testFixture}, ${testFunction}) {
 #line ${outputLine} "${outputFile}"`);
 
-for (var i = 0; i < extraLibraries.length; i++) {
-    var libraryName = extraLibraries[i].replace(/\\/g, '/');
+  for (let i = 0; i < extraLibraries.length; i++) {
+    const libraryName = extraLibraries[i].replace(/\\/g, '/');
     output(`
   AddLibrary(base::FilePath(FILE_PATH_LITERAL(
       "${libraryName}")));`);
@@ -521,7 +527,7 @@
   set_preload_test_fixture("${testFixture}");
   set_preload_test_name("${testFunction}");`);
   }
-  if(testType == 'mojo_lite_webui') {
+  if (testType == 'mojo_lite_webui') {
     output(`
   set_use_mojo_lite_bindings();`);
   }
@@ -529,10 +535,12 @@
     output(`
   set_webui_host("${webuiHost}");`);
   }
-  if (testGenPreamble)
+  if (testGenPreamble) {
     testGenPreamble(testFixture, testFunction);
-  if (browsePreload)
+  }
+  if (browsePreload) {
     output(`  BrowsePreload(GURL("${browsePreload}"));`);
+  }
   if (browsePrintPreload) {
     output(`
   BrowsePrintPreload(GURL(WebUITestDataPathToURL(
@@ -543,8 +551,9 @@
       RunJavascriptTestF(
           ${isAsyncParam}"${testFixture}",
           "${testFunction}"));`);
-  if (testGenPostamble)
+  if (testGenPostamble) {
     testGenPostamble(testFixture, testFunction);
+  }
   output('}\n');
 }
 
@@ -561,7 +570,7 @@
 }
 
 // Now that generation functions are defined, load in |testFile|.
-var js = readSourceAbsoluteJsFile(testFile);
+const js = readSourceAbsoluteJsFile(testFile);
 pathStack.push(testFile);
 eval(js);
 pathStack.pop();
diff --git a/chrome/test/data/extensions/api_test/passwords_private/test.js b/chrome/test/data/extensions/api_test/passwords_private/test.js
index ceaf22f..256803a 100644
--- a/chrome/test/data/extensions/api_test/passwords_private/test.js
+++ b/chrome/test/data/extensions/api_test/passwords_private/test.js
@@ -22,8 +22,6 @@
             0, 'another_user', 'new_pass');
       } else if (numCalls == 3) {
         chrome.test.assertEq('another_user', savedPasswordsList[0].username);
-        chrome.test.assertEq(
-            'new_pass'.length, savedPasswordsList[0].numCharactersInPassword);
         chrome.test.succeed();
       } else {
         chrome.test.fail();
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 38fb719..572f52dd 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -3668,6 +3668,18 @@
     ]
   },
 
+  "SystemFeaturesDisableList": {
+    "os": ["chromeos"],
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "SystemFeaturesDisableList": ["settings"]
+        },
+        "prefs": { "policy.system_features_disable_list": { "local_state": true } }
+      }
+    ]
+  },
+
   "SessionLengthLimit": {
     "os": ["chromeos"],
     "policy_pref_mapping_test": [
diff --git a/chrome/test/data/sync/encryption_keys_retrieval_complete.html b/chrome/test/data/sync/encryption_keys_retrieval_complete.html
index 44083cd..39cd4f3 100644
--- a/chrome/test/data/sync/encryption_keys_retrieval_complete.html
+++ b/chrome/test/data/sync/encryption_keys_retrieval_complete.html
@@ -1,14 +1,13 @@
 <html>
 <head>
 <script>
-// This particular test page uses ASCII keys for simplicity. Real callers are
-// not expected to deal with strings and converting them via charCodeAt().
-// TODO(crbug.com/978430): Pass an actual binary (non-ASCII) key.
-function asciiToArrayBuffer(s) {
-  var buffer = new ArrayBuffer(s.length);
+function base64DecodeToArrayBuffer(encoded) {
+  // atob() decodes base64 strings into binary string.
+  var decoded = atob(encoded); 
+  var buffer = new ArrayBuffer(decoded.length);
   var view = new Uint8Array(buffer);
-  for (var i = 0; i < s.length; i++) {
-    view[i] = s.charCodeAt(i);
+  for (var i = 0; i < decoded.length; i++) {
+    view[i] = decoded.charCodeAt(i);
   }
   return buffer;
 }
@@ -19,7 +18,7 @@
   } else {
     chrome.setSyncEncryptionKeys(() => { window.close(); },
                                  location.search.substring(1),
-                                 [asciiToArrayBuffer(location.hash.substring(1))],
+                                 [base64DecodeToArrayBuffer(location.hash.substring(1))],
                                  /*last_key_version=*/7);
   }
 }
diff --git a/chrome/test/data/webui/media/media_feeds_webui_browsertest.js b/chrome/test/data/webui/media/media_feeds_webui_browsertest.js
index 6927c3b..72c293b 100644
--- a/chrome/test/data/webui/media/media_feeds_webui_browsertest.js
+++ b/chrome/test/data/webui/media/media_feeds_webui_browsertest.js
@@ -36,7 +36,12 @@
         Array.from(document.querySelector('#feed-table-header').children);
 
     assertDeepEquals(
-        ['ID', 'Url', 'Last Discovery Time'],
+        [
+          'ID', 'Url', 'Display Name', 'Last Discovery Time', 'Last Fetch Time',
+          'User Status', 'Last Fetch Result', 'Fetch Failed Count',
+          'Cache Expiry Time', 'Last Fetch Item Count',
+          'Last Fetch Play Next Count', 'Last Fetch Content Types', 'Logos'
+        ],
         feedHeaders.map(x => x.textContent.trim()));
   });
 
diff --git a/chrome/test/data/webui/settings/chromeos/ambient_mode_page_test.js b/chrome/test/data/webui/settings/chromeos/ambient_mode_page_test.js
new file mode 100644
index 0000000..31def6c
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/ambient_mode_page_test.js
@@ -0,0 +1,95 @@
+// 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.
+
+/**
+ * @implements {settings.AmbientModeBrowserProxy}
+ */
+class TestAmbientModeBrowserProxy extends TestBrowserProxy {
+  constructor() {
+    super([
+      'onAmbientModePageReady',
+    ]);
+  }
+
+  /** @override */
+  onAmbientModePageReady() {
+    this.methodCalled('onAmbientModePageReady');
+  }
+}
+
+suite('AmbientModeHandler', function() {
+  /** @type {SettingsAmbientModePageElement} */
+  let page = null;
+
+  /** @type {?TestAmbientModeBrowserProxy} */
+  let browserProxy = null;
+
+  suiteSetup(function() {});
+
+  setup(function() {
+    browserProxy = new TestAmbientModeBrowserProxy();
+    settings.AmbientModeBrowserProxyImpl.instance_ = browserProxy;
+
+    PolymerTest.clearBody();
+
+    const prefElement = document.createElement('settings-prefs');
+    document.body.appendChild(prefElement);
+
+    return CrSettingsPrefs.initialized.then(function() {
+      page = document.createElement('settings-ambient-mode-page');
+      page.prefs = prefElement.prefs;
+      document.body.appendChild(page);
+    });
+  });
+
+  teardown(function() {
+    page.remove();
+  });
+
+  test('toggleAmbientMode', function() {
+    const button = page.$$('#ambientModeEnable');
+    assertTrue(!!button);
+    assertFalse(button.disabled);
+
+    // The button's state is set by the pref value.
+    const enabled = page.getPref('settings.ambient_mode.enabled.value');
+    assertEquals(enabled, button.checked);
+
+    // Click the button will toggle the pref value.
+    button.click();
+    Polymer.dom.flush();
+    const enabled_toggled = page.getPref('settings.ambient_mode.enabled.value');
+    assertEquals(enabled_toggled, button.checked);
+    assertEquals(enabled, !enabled_toggled);
+
+    // Click again will toggle the pref value.
+    button.click();
+    Polymer.dom.flush();
+    const enabled_toggled_twice =
+        page.getPref('settings.ambient_mode.enabled.value');
+    assertEquals(enabled_toggled_twice, button.checked);
+    assertEquals(enabled, enabled_toggled_twice);
+  });
+
+  test('chooseTopicSource', function() {
+    const topicSourceRadioGroup = page.$$('#topicSourceRadioGroup');
+
+    // The radio group's state is set by the pref value.
+    let topicSourceValue =
+        page.getPref('settings.ambient_mode.topic_source.value');
+    assertEquals(topicSourceValue, parseFloat(topicSourceRadioGroup.selected));
+
+    const radioButtons =
+        topicSourceRadioGroup.querySelectorAll('cr-radio-button');
+    assertEquals(2, radioButtons.length);
+
+    // Click on topic source radio button will set the pref value.
+    radioButtons.forEach(function(button, index) {
+      button.click();
+      topicSourceValue =
+          page.getPref('settings.ambient_mode.topic_source.value');
+      assertEquals(topicSourceValue, index);
+    });
+  });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index 3d6ee38..d79194fc 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -218,6 +218,33 @@
   mocha.run();
 });
 
+
+// Tests for ambient mode page.
+// eslint-disable-next-line no-var
+var OSSettingsAmbientModePageTest = class extends OSSettingsBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return super.browsePreload + 'ambient_mode_page/ambient_mode_page.html';
+  }
+
+  /** @override */
+  get featureList() {
+    return {enabled: ['chromeos::features::kAmbientModeFeature']};
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
+      'ambient_mode_page_test.js',
+    ]);
+  }
+};
+
+TEST_F('OSSettingsAmbientModePageTest', 'AllJsTests', () => {
+  mocha.run();
+});
+
 // Tests for the main contents of the settings page.
 // eslint-disable-next-line no-var
 var OSSettingsPageTest = class extends OSSettingsBrowserTest {
@@ -1044,6 +1071,11 @@
   }
 
   /** @override */
+  get featureList() {
+    return {enabled: ['chromeos::features::kAmbientModeFeature']};
+  }
+
+  /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
       '//ui/webui/resources/js/promise_resolver.js',
diff --git a/chrome/test/data/webui/settings/chromeos/personalization_page_test.js b/chrome/test/data/webui/settings/chromeos/personalization_page_test.js
index c3678e9..f4bcf07 100644
--- a/chrome/test/data/webui/settings/chromeos/personalization_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/personalization_page_test.js
@@ -86,4 +86,14 @@
         settings.routes.CHANGE_PICTURE,
         settings.Router.getInstance().getCurrentRoute());
   });
+
+  test('ambientMode', function() {
+    const row = personalizationPage.$$('#ambientModeRow');
+    assertTrue(!!row);
+    row.click();
+    assertEquals(
+        settings.routes.AMBIENT_MODE,
+        settings.Router.getInstance().getCurrentRoute());
+  });
+
 });
diff --git a/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js b/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
index 197c9acd..c778421 100644
--- a/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
@@ -35,411 +35,6 @@
 };
 
 // eslint-disable-next-line no-var
-var CrControlledButtonV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/controlled_button_tests.m.js';
-  }
-};
-
-TEST_F('CrControlledButtonV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrControlledRadioButtonV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/controlled_radio_button_tests.m.js';
-  }
-};
-
-TEST_F('CrControlledRadioButtonV3Test', 'All', function() {
-  mocha.run();
-});
-
-GEN('#if !defined(OS_CHROMEOS)');
-
-// eslint-disable-next-line no-var
-var CrSettingsDefaultBrowserV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/default_browser_browsertest.m.js';
-  }
-};
-
-TEST_F('CrSettingsDefaultBrowserV3Test', 'All', function() {
-  mocha.run();
-});
-
-GEN('#endif  // !defined(OS_CHROMEOS)');
-
-// eslint-disable-next-line no-var
-var CrSettingsDownloadsPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/downloads_page_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsDownloadsPageV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsCheckboxV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/checkbox_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsCheckboxV3Test', 'All', function() {
-  mocha.run();
-});
-
-GEN('#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)');
-
-// eslint-disable-next-line no-var
-var CrSettingsChromeCleanupPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/chrome_cleanup_page_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsChromeCleanupPageV3Test', 'All', function() {
-  mocha.run();
-});
-
-GEN('#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)');
-
-// eslint-disable-next-line no-var
-var CrSettingsDropdownMenuV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/dropdown_menu_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsDropdownMenuV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsExtensionControlledIndicatorV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/extension_controlled_indicator_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsExtensionControlledIndicatorV3Test', 'All', function() {
-  mocha.run();
-});
-
-GEN('#if !defined(OS_CHROMEOS)');
-
-// eslint-disable-next-line no-var
-var CrSettingsImportDataDialogV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/import_data_dialog_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsImportDataDialogV3Test', 'All', function() {
-  mocha.run();
-});
-
-GEN('#endif  // !defined(OS_CHROMEOS)');
-
-GEN('#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)');
-
-// eslint-disable-next-line no-var
-var CrSettingsIncompatibleApplicationsPageV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/incompatible_applications_page_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsIncompatibleApplicationsPageV3Test', 'All', function() {
-  mocha.run();
-});
-
-GEN('#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)');
-
-GEN('#if !defined(OS_CHROMEOS)');
-
-// eslint-disable-next-line no-var
-var CrSettingsPeoplePageManageProfileV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/people_page_manage_profile_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsPeoplePageManageProfileV3Test', 'All', function() {
-  mocha.run();
-});
-
-GEN('#endif  // !defined(OS_CHROMEOS)');
-
-// eslint-disable-next-line no-var
-var CrSettingsPeoplePageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/people_page_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsPeoplePageV3Test', 'All', function() {
-  mocha.run();
-});
-
-GEN('#if defined(OS_CHROMEOS)');
-
-// eslint-disable-next-line no-var
-var CrSettingsPeoplePageChromeOSV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/people_page_test_cros.m.js';
-  }
-};
-
-TEST_F('CrSettingsPeoplePageChromeOSV3Test', 'All', function() {
-  mocha.run();
-});
-
-GEN('#endif  // defined(OS_CHROMEOS)');
-
-// eslint-disable-next-line no-var
-var CrSettingsPeoplePageSyncControlsV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/people_page_sync_controls_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsPeoplePageSyncControlsV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsPeoplePageSyncPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/people_page_sync_page_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsPeoplePageSyncPageV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsPrefUtilV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/pref_util_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsPrefUtilV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsResetPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/reset_page_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsResetPageV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsSiteFaviconV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/site_favicon_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsSiteFaviconV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsSliderV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/settings_slider_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsSliderV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsSubpageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/settings_subpage_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsSubpageV3Test', 'All', function() {
-  mocha.run();
-});
-
-GEN('#if !defined(OS_CHROMEOS)');
-
-// eslint-disable-next-line no-var
-var CrSettingsSystemPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/system_page_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsSystemPageV3Test', 'All', function() {
-  mocha.run();
-});
-
-GEN('#endif  // !defined(OS_CHROMEOS)');
-
-// eslint-disable-next-line no-var
-var CrSettingsTextareaV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/settings_textarea_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsTextareaV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsToggleButtonV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/settings_toggle_button_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsToggleButtonV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsSearchEnginesV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/search_engines_page_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsSearchEnginesV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsSearchPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/search_page_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsSearchPageV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsOnStartupPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/on_startup_page_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsOnStartupPageV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsStartupUrlsPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/startup_urls_page_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsStartupUrlsPageV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsAppearancePageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/appearance_page_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsAppearancePageV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsAppearanceFontsPageV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/appearance_fonts_page_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsAppearanceFontsPageV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsMenuV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/settings_menu_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsMenuV3Test', 'SettingsMenu', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsPrefsV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/prefs_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsPrefsV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
 var CrSettingsAboutPageV3Test = class extends CrSettingsV3BrowserTest {
   /** @override */
   get browsePreload() {
@@ -458,34 +53,6 @@
 GEN('#endif');
 
 // eslint-disable-next-line no-var
-var CrSettingsLanguagesV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/languages_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsLanguagesV3Test', 'All', function() {
-  mocha.run();
-});
-
-GEN('#if !defined(OS_MACOSX)');
-
-// eslint-disable-next-line no-var
-var CrSettingsEditDictionaryPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/edit_dictionary_page_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsEditDictionaryPageV3Test', 'All', function() {
-  mocha.run();
-});
-
-GEN('#endif  //!defined(OS_MACOSX)');
-
-// eslint-disable-next-line no-var
 var CrSettingsLanguagesPageV3Test = class extends CrSettingsV3BrowserTest {
   /** @override */
   get browsePreload() {
@@ -512,18 +79,6 @@
 GEN('#endif');
 
 // eslint-disable-next-line no-var
-var CrSettingsSearchV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/search_settings_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsSearchV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
 var CrSettingsClearBrowsingDataV3Test = class extends CrSettingsV3BrowserTest {
   /** @override */
   get browsePreload() {
@@ -549,17 +104,6 @@
     });
 GEN('#endif');
 
-// eslint-disable-next-line no-var
-var CrSettingsBasicPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/basic_page_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsBasicPageV3Test', 'All', function() {
-  mocha.run();
-});
 
 // eslint-disable-next-line no-var
 var CrSettingsMainPageV3Test = class extends CrSettingsV3BrowserTest {
@@ -583,18 +127,6 @@
 });
 
 // eslint-disable-next-line no-var
-var CrSettingsAutofillPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/autofill_page_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsAutofillPageV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
 var CrSettingsAutofillSectionCompanyEnabledV3Test =
     class extends CrSettingsV3BrowserTest {
   /** @override */
@@ -679,57 +211,6 @@
   mocha.run();
 });
 
-GEN('#if defined(OS_CHROMEOS)');
-// eslint-disable-next-line no-var
-var CrSettingsPasswordsSectionV3Test_Cros =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/passwords_section_test_cros.m.js';
-  }
-};
-
-TEST_F('CrSettingsPasswordsSectionV3Test_Cros', 'All', function() {
-  mocha.run();
-});
-GEN('#endif');
-
-// eslint-disable-next-line no-var
-var CrSettingsPaymentsSectionV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/payments_section_test.m.js';
-  }
-};
-
-TEST_F('CrSettingsPaymentsSectionV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsSiteDataDetailsV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/site_data_details_subpage_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsSiteDataDetailsV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsSiteListEntryV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/site_list_entry_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsSiteListEntryV3Test', 'All', function() {
-  mocha.run();
-});
-
 // eslint-disable-next-line no-var
 var CrSettingsSiteListV3Test = class extends CrSettingsV3BrowserTest {
   /** @override */
@@ -752,96 +233,80 @@
   runMochaSuite('AddExceptionDialog');
 });
 
-GEN('#if defined(OS_CHROMEOS)');
-// eslint-disable-next-line no-var
-var CrSettingsSiteListChromeOSV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/site_list_tests_cros.m.js';
-  }
-};
+[['AppearanceFontsPage', 'appearance_fonts_page_test.m.js'],
+ ['AppearancePage', 'appearance_page_test.m.js'],
+ ['AutofillPage', 'autofill_page_test.m.js'],
+ ['BasicPage', 'basic_page_test.m.js'],
+ ['CategoryDefaultSetting', 'category_default_setting_tests.m.js'],
+ ['CategorySettingExceptions', 'category_setting_exceptions_tests.m.js'],
+ ['Checkbox', 'checkbox_tests.m.js'],
+ ['ChooserExceptionList', 'chooser_exception_list_tests.m.js'],
+ ['ChooserExceptionListEntry', 'chooser_exception_list_entry_tests.m.js'],
+ ['ControlledButton', 'controlled_button_tests.m.js'],
+ ['ControlledRadioButton', 'controlled_radio_button_tests.m.js'],
+ ['DownloadsPage', 'downloads_page_test.m.js'],
+ ['DropdownMenu', 'dropdown_menu_tests.m.js'],
+ ['ExtensionControlledIndicator', 'extension_controlled_indicator_tests.m.js'],
+ ['Languages', 'languages_tests.m.js'],
+ ['Menu', 'settings_menu_test.m.js'],
+ ['OnStartupPage', 'on_startup_page_tests.m.js'],
+ ['PaymentsSection', 'payments_section_test.m.js'],
+ ['PeoplePage', 'people_page_test.m.js'],
+ ['PeoplePageSyncControls', 'people_page_sync_controls_test.m.js'],
+ ['PeoplePageSyncPage', 'people_page_sync_page_test.m.js'],
+ ['Prefs', 'prefs_tests.m.js'],
+ ['PrefUtil', 'pref_util_tests.m.js'],
+ ['ResetPage', 'reset_page_test.m.js'],
+ ['SearchEngines', 'search_engines_page_test.m.js'],
+ ['SearchPage', 'search_page_test.m.js'],
+ ['Search', 'search_settings_test.m.js'],
+ ['SiteDataDetails', 'site_data_details_subpage_tests.m.js'],
+ ['SiteDetailsPermission', 'site_details_permission_tests.m.js'],
+ ['SiteEntry', 'site_entry_tests.m.js'],
+ ['SiteFavicon', 'site_favicon_test.m.js'],
+ ['SiteListEntry', 'site_list_entry_tests.m.js'],
+ ['Slider', 'settings_slider_tests.m.js'],
+ ['StartupUrlsPage', 'startup_urls_page_test.m.js'],
+ ['Subpage', 'settings_subpage_test.m.js'],
+ ['Textarea', 'settings_textarea_tests.m.js'],
+ ['ToggleButton', 'settings_toggle_button_tests.m.js'],
+].forEach(test => registerTest(...test));
 
-// Copied from Polymer 2 test:
-// TODO(crbug.com/929455): flaky, fix.
-TEST_F(
-    'CrSettingsSiteListChromeOSV3Test', 'DISABLED_AndroidSmsInfo', function() {
-      mocha.run();
-    });
+GEN('#if defined(OS_CHROMEOS)');
+[['PasswordsSectionCros', 'passwords_section_test_cros.m.js'],
+ ['PeoplePageChromeOS', 'people_page_test_cros.m.js'],
+ // Copied from Polymer 2 test. TODO(crbug.com/929455): flaky, fix.
+ ['SiteListChromeOS', 'site_list_tests_cros.m.js', 'DISABLED_AndroidSmsInfo'],
+].forEach(test => registerTest(...test));
 GEN('#endif  // defined(OS_CHROMEOS)');
 
-// eslint-disable-next-line no-var
-var CrSettingsChooserExceptionListEntryV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/chooser_exception_list_entry_tests.m.js';
-  }
-};
+GEN('#if !defined(OS_MACOSX)');
+[['EditDictionaryPage', 'edit_dictionary_page_test.m.js'],
+].forEach(test => registerTest(...test));
+GEN('#endif  //!defined(OS_MACOSX)');
 
-TEST_F('CrSettingsChooserExceptionListEntryV3Test', 'All', function() {
-  mocha.run();
-});
+GEN('#if !defined(OS_CHROMEOS)');
+[['DefaultBrowser', 'default_browser_browsertest.m.js'],
+ ['ImportDataDialog', 'import_data_dialog_test.m.js'],
+ ['PeoplePageManageProfile', 'people_page_manage_profile_test.m.js'],
+ ['SystemPage', 'system_page_tests.m.js'],
+].forEach(test => registerTest(...test));
+GEN('#endif  // !defined(OS_CHROMEOS)');
 
-// eslint-disable-next-line no-var
-var CrSettingsChooserExceptionListV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/chooser_exception_list_tests.m.js';
-  }
-};
+GEN('#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)');
+[['ChromeCleanupPage', 'chrome_cleanup_page_test.m.js'],
+ ['IncompatibleApplicationsPage', 'incompatible_applications_page_test.m.js'],
+].forEach(test => registerTest(...test));
+GEN('#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)');
 
-TEST_F('CrSettingsChooserExceptionListV3Test', 'All', function() {
-  mocha.run();
-});
+function registerTest(testName, module, caseName) {
+  const className = `CrSettings${testName}V3Test`;
+  this[className] = class extends CrSettingsV3BrowserTest {
+    /** @override */
+    get browsePreload() {
+      return `chrome://settings/test_loader.html?module=settings/${module}`;
+    }
+  };
 
-// eslint-disable-next-line no-var
-var CrSettingsCategoryDefaultSettingV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/category_default_setting_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsCategoryDefaultSettingV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsCategorySettingExceptionsV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/category_setting_exceptions_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsCategorySettingExceptionsV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsSiteEntryV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/site_entry_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsSiteEntryV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsSiteDetailsPermissionV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/site_details_permission_tests.m.js';
-  }
-};
-
-TEST_F('CrSettingsSiteDetailsPermissionV3Test', 'All', function() {
-  mocha.run();
-});
+  TEST_F(className, caseName || 'All', () => mocha.run());
+}
diff --git a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
index 5863dfb..1bfc22f 100644
--- a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
+++ b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
@@ -11,16 +11,13 @@
    * Creates a single item for the list of passwords.
    * @param {string=} url
    * @param {string=} username
-   * @param {number=} passwordLength
    * @param {number=} id
    * @return {chrome.passwordsPrivate.PasswordUiEntry}
    */
-  /* #export */ function createPasswordEntry(
-      url, username, passwordLength, id) {
+  /* #export */ function createPasswordEntry(url, username, id) {
     // Generate fake data if param is undefined.
     url = url || patternMaker_('www.xxxxxx.com', 16);
     username = username || patternMaker_('user_xxxxx', 16);
-    passwordLength = passwordLength || Math.floor(Math.random() * 15) + 3;
     id = id || 0;
 
     return {
@@ -30,7 +27,6 @@
         link: 'http://' + url + '/login',
       },
       username: username,
-      numCharactersInPassword: passwordLength,
       id: id,
     };
   }
diff --git a/chrome/test/data/webui/settings/passwords_section_test.js b/chrome/test/data/webui/settings/passwords_section_test.js
index 6b297db..7ebd3d4 100644
--- a/chrome/test/data/webui/settings/passwords_section_test.js
+++ b/chrome/test/data/webui/settings/passwords_section_test.js
@@ -42,9 +42,6 @@
           passwordInfo.urls.shown, node.$$('#originUrl').textContent.trim());
       assertEquals(passwordInfo.urls.link, node.$$('#originUrl').href);
       assertEquals(passwordInfo.username, node.$$('#username').value);
-      assertEquals(
-          passwordInfo.numCharactersInPassword,
-          node.$$('#password').value.length);
       assertDeepEquals(listElement.items[index].entry, passwordInfo);
     }
   }
@@ -166,12 +163,12 @@
 
     test('verifySavedPasswordLength', function() {
       const passwordList = [
-        autofill_test_util.createPasswordEntry('site1.com', 'luigi', 1),
-        autofill_test_util.createPasswordEntry('longwebsite.com', 'peach', 7),
-        autofill_test_util.createPasswordEntry('site2.com', 'mario', 70),
-        autofill_test_util.createPasswordEntry('site1.com', 'peach', 11),
-        autofill_test_util.createPasswordEntry('google.com', 'mario', 7),
-        autofill_test_util.createPasswordEntry('site2.com', 'luigi', 8),
+        autofill_test_util.createPasswordEntry('site1.com', 'luigi'),
+        autofill_test_util.createPasswordEntry('longwebsite.com', 'peach'),
+        autofill_test_util.createPasswordEntry('site2.com', 'mario'),
+        autofill_test_util.createPasswordEntry('site1.com', 'peach'),
+        autofill_test_util.createPasswordEntry('google.com', 'mario'),
+        autofill_test_util.createPasswordEntry('site2.com', 'luigi'),
       ];
 
       const passwordsSection = elementFactory.createPasswordsSection(
@@ -193,10 +190,9 @@
     test('verifyPasswordListRemove', function() {
       const passwordList = [
         autofill_test_util.createPasswordEntry(
-            'anotherwebsite.com', 'luigi', 1, 0),
-        autofill_test_util.createPasswordEntry(
-            'longwebsite.com', 'peach', 7, 1),
-        autofill_test_util.createPasswordEntry('website.com', 'mario', 70, 2)
+            'anotherwebsite.com', 'luigi', 0),
+        autofill_test_util.createPasswordEntry('longwebsite.com', 'peach', 1),
+        autofill_test_util.createPasswordEntry('website.com', 'mario', 2)
       ];
 
       const passwordsSection = elementFactory.createPasswordsSection(
@@ -221,9 +217,8 @@
     test('verifyPasswordListAdd', function() {
       const passwordList = [
         autofill_test_util.createPasswordEntry(
-            'anotherwebsite.com', 'luigi', 1, 0),
-        autofill_test_util.createPasswordEntry(
-            'longwebsite.com', 'peach', 7, 1),
+            'anotherwebsite.com', 'luigi', 0),
+        autofill_test_util.createPasswordEntry('longwebsite.com', 'peach', 1),
       ];
 
       const passwordsSection = elementFactory.createPasswordsSection(
@@ -231,8 +226,8 @@
 
       validatePasswordList(passwordsSection, passwordList);
       // Simulate 'website.com' being added to the list.
-      passwordList.unshift(autofill_test_util.createPasswordEntry(
-          'website.com', 'mario', 70, 2));
+      passwordList.unshift(
+          autofill_test_util.createPasswordEntry('website.com', 'mario', 2));
       passwordManager.lastCallback.addSavedPasswordListChangedListener(
           passwordList);
       Polymer.dom.flush();
@@ -248,8 +243,8 @@
 
       // Set-up initial list.
       let passwordList = [
-        autofill_test_util.createPasswordEntry('website.com', 'mario', 1, 0),
-        autofill_test_util.createPasswordEntry('website.com', 'luigi', 7, 1)
+        autofill_test_util.createPasswordEntry('website.com', 'mario', 0),
+        autofill_test_util.createPasswordEntry('website.com', 'luigi', 1)
       ];
 
       passwordManager.lastCallback.addSavedPasswordListChangedListener(
@@ -276,12 +271,12 @@
     // event. Does not actually remove any passwords.
     test('verifyPasswordItemRemoveButton', function(done) {
       const passwordList = [
-        autofill_test_util.createPasswordEntry('one', 'six', 5),
-        autofill_test_util.createPasswordEntry('two', 'five', 3),
-        autofill_test_util.createPasswordEntry('three', 'four', 1),
-        autofill_test_util.createPasswordEntry('four', 'three', 2),
-        autofill_test_util.createPasswordEntry('five', 'two', 4),
-        autofill_test_util.createPasswordEntry('six', 'one', 6),
+        autofill_test_util.createPasswordEntry('one', 'six'),
+        autofill_test_util.createPasswordEntry('two', 'five'),
+        autofill_test_util.createPasswordEntry('three', 'four'),
+        autofill_test_util.createPasswordEntry('four', 'three'),
+        autofill_test_util.createPasswordEntry('five', 'two'),
+        autofill_test_util.createPasswordEntry('six', 'one'),
       ];
 
       const passwordsSection = elementFactory.createPasswordsSection(
@@ -310,7 +305,7 @@
     // (passwordless) credentials. Does not test Copy button.
     test('verifyCopyAbsentForFederatedPasswordInMenu', function() {
       const passwordList = [
-        autofill_test_util.createPasswordEntry('one.com', 'hey', 0),
+        autofill_test_util.createPasswordEntry('one.com', 'hey'),
       ];
       passwordList[0].federationText = 'with chromium.org';
 
@@ -326,7 +321,7 @@
     // credentials. Does not test Copy button.
     test('verifyCopyPresentInMenu', function() {
       const passwordList = [
-        autofill_test_util.createPasswordEntry('one.com', 'hey', 5),
+        autofill_test_util.createPasswordEntry('one.com', 'hey'),
       ];
       const passwordsSection = elementFactory.createPasswordsSection(
           passwordManager, passwordList, []);
@@ -338,12 +333,12 @@
 
     test('verifyFilterPasswords', function() {
       const passwordList = [
-        autofill_test_util.createPasswordEntry('one.com', 'SHOW', 5),
-        autofill_test_util.createPasswordEntry('two.com', 'shower', 3),
-        autofill_test_util.createPasswordEntry('three.com/show', 'four', 1),
-        autofill_test_util.createPasswordEntry('four.com', 'three', 2),
-        autofill_test_util.createPasswordEntry('five.com', 'two', 4),
-        autofill_test_util.createPasswordEntry('six-show.com', 'one', 6),
+        autofill_test_util.createPasswordEntry('one.com', 'SHOW'),
+        autofill_test_util.createPasswordEntry('two.com', 'shower'),
+        autofill_test_util.createPasswordEntry('three.com/show', 'four'),
+        autofill_test_util.createPasswordEntry('four.com', 'three'),
+        autofill_test_util.createPasswordEntry('five.com', 'two'),
+        autofill_test_util.createPasswordEntry('six-show.com', 'one'),
       ];
 
       const passwordsSection = elementFactory.createPasswordsSection(
@@ -352,10 +347,10 @@
       Polymer.dom.flush();
 
       const expectedList = [
-        autofill_test_util.createPasswordEntry('one.com', 'SHOW', 5),
-        autofill_test_util.createPasswordEntry('two.com', 'shower', 3),
-        autofill_test_util.createPasswordEntry('three.com/show', 'four', 1),
-        autofill_test_util.createPasswordEntry('six-show.com', 'one', 6),
+        autofill_test_util.createPasswordEntry('one.com', 'SHOW'),
+        autofill_test_util.createPasswordEntry('two.com', 'shower'),
+        autofill_test_util.createPasswordEntry('three.com/show', 'four'),
+        autofill_test_util.createPasswordEntry('six-show.com', 'one'),
       ];
 
       validatePasswordList(passwordsSection, expectedList);
@@ -363,12 +358,12 @@
 
     test('verifyFilterPasswordsWithRemoval', function() {
       const passwordList = [
-        autofill_test_util.createPasswordEntry('one.com', 'SHOW', 5, 0),
-        autofill_test_util.createPasswordEntry('two.com', 'shower', 3, 1),
-        autofill_test_util.createPasswordEntry('three.com/show', 'four', 1, 2),
-        autofill_test_util.createPasswordEntry('four.com', 'three', 2, 3),
-        autofill_test_util.createPasswordEntry('five.com', 'two', 4, 4),
-        autofill_test_util.createPasswordEntry('six-show.com', 'one', 6, 5),
+        autofill_test_util.createPasswordEntry('one.com', 'SHOW', 0),
+        autofill_test_util.createPasswordEntry('two.com', 'shower', 1),
+        autofill_test_util.createPasswordEntry('three.com/show', 'four', 2),
+        autofill_test_util.createPasswordEntry('four.com', 'three', 3),
+        autofill_test_util.createPasswordEntry('five.com', 'two', 4),
+        autofill_test_util.createPasswordEntry('six-show.com', 'one', 5),
       ];
 
       const passwordsSection = elementFactory.createPasswordsSection(
@@ -377,10 +372,10 @@
       Polymer.dom.flush();
 
       let expectedList = [
-        autofill_test_util.createPasswordEntry('one.com', 'SHOW', 5, 0),
-        autofill_test_util.createPasswordEntry('two.com', 'shower', 3, 1),
-        autofill_test_util.createPasswordEntry('three.com/show', 'four', 1, 2),
-        autofill_test_util.createPasswordEntry('six-show.com', 'one', 6, 5),
+        autofill_test_util.createPasswordEntry('one.com', 'SHOW', 0),
+        autofill_test_util.createPasswordEntry('two.com', 'shower', 1),
+        autofill_test_util.createPasswordEntry('three.com/show', 'four', 2),
+        autofill_test_util.createPasswordEntry('six-show.com', 'one', 5),
       ];
 
       validatePasswordList(passwordsSection, expectedList);
@@ -389,9 +384,9 @@
       passwordList.splice(2, 1);
 
       expectedList = [
-        autofill_test_util.createPasswordEntry('one.com', 'SHOW', 5, 0),
-        autofill_test_util.createPasswordEntry('two.com', 'shower', 3, 1),
-        autofill_test_util.createPasswordEntry('six-show.com', 'one', 6, 5),
+        autofill_test_util.createPasswordEntry('one.com', 'SHOW', 0),
+        autofill_test_util.createPasswordEntry('two.com', 'shower', 1),
+        autofill_test_util.createPasswordEntry('six-show.com', 'one', 5),
       ];
 
       passwordManager.lastCallback.addSavedPasswordListChangedListener(
@@ -532,7 +527,7 @@
     });
 
     test('verifyFederatedPassword', function() {
-      const item = autofill_test_util.createPasswordEntry('goo.gl', 'bart', 0);
+      const item = autofill_test_util.createPasswordEntry('goo.gl', 'bart');
       item.federationText = 'with chromium.org';
       const passwordDialog = elementFactory.createPasswordEditDialog(item);
 
@@ -546,8 +541,7 @@
 
     test('showSavedPasswordEditDialog', function() {
       const PASSWORD = 'bAn@n@5';
-      const item = autofill_test_util.createPasswordEntry(
-          'goo.gl', 'bart', PASSWORD.length);
+      const item = autofill_test_util.createPasswordEntry('goo.gl', 'bart');
       const passwordDialog = elementFactory.createPasswordEditDialog(item);
 
       assertFalse(passwordDialog.$.showPasswordButton.hidden);
@@ -563,8 +557,7 @@
 
     test('showSavedPasswordListItem', function() {
       const PASSWORD = 'bAn@n@5';
-      const item = autofill_test_util.createPasswordEntry(
-          'goo.gl', 'bart', PASSWORD.length);
+      const item = autofill_test_util.createPasswordEntry('goo.gl', 'bart');
       const passwordListItem = elementFactory.createPasswordListItem(item);
       // Hidden passwords should be disabled.
       assertTrue(passwordListItem.$$('#password').disabled);
@@ -587,7 +580,7 @@
     // password.
     test('onShowSavedPasswordEditDialog', function() {
       const expectedItem =
-          autofill_test_util.createPasswordEntry('goo.gl', 'bart', 8, 1);
+          autofill_test_util.createPasswordEntry('goo.gl', 'bart', 1);
       const passwordDialog =
           elementFactory.createPasswordEditDialog(expectedItem);
       assertEquals('', passwordDialog.item.password);
@@ -604,7 +597,7 @@
 
     test('onShowSavedPasswordListItem', function() {
       const expectedItem =
-          autofill_test_util.createPasswordEntry('goo.gl', 'bart', 8, 1);
+          autofill_test_util.createPasswordEntry('goo.gl', 'bart', 1);
       const passwordListItem =
           elementFactory.createPasswordListItem(expectedItem);
       assertEquals('', passwordListItem.item.password);
@@ -621,7 +614,7 @@
 
     test('onCopyPasswordListItem', function() {
       const expectedItem =
-          autofill_test_util.createPasswordEntry('goo.gl', 'bart', 8, 1);
+          autofill_test_util.createPasswordEntry('goo.gl', 'bart', 1);
       const passwordsSection = elementFactory.createPasswordsSection(
           passwordManager, [expectedItem], []);
 
@@ -637,7 +630,7 @@
 
     test('closingPasswordsSectionHidesUndoToast', function(done) {
       const passwordEntry =
-          autofill_test_util.createPasswordEntry('goo.gl', 'bart', 1);
+          autofill_test_util.createPasswordEntry('goo.gl', 'bart');
       const passwordsSection = elementFactory.createPasswordsSection(
           passwordManager, [passwordEntry], []);
       const toastManager = cr.toastManager.getToastManager();
@@ -659,7 +652,7 @@
     // Chrome offers the export option when there are passwords.
     test('offerExportWhenPasswords', function(done) {
       const passwordList = [
-        autofill_test_util.createPasswordEntry('googoo.com', 'Larry', 1),
+        autofill_test_util.createPasswordEntry('googoo.com', 'Larry'),
       ];
       const passwordsSection = elementFactory.createPasswordsSection(
           passwordManager, passwordList, []);
@@ -685,7 +678,7 @@
     // dialog.
     test('exportOpen', function(done) {
       const passwordList = [
-        autofill_test_util.createPasswordEntry('googoo.com', 'Larry', 1),
+        autofill_test_util.createPasswordEntry('googoo.com', 'Larry'),
       ];
       const passwordsSection = elementFactory.createPasswordsSection(
           passwordManager, passwordList, []);
@@ -821,7 +814,7 @@
               passwordManager.data.checkStatus.elapsedTimeSinceLastCheck,
               undefined);
           const passwordList = [
-            autofill_test_util.createPasswordEntry('site1.com', 'luigi', 1),
+            autofill_test_util.createPasswordEntry('site1.com', 'luigi'),
           ];
           const passwordsSection = elementFactory.createPasswordsSection(
               passwordManager, passwordList, []);
@@ -846,7 +839,7 @@
               passwordManager.data.checkStatus.elapsedTimeSinceLastCheck,
               undefined);
           const passwordList = [
-            autofill_test_util.createPasswordEntry('site1.com', 'luigi', 1),
+            autofill_test_util.createPasswordEntry('site1.com', 'luigi'),
           ];
           const passwordsSection = elementFactory.createPasswordsSection(
               passwordManager, passwordList, []);
@@ -892,7 +885,7 @@
           passwordManager.data.checkStatus.elapsedTimeSinceLastCheck =
               '5 min ago';
           const passwordList = [
-            autofill_test_util.createPasswordEntry('site1.com', 'luigi', 1),
+            autofill_test_util.createPasswordEntry('site1.com', 'luigi'),
           ];
           const passwordsSection = elementFactory.createPasswordsSection(
               passwordManager, passwordList, []);
@@ -927,7 +920,7 @@
           passwordManager.data.checkStatus.elapsedTimeSinceLastCheck =
               '5 min ago';
           const passwordList = [
-            autofill_test_util.createPasswordEntry('site1.com', 'luigi', 1),
+            autofill_test_util.createPasswordEntry('site1.com', 'luigi'),
           ];
           const passwordsSection = elementFactory.createPasswordsSection(
               passwordManager, passwordList, []);
@@ -958,8 +951,8 @@
       passwordManager.data.leakedCredentials = [];
       passwordManager.data.checkStatus.elapsedTimeSinceLastCheck = '5 min ago';
       const passwordList = [
-        autofill_test_util.createPasswordEntry('one.com', 'test4', 1),
-        autofill_test_util.createPasswordEntry('two.com', 'test3', 1),
+        autofill_test_util.createPasswordEntry('one.com', 'test4'),
+        autofill_test_util.createPasswordEntry('two.com', 'test3'),
       ];
       const passwordsSection = elementFactory.createPasswordsSection(
           passwordManager, passwordList, []);
@@ -1007,8 +1000,8 @@
       // Suppose no leaks detected initially, non-empty list of passwords,
       // signed in.
       const passwordList = [
-        autofill_test_util.createPasswordEntry('one.com', 'test4', 1),
-        autofill_test_util.createPasswordEntry('two.com', 'test3', 1),
+        autofill_test_util.createPasswordEntry('one.com', 'test4'),
+        autofill_test_util.createPasswordEntry('two.com', 'test3'),
       ];
       const passwordsSection = elementFactory.createPasswordsSection(
           passwordManager, passwordList, []);
diff --git a/chrome/updater/app/app.h b/chrome/updater/app/app.h
index d1b17179..248128d 100644
--- a/chrome/updater/app/app.h
+++ b/chrome/updater/app/app.h
@@ -7,11 +7,23 @@
 
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
+#include "base/no_destructor.h"
 #include "base/strings/string_piece.h"
 
 namespace updater {
 
-// An App is a main processing mode for the updater.
+// Creates a ref-counted singleton instance of the type T. Use this function
+// to get instances of classes derived from updater::App.
+template <typename T>
+scoped_refptr<T> AppInstance() {
+  static base::NoDestructor<scoped_refptr<T>> instance{
+      base::MakeRefCounted<T>()};
+  return *instance;
+}
+
+// An App is an abstract class used as a main processing mode for the updater.
+// Instances of classes derived from App must be accessed as singletons by
+// calling the function template AppInstance<T>.
 class App : public base::RefCountedThreadSafe<App> {
  public:
   // Starts the thread pool and task executor, then runs a runloop on the main
diff --git a/chrome/updater/app/app_uninstall.cc b/chrome/updater/app/app_uninstall.cc
index 42699ee1..c74454f 100644
--- a/chrome/updater/app/app_uninstall.cc
+++ b/chrome/updater/app/app_uninstall.cc
@@ -22,6 +22,9 @@
 
 // AppUninstall uninstalls the updater.
 class AppUninstall : public App {
+ public:
+  AppUninstall() = default;
+
  private:
   ~AppUninstall() override = default;
   void FirstTaskRun() override;
@@ -33,8 +36,8 @@
       base::BindOnce(&AppUninstall::Shutdown, this));
 }
 
-scoped_refptr<App> MakeAppUninstall() {
-  return base::MakeRefCounted<AppUninstall>();
+scoped_refptr<App> AppUninstallInstance() {
+  return AppInstance<AppUninstall>();
 }
 
 }  // namespace updater
diff --git a/chrome/updater/app/app_uninstall.h b/chrome/updater/app/app_uninstall.h
index 85ed261..e88e38e 100644
--- a/chrome/updater/app/app_uninstall.h
+++ b/chrome/updater/app/app_uninstall.h
@@ -11,7 +11,7 @@
 
 class App;
 
-scoped_refptr<App> MakeAppUninstall();
+scoped_refptr<App> AppUninstallInstance();
 
 }  // namespace updater
 
diff --git a/chrome/updater/app/app_update_all.cc b/chrome/updater/app/app_update_all.cc
index 155e424..f27e43319a 100644
--- a/chrome/updater/app/app_update_all.cc
+++ b/chrome/updater/app/app_update_all.cc
@@ -17,6 +17,9 @@
 namespace updater {
 
 class AppUpdateAll : public App {
+ public:
+  AppUpdateAll() = default;
+
  private:
   ~AppUpdateAll() override = default;
 
@@ -50,8 +53,8 @@
       base::BindOnce(&AppUpdateAll::Shutdown, this)));
 }
 
-scoped_refptr<App> MakeAppUpdateAll() {
-  return base::MakeRefCounted<AppUpdateAll>();
+scoped_refptr<App> AppUpdateAllInstance() {
+  return AppInstance<AppUpdateAll>();
 }
 
 }  // namespace updater
diff --git a/chrome/updater/app/app_update_all.h b/chrome/updater/app/app_update_all.h
index 7559df4..db216fa 100644
--- a/chrome/updater/app/app_update_all.h
+++ b/chrome/updater/app/app_update_all.h
@@ -11,7 +11,7 @@
 
 class App;
 
-scoped_refptr<App> MakeAppUpdateAll();
+scoped_refptr<App> AppUpdateAllInstance();
 
 }  // namespace updater
 
diff --git a/chrome/updater/server/mac/server.h b/chrome/updater/server/mac/server.h
index b849378..bea6145 100644
--- a/chrome/updater/server/mac/server.h
+++ b/chrome/updater/server/mac/server.h
@@ -11,7 +11,7 @@
 
 class App;
 
-scoped_refptr<App> MakeAppServer();
+scoped_refptr<App> AppServerInstance();
 
 }  // namespace updater
 
diff --git a/chrome/updater/server/mac/server.mm b/chrome/updater/server/mac/server.mm
index a8e2c40..7a490817 100644
--- a/chrome/updater/server/mac/server.mm
+++ b/chrome/updater/server/mac/server.mm
@@ -8,7 +8,6 @@
 #include <xpc/xpc.h>
 
 #include "base/mac/scoped_nsobject.h"
-#include "base/memory/ref_counted.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
@@ -57,8 +56,8 @@
   }
 }
 
-scoped_refptr<App> MakeAppServer() {
-  return base::MakeRefCounted<AppServer>();
+scoped_refptr<App> AppServerInstance() {
+  return AppInstance<AppServer>();
 }
 
 }  // namespace updater
diff --git a/chrome/updater/server/win/server.cc b/chrome/updater/server/win/server.cc
index 6bf0ff9..60d217f 100644
--- a/chrome/updater/server/win/server.cc
+++ b/chrome/updater/server/win/server.cc
@@ -19,7 +19,6 @@
 #include <memory>
 
 #include "base/logging.h"
-#include "base/memory/ref_counted.h"
 #include "base/stl_util.h"
 #include "base/system/sys_info.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
@@ -206,8 +205,8 @@
     Shutdown(hr);
 }
 
-scoped_refptr<App> MakeAppServer() {
-  return base::MakeRefCounted<ComServer>();
+scoped_refptr<App> AppServerInstance() {
+  return AppInstance<ComServer>();
 }
 
 }  // namespace updater
diff --git a/chrome/updater/server/win/server.h b/chrome/updater/server/win/server.h
index d1e45e30..9934f7a0 100644
--- a/chrome/updater/server/win/server.h
+++ b/chrome/updater/server/win/server.h
@@ -63,7 +63,7 @@
 
 class App;
 
-scoped_refptr<App> MakeAppServer();
+scoped_refptr<App> AppServerInstance();
 
 }  // namespace updater
 
diff --git a/chrome/updater/updater.cc b/chrome/updater/updater.cc
index 304f9ee669..a5f709bb 100644
--- a/chrome/updater/updater.cc
+++ b/chrome/updater/updater.cc
@@ -90,7 +90,7 @@
 #endif
 
   if (command_line->HasSwitch(kServerSwitch)) {
-    return MakeAppServer()->Run();
+    return AppServerInstance()->Run();
   }
 
 #if defined(OS_WIN)
@@ -98,14 +98,14 @@
     return ServiceMain::RunComService(command_line);
 
   if (command_line->HasSwitch(kInstallSwitch))
-    return MakeAppInstall({kChromeAppId})->Run();
+    return AppInstallInstance()->Run();
 #endif  // OS_WIN
 
   if (command_line->HasSwitch(kUninstallSwitch))
-    return MakeAppUninstall()->Run();
+    return AppUninstallInstance()->Run();
 
   if (command_line->HasSwitch(kUpdateAppsSwitch)) {
-    return MakeAppUpdateAll()->Run();
+    return AppUpdateAllInstance()->Run();
   }
 
   VLOG(1) << "Unknown command line switch.";
diff --git a/chrome/updater/win/install_app.cc b/chrome/updater/win/install_app.cc
index 4b90b5a..2d51647 100644
--- a/chrome/updater/win/install_app.cc
+++ b/chrome/updater/win/install_app.cc
@@ -692,10 +692,11 @@
 
 }  // namespace
 
-// Installs the updater and one application specified by |app_id|.
+// Sets the updater up, shows up a splash screen, then installs an application
+// while displaying the UI progress window.
 class AppInstall : public App {
  public:
-  explicit AppInstall(const std::string& app_id);
+  AppInstall() = default;
 
  private:
   ~AppInstall() override = default;
@@ -705,7 +706,10 @@
 
   void SetupDone(int result);
 
-  std::string app_id_;
+  // TODO(sorin): remove the hardcoding of the application id.
+  // https://crbug.com/1014298
+  const std::string app_id_ = {kChromeAppId};
+
   scoped_refptr<Configurator> config_;
   scoped_refptr<InstallAppController> app_install_controller_;
 
@@ -714,8 +718,6 @@
   std::unique_ptr<ui::SplashScreen> splash_screen_;
 };
 
-AppInstall::AppInstall(const std::string& app_id) : app_id_(app_id) {}
-
 void AppInstall::Initialize() {
   base::i18n::InitializeICU();
   config_ = base::MakeRefCounted<Configurator>();
@@ -758,12 +760,12 @@
       app_id_, base::BindOnce(&AppInstall::Shutdown, this));
 }
 
-scoped_refptr<App> MakeAppInstall(const std::string& app_id) {
+scoped_refptr<App> AppInstallInstance() {
   // TODO(sorin) "--install" must be run with "--single-process" until
   // crbug.com/1053729 is resolved.
   DCHECK(
       base::CommandLine::ForCurrentProcess()->HasSwitch(kSingleProcessSwitch));
-  return base::MakeRefCounted<AppInstall>(app_id);
+  return AppInstance<AppInstall>();
 }
 
 }  // namespace updater
diff --git a/chrome/updater/win/install_app.h b/chrome/updater/win/install_app.h
index 11ad6a3..085cf2f4 100644
--- a/chrome/updater/win/install_app.h
+++ b/chrome/updater/win/install_app.h
@@ -13,9 +13,7 @@
 
 class App;
 
-// Sets the updater up, shows up a splash screen, then installs an application
-// while displaying the UI progress window.
-scoped_refptr<App> MakeAppInstall(const std::string& app_id);
+scoped_refptr<App> AppInstallInstance();
 
 }  // namespace updater
 
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index 16c77f4..dffdb5e 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -660,7 +660,7 @@
       "//base:base_java",
       "//chromecast/android:libcast_shell_android",
       "//chromecast/browser/android:cast_shell_java",
-      "//components/crash/content/app:chrome_crashpad_handler_named_as_so",
+      "//components/crash/core/app:chrome_crashpad_handler_named_as_so",
     ]
 
     loadable_modules = [ "$root_out_dir/libchrome_crashpad_handler.so" ]
diff --git a/chromecast/app/BUILD.gn b/chromecast/app/BUILD.gn
index f927fda..36be37d 100644
--- a/chromecast/app/BUILD.gn
+++ b/chromecast/app/BUILD.gn
@@ -32,7 +32,7 @@
 
   if (!is_fuchsia) {
     # TODO(crbug.com/753619): Enable crash reporting on Fuchsia.
-    deps += [ "//components/crash/content/app" ]
+    deps += [ "//components/crash/core/app" ]
   }
 }
 
@@ -56,7 +56,7 @@
   if (!is_fuchsia) {
     deps += [
       "//chromecast/crash",
-      "//components/crash/content/app",
+      "//components/crash/core/app",
     ]
   }
 
diff --git a/chromecast/app/android/cast_crash_reporter_client_android.h b/chromecast/app/android/cast_crash_reporter_client_android.h
index 139d4ba..a190e95 100644
--- a/chromecast/app/android/cast_crash_reporter_client_android.h
+++ b/chromecast/app/android/cast_crash_reporter_client_android.h
@@ -9,7 +9,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 
 namespace chromecast {
 
diff --git a/chromecast/app/android/crash_handler.cc b/chromecast/app/android/crash_handler.cc
index 3737f72a..f8e9a496 100644
--- a/chromecast/app/android/crash_handler.cc
+++ b/chromecast/app/android/crash_handler.cc
@@ -17,8 +17,8 @@
 #include "chromecast/base/chromecast_config_android.h"
 #include "chromecast/base/version.h"
 #include "chromecast/browser/jni_headers/CastCrashHandler_jni.h"
-#include "components/crash/content/app/crash_reporter_client.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crash_reporter_client.h"
+#include "components/crash/core/app/crashpad.h"
 #include "content/public/common/content_switches.h"
 
 namespace {
diff --git a/chromecast/app/cast_main_delegate.cc b/chromecast/app/cast_main_delegate.cc
index be42de1..329269f 100644
--- a/chromecast/app/cast_main_delegate.cc
+++ b/chromecast/app/cast_main_delegate.cc
@@ -29,7 +29,7 @@
 #include "chromecast/gpu/cast_content_gpu_client.h"
 #include "chromecast/renderer/cast_content_renderer_client.h"
 #include "chromecast/utility/cast_content_utility_client.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 #include "components/crash/core/common/crash_key.h"
 #include "content/public/browser/browser_main_runner.h"
 #include "content/public/common/content_switches.h"
diff --git a/chromecast/app/linux/cast_crash_reporter_client.cc b/chromecast/app/linux/cast_crash_reporter_client.cc
index 019cefe9..c560e78 100644
--- a/chromecast/app/linux/cast_crash_reporter_client.cc
+++ b/chromecast/app/linux/cast_crash_reporter_client.cc
@@ -7,7 +7,7 @@
 #include "base/time/time.h"
 #include "chromecast/base/error_codes.h"
 #include "chromecast/crash/linux/crash_util.h"
-#include "components/crash/content/app/breakpad_linux.h"
+#include "components/crash/core/app/breakpad_linux.h"
 #include "content/public/common/content_switches.h"
 #include "services/service_manager/sandbox/switches.h"
 
diff --git a/chromecast/app/linux/cast_crash_reporter_client.h b/chromecast/app/linux/cast_crash_reporter_client.h
index 0470507..127e086 100644
--- a/chromecast/app/linux/cast_crash_reporter_client.h
+++ b/chromecast/app/linux/cast_crash_reporter_client.h
@@ -10,7 +10,7 @@
 #include <string>
 
 #include "base/macros.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 
 namespace chromecast {
 
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index de4c1ed3..73763c7 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -282,8 +282,8 @@
   if (!is_fuchsia) {
     # TODO(crbug.com/753619): Enable crash reporting on Fuchsia.
     deps += [
-      "//components/crash/content/app",
       "//components/crash/content/browser",
+      "//components/crash/core/app",
     ]
 
     # TODO(crbug.com/933142): Fuchsia needs its own methods to bridge with heap
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index b0e36f5..240defd 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -93,12 +93,12 @@
 #endif  // defined(OS_LINUX) || defined(OS_ANDROID)
 
 #if defined(OS_LINUX)
-#include "components/crash/content/app/breakpad_linux.h"
+#include "components/crash/core/app/breakpad_linux.h"
 #endif  // defined(OS_LINUX)
 
 #if defined(OS_ANDROID)
 #include "components/cdm/browser/cdm_message_filter_android.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #else
 #include "chromecast/browser/memory_pressure_controller_impl.h"
 #endif  // defined(OS_ANDROID)
diff --git a/chromecast/browser/general_audience_browsing_service.cc b/chromecast/browser/general_audience_browsing_service.cc
index a770141..2c3dfb0 100644
--- a/chromecast/browser/general_audience_browsing_service.cc
+++ b/chromecast/browser/general_audience_browsing_service.cc
@@ -104,7 +104,7 @@
   return std::make_unique<safe_search_api::URLChecker>(
       std::make_unique<safe_search_api::SafeSearchURLCheckerClient>(
           shared_url_loader_factory_, CreateNetworkTrafficAnnotationTag(),
-          std::string(), api_key_),
+          api_key_),
       /* cache size */ 1000);
 }
 
diff --git a/chromecast/external_mojo/external_service_support/BUILD.gn b/chromecast/external_mojo/external_service_support/BUILD.gn
index ad5c291..af091c2 100644
--- a/chromecast/external_mojo/external_service_support/BUILD.gn
+++ b/chromecast/external_mojo/external_service_support/BUILD.gn
@@ -37,7 +37,7 @@
     ]
     deps += [
       "//chromecast/crash",
-      "//components/crash/content/app",
+      "//components/crash/core/app",
     ]
   }
 }
diff --git a/chromecast/external_mojo/external_service_support/DEPS b/chromecast/external_mojo/external_service_support/DEPS
index b69ea9f7..25419933 100644
--- a/chromecast/external_mojo/external_service_support/DEPS
+++ b/chromecast/external_mojo/external_service_support/DEPS
@@ -1,5 +1,5 @@
 include_rules = [
   "+chromecast/crash",
-  "+components/crash/content/app",
+  "+components/crash/core/app",
   "+mojo/core/embedder",
 ]
diff --git a/chromecast/external_mojo/external_service_support/crash_reporter_client.cc b/chromecast/external_mojo/external_service_support/crash_reporter_client.cc
index 51ff8108..3020911 100644
--- a/chromecast/external_mojo/external_service_support/crash_reporter_client.cc
+++ b/chromecast/external_mojo/external_service_support/crash_reporter_client.cc
@@ -6,7 +6,7 @@
 #include "base/no_destructor.h"
 #include "base/time/time.h"
 #include "chromecast/crash/linux/crash_util.h"
-#include "components/crash/content/app/breakpad_linux.h"
+#include "components/crash/core/app/breakpad_linux.h"
 
 namespace chromecast {
 namespace external_service_support {
diff --git a/chromecast/external_mojo/external_service_support/crash_reporter_client.h b/chromecast/external_mojo/external_service_support/crash_reporter_client.h
index 6e3e475..efe4c936 100644
--- a/chromecast/external_mojo/external_service_support/crash_reporter_client.h
+++ b/chromecast/external_mojo/external_service_support/crash_reporter_client.h
@@ -8,7 +8,7 @@
 #include <string>
 
 #include "base/macros.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 
 namespace chromecast {
 namespace external_service_support {
diff --git a/chromeos/services/device_sync/cryptauth_scheduler_impl.cc b/chromeos/services/device_sync/cryptauth_scheduler_impl.cc
index 45480773..37945d2 100644
--- a/chromeos/services/device_sync/cryptauth_scheduler_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_scheduler_impl.cc
@@ -510,7 +510,18 @@
                             base::nullopt /* session_id */);
   }
 
+  // Schedule a first-time DeviceSync if one has never successfully completed.
+  // However, unlike Enrollment, there are no periodic DeviceSyncs.
+  if (request_type == RequestType::kDeviceSync &&
+      !pending_requests_[request_type] && !GetLastSuccessTime(request_type)) {
+    pending_requests_[request_type] = BuildClientMetadata(
+        0 /* retry_count */, cryptauthv2::ClientMetadata::INITIALIZATION,
+        base::nullopt /* session_id */);
+  }
+
   if (!pending_requests_[request_type]) {
+    // By this point, only DeviceSync can have no requests pending because it
+    // does not schedule periodic syncs.
     DCHECK_EQ(RequestType::kDeviceSync, request_type);
     pref_service_->SetString(GetPendingRequestPrefName(request_type),
                              kNoClientMetadata);
diff --git a/chromeos/services/device_sync/cryptauth_scheduler_impl.h b/chromeos/services/device_sync/cryptauth_scheduler_impl.h
index 96f17e3..ff434fa1 100644
--- a/chromeos/services/device_sync/cryptauth_scheduler_impl.h
+++ b/chromeos/services/device_sync/cryptauth_scheduler_impl.h
@@ -41,7 +41,8 @@
 // is made immediately on startup. After a successful Enrollment, periodic
 // Enrollment requests are made at time intervals provided by the CryptAuth
 // server in the ClientDirective proto. Note that there is no periodic
-// scheduling for DeviceSync, per se.
+// scheduling for DeviceSync, per se; however, an initialization request will be
+// made.
 //
 // ClientDirectives received from CryptAuth may contain an InvokeNext field
 // specifying that an Enrollment and/or DeviceSync request should be made
diff --git a/chromeos/services/device_sync/cryptauth_scheduler_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_scheduler_impl_unittest.cc
index f3b9295d5..dc8cc4c 100644
--- a/chromeos/services/device_sync/cryptauth_scheduler_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_scheduler_impl_unittest.cc
@@ -445,6 +445,62 @@
   VerifyClientDirective(cryptauthv2::GetClientDirectiveForTest());
 }
 
+TEST_F(DeviceSyncCryptAuthSchedulerImplTest,
+       SuccessfulInitializationDeviceSync) {
+  AddDisconnectedWifiNetwork();
+  SetWifiNetworkStatus(NetworkConnectionStatus::kConnected);
+
+  const base::Time kStartTime = base::Time::FromDoubleT(1600600000);
+  const base::Time kInitializationFinishTime =
+      kStartTime + base::TimeDelta::FromSeconds(5);
+
+  clock()->SetNow(kStartTime);
+
+  CreateScheduler(base::nullopt /* persisted_client_directive */,
+                  base::nullopt /* persisted_enrollment_client_metadata */,
+                  base::nullopt /* persisted_last_enrollment_attempt_time */,
+                  base::nullopt /* persisted_last_successful_enrollment_time */,
+                  base::nullopt /* persisted_device_sync_client_metadata */,
+                  base::nullopt /* persisted_last_device_sync_attempt_time */,
+                  base::nullopt /* persisted_last_successful_device_sync_time */
+  );
+
+  // No DeviceSync has been scheduled yet.
+  EXPECT_FALSE(device_sync_timer()->IsRunning());
+  EXPECT_EQ(base::nullopt, scheduler()->GetTimeToNextDeviceSyncRequest());
+
+  EXPECT_FALSE(scheduler()->HasDeviceSyncSchedulingStarted());
+  scheduler()->StartDeviceSyncScheduling(fake_device_sync_delegate());
+  EXPECT_TRUE(scheduler()->HasDeviceSyncSchedulingStarted());
+
+  // No successful DeviceSync has ever occurred; attempt immediately.
+  cryptauthv2::ClientMetadata expected_scheduled_device_sync_request =
+      cryptauthv2::BuildClientMetadata(
+          0 /* retry_count */, cryptauthv2::ClientMetadata::INITIALIZATION,
+          base::nullopt /* session_id */);
+  VerifyScheduledDeviceSync(expected_scheduled_device_sync_request,
+                            kZeroTimeDelta /* expected_delay */);
+
+  device_sync_timer()->Fire();
+  EXPECT_TRUE(scheduler()->IsWaitingForDeviceSyncResult());
+
+  VerifyLastClientMetadataReceivedByDeviceSyncDelegate(
+      1 /* total_received */, expected_scheduled_device_sync_request);
+
+  clock()->SetNow(kInitializationFinishTime);
+  scheduler()->HandleDeviceSyncResult(
+      CryptAuthDeviceSyncResult(CryptAuthDeviceSyncResult::ResultCode::kSuccess,
+                                true /* did_device_registry_change */,
+                                cryptauthv2::GetClientDirectiveForTest()));
+  VerifyLastDeviceSyncAttemptTime(kInitializationFinishTime);
+  VerifyLastSuccessfulDeviceSyncTime(kInitializationFinishTime);
+  VerifyClientDirective(cryptauthv2::GetClientDirectiveForTest());
+
+  // No periodic DeviceSyncs are scheduled.
+  EXPECT_FALSE(device_sync_timer()->IsRunning());
+  EXPECT_EQ(base::nullopt, scheduler()->GetTimeToNextDeviceSyncRequest());
+}
+
 TEST_F(DeviceSyncCryptAuthSchedulerImplTest, FailedRequests) {
   AddDisconnectedWifiNetwork();
   SetWifiNetworkStatus(NetworkConnectionStatus::kConnected);
@@ -698,13 +754,20 @@
   AddDisconnectedWifiNetwork();
   SetWifiNetworkStatus(NetworkConnectionStatus::kConnected);
 
-  CreateScheduler(base::nullopt /* persisted_client_directive */,
-                  base::nullopt /* persisted_enrollment_client_metadata */,
-                  base::nullopt /* persisted_last_enrollment_attempt_time */,
-                  base::nullopt /* persisted_last_successful_enrollment_time */,
-                  base::nullopt /* persisted_device_sync_client_metadata */,
-                  base::nullopt /* persisted_last_device_sync_attempt_time */,
-                  base::nullopt /* persisted_last_successful_device_sync_time */
+  const base::Time kLastSuccessTime = base::Time::FromDoubleT(1600600000);
+  const base::Time kLastAttemptTime =
+      kLastSuccessTime + base::TimeDelta::FromDays(30);
+  const base::Time kStartTime = kLastAttemptTime + (kImmediateRetryDelay / 2);
+  clock()->SetNow(kStartTime);
+
+  CreateScheduler(
+      cryptauthv2::GetClientDirectiveForTest() /* persisted_client_directive */,
+      base::nullopt /* persisted_enrollment_client_metadata */,
+      kLastAttemptTime /* persisted_last_enrollment_attempt_time */,
+      kLastSuccessTime /* persisted_last_successful_enrollment_time */,
+      base::nullopt /* persisted_device_sync_client_metadata */,
+      kLastAttemptTime /* persisted_last_device_sync_attempt_time */,
+      kLastSuccessTime /* persisted_last_successful_device_sync_time */
   );
 
   scheduler()->StartEnrollmentScheduling(fake_enrollment_delegate());
diff --git a/components/BUILD.gn b/components/BUILD.gn
index a523df1e..5f5f67f9 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -287,8 +287,8 @@
 
     if (!is_fuchsia) {
       deps += [
-        "//components/crash/content/app:unit_tests",
         "//components/crash/content/browser:unit_tests",
+        "//components/crash/core/app:unit_tests",
         "//components/data_reduction_proxy/content/common:unit_tests",
         "//components/data_reduction_proxy/core/browser:unit_tests",
         "//components/data_reduction_proxy/core/common:unit_tests",
diff --git a/components/arc/mojom/intent_helper.mojom b/components/arc/mojom/intent_helper.mojom
index 4dbc73a..448eb61 100644
--- a/components/arc/mojom/intent_helper.mojom
+++ b/components/arc/mojom/intent_helper.mojom
@@ -170,8 +170,9 @@
   TETHERSETTINGS,
   ETHERNET,
   CELLULAR,
+  AMBIENTMODE,
 
-  LAST = CELLULAR,
+  LAST = AMBIENTMODE,
 };
 
 // Describes an unique chrome app.
diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h
index 210bd27..c8c2da87 100644
--- a/components/autofill/core/browser/autofill_client.h
+++ b/components/autofill/core/browser/autofill_client.h
@@ -418,9 +418,9 @@
       const std::vector<base::string16>& values,
       const std::vector<base::string16>& labels) = 0;
 
-  // Informs the client that |UpdatePopup| will be called which enables
-  // keeping the UI alive.
-  virtual void PinPopupViewUntilUpdate() = 0;
+  // Informs the client that the popup needs to be kept alive. Call before
+  // |UpdatePopup| to update the open popup in-place.
+  virtual void PinPopupView() = 0;
 
   // Returns (not elided) suggestions currently held by the UI.
   virtual base::span<const Suggestion> GetPopupSuggestions() const = 0;
diff --git a/components/autofill/core/browser/test_autofill_client.cc b/components/autofill/core/browser/test_autofill_client.cc
index e91be41..620df7d 100644
--- a/components/autofill/core/browser/test_autofill_client.cc
+++ b/components/autofill/core/browser/test_autofill_client.cc
@@ -215,7 +215,7 @@
   return base::span<const Suggestion>();
 }
 
-void TestAutofillClient::PinPopupViewUntilUpdate() {}
+void TestAutofillClient::PinPopupView() {}
 
 void TestAutofillClient::UpdatePopup(const std::vector<Suggestion>& suggestions,
                                      autofill::PopupType popup_type) {}
diff --git a/components/autofill/core/browser/test_autofill_client.h b/components/autofill/core/browser/test_autofill_client.h
index b971d304..c92f939 100644
--- a/components/autofill/core/browser/test_autofill_client.h
+++ b/components/autofill/core/browser/test_autofill_client.h
@@ -118,7 +118,7 @@
       const std::vector<base::string16>& values,
       const std::vector<base::string16>& labels) override;
   base::span<const autofill::Suggestion> GetPopupSuggestions() const override;
-  void PinPopupViewUntilUpdate() override;
+  void PinPopupView() override;
   void UpdatePopup(const std::vector<autofill::Suggestion>& suggestions,
                    PopupType popup_type) override;
   void HideAutofillPopup(PopupHidingReason reason) override;
diff --git a/components/browser_watcher/activity_report_extractor.cc b/components/browser_watcher/activity_report_extractor.cc
index 871b47c..540a3081 100644
--- a/components/browser_watcher/activity_report_extractor.cc
+++ b/components/browser_watcher/activity_report_extractor.cc
@@ -16,7 +16,6 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/browser_watcher/activity_data_names.h"
-#include "components/variations/active_field_trials.h"
 #include "third_party/crashpad/crashpad/util/misc/uuid.h"
 
 namespace browser_watcher {
@@ -30,8 +29,6 @@
 
 namespace {
 
-const char kFieldTrialKeyPrefix[] = "FieldTrial.";
-
 // Collects stability user data from the recorded format to the collected
 // format.
 void CollectUserData(
@@ -65,20 +62,6 @@
       }
       case ActivityUserData::STRING_VALUE: {
         base::StringPiece value = recorded_value.GetString();
-
-        if (report && base::StartsWith(key, kFieldTrialKeyPrefix,
-                                       base::CompareCase::SENSITIVE)) {
-          // This entry represents an active Field Trial.
-          std::string trial_name =
-              key.substr(std::strlen(kFieldTrialKeyPrefix));
-          variations::ActiveGroupId group_id =
-              variations::MakeActiveGroupId(trial_name, value.as_string());
-          FieldTrial* field_trial = report->add_field_trials();
-          field_trial->set_name_id(group_id.name);
-          field_trial->set_group_id(group_id.group);
-          continue;
-        }
-
         collected_value.set_string_value(value.data(), value.size());
 
         // Promote version information to the global key value store.
@@ -87,6 +70,9 @@
               key == kActivityProduct || key == kActivityChannel ||
               key == kActivityPlatform || key == kActivityVersion;
           if (should_promote) {
+            // TODO(siggi): Copy the promoted data or perhaps remove this
+            //     promotion once the backend is able to display the per-process
+            //     data.
             (*report->mutable_global_data())[key].Swap(&collected_value);
             continue;
           }
diff --git a/components/browser_watcher/activity_report_extractor_unittest.cc b/components/browser_watcher/activity_report_extractor_unittest.cc
index 0cf8769f..4d2260b1 100644
--- a/components/browser_watcher/activity_report_extractor_unittest.cc
+++ b/components/browser_watcher/activity_report_extractor_unittest.cc
@@ -398,34 +398,6 @@
   EXPECT_EQ(strlen(string2), static_cast<uint64_t>(sref.size()));
 }
 
-TEST_F(StabilityReportExtractorTest, FieldTrialCollection) {
-  // Record some data.
-  GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
-                                        "", 3);
-  ActivityUserData& process_data = GlobalActivityTracker::Get()->process_data();
-  process_data.SetString("string", "bar");
-  process_data.SetString("FieldTrial.string", "bar");
-  process_data.SetString("FieldTrial.foo", "bar");
-
-  // Collect the stability report.
-  StabilityReport report;
-  ASSERT_EQ(SUCCESS, Extract(CreateAnalyzer(), &report));
-  ASSERT_EQ(1, report.process_states_size());
-
-  // Validate the report's experiment and global data.
-  ASSERT_EQ(2, report.field_trials_size());
-  EXPECT_NE(0U, report.field_trials(0).name_id());
-  EXPECT_NE(0U, report.field_trials(0).group_id());
-  EXPECT_NE(0U, report.field_trials(1).name_id());
-  EXPECT_EQ(report.field_trials(0).group_id(),
-            report.field_trials(1).group_id());
-
-  // Expect 1 key/value pair.
-  const auto& collected_data = report.process_states(0).data();
-  EXPECT_EQ(kInternalProcessDatums + 1U, collected_data.size());
-  EXPECT_TRUE(base::Contains(collected_data, "string"));
-}
-
 TEST_F(StabilityReportExtractorTest, ModuleCollection) {
   // Record some module information.
   GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
diff --git a/components/crash/content/app/BUILD.gn b/components/crash/content/app/BUILD.gn
index 97ae2b1..87f9bb6a 100644
--- a/components/crash/content/app/BUILD.gn
+++ b/components/crash/content/app/BUILD.gn
@@ -2,274 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# TODO(crbug.com/753619): Enable crash reporting on Fuchsia.
-assert(!is_ios && !is_fuchsia)
-
-import("//components/gwp_asan/buildflags/buildflags.gni")
-
-if (is_android) {
-  import("//build/config/android/config.gni")
-}
-
-source_set("lib") {
-  visibility = [ ":*" ]
-
-  sources = [
-    "crash_reporter_client.cc",
-    "crash_reporter_client.h",
-  ]
-
-  deps = [ "//base" ]
-}
-
-source_set("crashpad_handler_main") {
-  sources = [ "crashpad_handler_main.cc" ]
-
-  deps = [
-    "//components/gwp_asan/buildflags",
-    "//third_party/crashpad/crashpad/handler:handler",
-  ]
-
-  if (enable_gwp_asan) {
-    deps += [ "//components/gwp_asan/crash_handler" ]
-  }
-}
-
-static_library("app") {
-  sources = [
-    "client_upload_info.cc",
-    "client_upload_info.h",
-    "crash_export_thunks.h",
-    "crash_switches.cc",
-    "crash_switches.h",
-    "crashpad.h",
-    "crashpad_android.cc",
-    "crashpad_linux.cc",
-    "crashpad_mac.mm",
-    "crashpad_win.cc",
-  ]
-
-  if (is_mac || is_win || is_android || is_linux) {
-    sources += [ "crashpad.cc" ]
-  }
-
-  if (is_linux) {
-    sources += [
-      "breakpad_linux.cc",
-      "breakpad_linux.h",
-      "breakpad_linux_impl.h",
-    ]
-  }
-
-  defines = [ "CRASH_IMPLEMENTATION" ]
-
-  public_deps = [ ":lib" ]
-  deps = [
-    "//base",
-    "//build:branding_buildflags",
-  ]
-
-  if (is_mac || is_win || is_android || is_linux) {
-    deps += [
-      "//third_party/crashpad/crashpad/client",
-      "//third_party/crashpad/crashpad/util",
-    ]
-  }
-
-  if (is_android) {
-    deps += [
-      ":crashpad_handler_main",
-      "//components/crash/android:jni_headers",
-      "//third_party/crashpad/crashpad/handler",
-      "//third_party/crashpad/crashpad/snapshot",
-    ]
-  }
-
-  if (is_android || is_linux) {
-    deps += [
-      "//base:base_static",
-      "//components/crash/core/common",
-      "//sandbox",
-    ]
-  }
-
-  if (is_linux) {
-    deps += [ "//third_party/breakpad:client" ]
-  }
-
-  if (is_win) {
-    sources += [
-      "dump_hung_process_with_ptype.cc",
-      "dump_hung_process_with_ptype.h",
-      "minidump_with_crashpad_info.cc",
-      "minidump_with_crashpad_info.h",
-    ]
-
-    deps += [
-      "//third_party/crashpad/crashpad/client",
-      "//third_party/crashpad/crashpad/handler",
-      "//third_party/crashpad/crashpad/minidump",
-      "//third_party/crashpad/crashpad/util",
-    ]
-  }
-
-  if (is_mac) {
-    deps += [
-      "//third_party/crashpad/crashpad/minidump",
-      "//third_party/crashpad/crashpad/snapshot",
-    ]
-  }
-
-  if (is_android) {
-    libs = [ "log" ]
-  }
-
-  if (is_linux) {
-    data_deps = [ "//third_party/crashpad/crashpad/handler:crashpad_handler" ]
-  }
-}
-
-if (is_win) {
-  static_library("run_as_crashpad_handler") {
-    sources = [
-      "crash_switches.cc",
-      "crash_switches.h",
-      "fallback_crash_handler_launcher_win.cc",
-      "fallback_crash_handler_launcher_win.h",
-      "fallback_crash_handler_win.cc",
-      "fallback_crash_handler_win.h",
-      "fallback_crash_handling_win.cc",
-      "fallback_crash_handling_win.h",
-      "minidump_with_crashpad_info.cc",
-      "minidump_with_crashpad_info.h",
-      "run_as_crashpad_handler_win.cc",
-      "run_as_crashpad_handler_win.h",
-    ]
-
-    deps = [
-      "//base",
-      "//components/browser_watcher:activity_report",
-      "//components/gwp_asan/buildflags",
-      "//third_party/crashpad/crashpad/client",
-      "//third_party/crashpad/crashpad/handler",
-      "//third_party/crashpad/crashpad/minidump",
-      "//third_party/crashpad/crashpad/util",
-    ]
-
-    if (enable_gwp_asan) {
-      deps += [ "//components/gwp_asan/crash_handler" ]
-    }
-  }
-
-  # This source set contains the include file that declares the export thunks.
-  # Any target that gets compiled into both test and release code needs to
-  # depend on this for the include alone. The binary it's linked into then needs
-  # to depend on either :crash_export_thunks or :test_support, or in the case of
-  # release binaries, on //chrome_elf, which re-exports the thunks.
-  source_set("crash_export_thunk_include") {
-    sources = [ "crash_export_thunks.h" ]
-  }
-
-  # This source set contains a set of functions that allow using the crashpad
-  # handler across a module boundary. The intent is for these functions to be
-  # built into a dynamic library, which the user of the crashpad handler then
-  # has a link-time dependency on. This will result in an import dependency
-  # from the user to the dynamic library that will be bound at load time.
-  # In a single-module project, this source set can alternatively be included in
-  # the sole module, in which case the implementation will be bound at link
-  # time.
-  source_set("crash_export_thunks") {
-    sources = [
-      "crash_export_thunks.cc",
-      "crash_export_thunks.h",
-    ]
-
-    deps = [
-      ":app",
-      "//base",
-      "//third_party/crashpad/crashpad/client",
-    ]
-  }
-
-  # This source set contains a set of test stubs for the functions above.
-  # time.
-  source_set("crash_export_stubs") {
-    testonly = true
-
-    sources = [
-      "crash_export_stubs.cc",
-      "crash_export_thunks.h",
-    ]
-
-    deps = [
-      ":app",
-      "//base",
-    ]
-  }
-}
-
-if (is_mac || is_android) {
-  # We build a chromium-specific crashpad_handler executable so that we can
-  # define custom UserStreamDataSources.
-  executable("chrome_crashpad_handler") {
-    sources = [ "chrome_crashpad_handler.cc" ]
-
-    deps = [
-      ":crashpad_handler_main",
-      "//third_party/crashpad/crashpad/handler",
-    ]
-
-    if (is_mac && is_component_build) {
-      ldflags = [
-        # The handler is in
-        # Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/A/Helpers/
-        # or
-        # Content Shell.app/Contents/Frameworks/Content Shell Framework.framework/Versions/C/Helpers/
-        # so set rpath up to the base.
-        "-rpath",
-        "@loader_path/../../../../../../..",
-
-        # The handler can also be executed in an unbundled framework at
-        # Chromium Framework.framework/Versions/A/Helpers/
-        "-rpath",
-        "@loader_path/../../../..",
-
-        # The handler can be executed from headless_browsertests in Helpers/
-        "-rpath",
-        "@loader_path/..",
-      ]
-    }
-  }
-}
-
-if (is_android) {
-  # There is not any normal way to package native executables in an Android APK.
-  # It is normal to package native code as a loadable module but Android's APK
-  # installer will ignore files not named like a shared object, so give the
-  # handler executable an acceptable name.
-  copy("chrome_crashpad_handler_named_as_so") {
-    deps = [ ":chrome_crashpad_handler" ]
-
-    sources = [ "$root_out_dir/chrome_crashpad_handler" ]
-
-    outputs = [ "$root_out_dir/libchrome_crashpad_handler.so" ]
-  }
-}
-
-# This source set provides the functionality required for tests, which on Windows
-# link the export thunks directly into the test binary.
-source_set("test_support") {
-  testonly = true
-
-  deps = [ ":lib" ]
-
-  if (is_win) {
-    deps += [ ":crash_export_stubs" ]
-  }
-}
-
-# TODO(crbug.com/565771): This target and :deprecated_breakpad_win should be
-# removed.
+# TODO(crbug.com/565771): This target should be removed.
 if (is_win) {
   static_library("deprecated_breakpad_win") {
     visibility = [ "//components/nacl/broker:nacl64" ]
@@ -284,9 +17,9 @@
     defines = [ "CRASH_IMPLEMENTATION" ]
 
     deps = [
-      ":lib",
       "//base",
       "//base:base_static",
+      "//components/crash/core/app:lib",
       "//components/crash/core/common",
       "//content/public/common:result_codes",
       "//sandbox",
@@ -299,29 +32,3 @@
     libs = [ "userenv.lib" ]
   }
 }
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [
-    "fallback_crash_handler_launcher_win_unittest.cc",
-    "fallback_crash_handler_win_unittest.cc",
-    "fallback_crash_handling_win_unittest.cc",
-  ]
-  deps = [
-    ":lib",
-    "//base",
-    "//base/test:test_support",
-    "//testing/gmock",
-    "//testing/gtest",
-  ]
-
-  if (is_win) {
-    deps += [
-      ":run_as_crashpad_handler",
-      "//third_party/breakpad:client",
-      "//third_party/crashpad/crashpad/client:client",
-      "//third_party/crashpad/crashpad/snapshot:snapshot",
-      "//third_party/crashpad/crashpad/util",
-    ]
-  }
-}
diff --git a/components/crash/content/app/DEPS b/components/crash/content/app/DEPS
index 8fae20b..abc83c36 100644
--- a/components/crash/content/app/DEPS
+++ b/components/crash/content/app/DEPS
@@ -1,13 +1,5 @@
 include_rules = [
   "+sandbox",
 
-  "+components/crash/android/jni_headers",
-  "+components/browser_watcher/activity_report_user_stream_data_source.h",
-  "+components/gwp_asan/buildflags/buildflags.h",
-  "+components/gwp_asan/crash_handler/crash_handler.h",
   "+content/public/common/result_codes.h",
-  "+services/service_manager/embedder/descriptors.h",
-  "+services/service_manager/embedder/switches.h",
-  "+third_party/crashpad",
-  "+third_party/lss/linux_syscall_support.h",
 ]
diff --git a/components/crash/content/app/breakpad_win.cc b/components/crash/content/app/breakpad_win.cc
index d756069..e73162a 100644
--- a/components/crash/content/app/breakpad_win.cc
+++ b/components/crash/content/app/breakpad_win.cc
@@ -35,8 +35,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/pe_image.h"
 #include "base/win/win_util.h"
-#include "components/crash/content/app/crash_reporter_client.h"
 #include "components/crash/content/app/hard_error_handler_win.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 #include "components/crash/core/common/crash_keys.h"
 #include "content/public/common/result_codes.h"
 #include "sandbox/win/src/nt_internals.h"
diff --git a/components/crash/content/app/hard_error_handler_win.cc b/components/crash/content/app/hard_error_handler_win.cc
index dec998f..4af26e9e 100644
--- a/components/crash/content/app/hard_error_handler_win.cc
+++ b/components/crash/content/app/hard_error_handler_win.cc
@@ -9,7 +9,7 @@
 #include <winternl.h>
 
 #include "base/strings/string_util.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 
 namespace breakpad {
 
diff --git a/components/crash/content/browser/BUILD.gn b/components/crash/content/browser/BUILD.gn
index d06fa485..ce7bc78 100644
--- a/components/crash/content/browser/BUILD.gn
+++ b/components/crash/content/browser/BUILD.gn
@@ -23,7 +23,7 @@
 
   deps = [
     "//base",
-    "//components/crash/content/app",
+    "//components/crash/core/app",
     "//content/public/browser",
     "//content/public/common",
   ]
diff --git a/components/crash/content/browser/crash_handler_host_linux.cc b/components/crash/content/browser/crash_handler_host_linux.cc
index 9d61943b..e63d26f 100644
--- a/components/crash/content/browser/crash_handler_host_linux.cc
+++ b/components/crash/content/browser/crash_handler_host_linux.cc
@@ -53,7 +53,7 @@
 #endif
 
 #if defined(OS_ANDROID)
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #include "third_party/crashpad/crashpad/client/crashpad_client.h"  // nogncheck
 #include "third_party/crashpad/crashpad/util/posix/signals.h"      // nogncheck
 #endif
@@ -173,7 +173,7 @@
   // for writing the minidump as well as a file descriptor and a credentials
   // block so that they can't lie about their pid.
   //
-  // The message sender is in components/crash/content/app/breakpad_linux.cc.
+  // The message sender is in components/crash/core/app/breakpad_linux.cc.
 
   struct msghdr msg = {nullptr};
   struct iovec iov[kCrashIovSize];
diff --git a/components/crash/content/browser/crash_handler_host_linux.h b/components/crash/content/browser/crash_handler_host_linux.h
index b50ec1ec..4391eb9 100644
--- a/components/crash/content/browser/crash_handler_host_linux.h
+++ b/components/crash/content/browser/crash_handler_host_linux.h
@@ -22,7 +22,7 @@
 #include "build/build_config.h"
 
 #if !defined(OS_ANDROID)
-#include "components/crash/content/app/breakpad_linux_impl.h"
+#include "components/crash/core/app/breakpad_linux_impl.h"
 #endif
 
 namespace base {
diff --git a/components/crash/core/app/BUILD.gn b/components/crash/core/app/BUILD.gn
new file mode 100644
index 0000000..bb11c40
--- /dev/null
+++ b/components/crash/core/app/BUILD.gn
@@ -0,0 +1,298 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# TODO(crbug.com/753619): Enable crash reporting on Fuchsia.
+assert(!is_ios && !is_fuchsia)
+
+import("//components/gwp_asan/buildflags/buildflags.gni")
+
+if (is_android) {
+  import("//build/config/android/config.gni")
+}
+
+source_set("lib") {
+  visibility = [
+    ":*",
+    "//components/crash/content/app:*",
+  ]
+
+  sources = [
+    "crash_reporter_client.cc",
+    "crash_reporter_client.h",
+  ]
+
+  deps = [ "//base" ]
+}
+
+source_set("crashpad_handler_main") {
+  sources = [ "crashpad_handler_main.cc" ]
+
+  deps = [
+    "//components/gwp_asan/buildflags",
+    "//third_party/crashpad/crashpad/handler:handler",
+  ]
+
+  if (enable_gwp_asan) {
+    deps += [ "//components/gwp_asan/crash_handler" ]
+  }
+}
+
+static_library("app") {
+  sources = [
+    "client_upload_info.cc",
+    "client_upload_info.h",
+    "crash_export_thunks.h",
+    "crash_switches.cc",
+    "crash_switches.h",
+    "crashpad.h",
+    "crashpad_android.cc",
+    "crashpad_linux.cc",
+    "crashpad_mac.mm",
+    "crashpad_win.cc",
+  ]
+
+  if (is_mac || is_win || is_android || is_linux) {
+    sources += [ "crashpad.cc" ]
+  }
+
+  if (is_linux) {
+    sources += [
+      "breakpad_linux.cc",
+      "breakpad_linux.h",
+      "breakpad_linux_impl.h",
+    ]
+  }
+
+  defines = [ "CRASH_IMPLEMENTATION" ]
+
+  public_deps = [ ":lib" ]
+  deps = [
+    "//base",
+    "//build:branding_buildflags",
+  ]
+
+  if (is_mac || is_win || is_android || is_linux) {
+    deps += [
+      "//third_party/crashpad/crashpad/client",
+      "//third_party/crashpad/crashpad/util",
+    ]
+  }
+
+  if (is_android) {
+    deps += [
+      ":crashpad_handler_main",
+      "//components/crash/android:jni_headers",
+      "//third_party/crashpad/crashpad/handler",
+      "//third_party/crashpad/crashpad/snapshot",
+    ]
+  }
+
+  if (is_android || is_linux) {
+    deps += [
+      "//base:base_static",
+      "//components/crash/core/common",
+      "//sandbox",
+    ]
+  }
+
+  if (is_linux) {
+    deps += [ "//third_party/breakpad:client" ]
+  }
+
+  if (is_win) {
+    sources += [
+      "dump_hung_process_with_ptype.cc",
+      "dump_hung_process_with_ptype.h",
+      "minidump_with_crashpad_info.cc",
+      "minidump_with_crashpad_info.h",
+    ]
+
+    deps += [
+      "//third_party/crashpad/crashpad/client",
+      "//third_party/crashpad/crashpad/handler",
+      "//third_party/crashpad/crashpad/minidump",
+      "//third_party/crashpad/crashpad/util",
+    ]
+  }
+
+  if (is_mac) {
+    deps += [
+      "//third_party/crashpad/crashpad/minidump",
+      "//third_party/crashpad/crashpad/snapshot",
+    ]
+  }
+
+  if (is_android) {
+    libs = [ "log" ]
+  }
+
+  if (is_linux) {
+    data_deps = [ "//third_party/crashpad/crashpad/handler:crashpad_handler" ]
+  }
+}
+
+if (is_win) {
+  static_library("run_as_crashpad_handler") {
+    sources = [
+      "crash_switches.cc",
+      "crash_switches.h",
+      "fallback_crash_handler_launcher_win.cc",
+      "fallback_crash_handler_launcher_win.h",
+      "fallback_crash_handler_win.cc",
+      "fallback_crash_handler_win.h",
+      "fallback_crash_handling_win.cc",
+      "fallback_crash_handling_win.h",
+      "minidump_with_crashpad_info.cc",
+      "minidump_with_crashpad_info.h",
+      "run_as_crashpad_handler_win.cc",
+      "run_as_crashpad_handler_win.h",
+    ]
+
+    deps = [
+      "//base",
+      "//components/browser_watcher:activity_report",
+      "//components/gwp_asan/buildflags",
+      "//third_party/crashpad/crashpad/client",
+      "//third_party/crashpad/crashpad/handler",
+      "//third_party/crashpad/crashpad/minidump",
+      "//third_party/crashpad/crashpad/util",
+    ]
+
+    if (enable_gwp_asan) {
+      deps += [ "//components/gwp_asan/crash_handler" ]
+    }
+  }
+
+  # This source set contains the include file that declares the export thunks.
+  # Any target that gets compiled into both test and release code needs to
+  # depend on this for the include alone. The binary it's linked into then needs
+  # to depend on either :crash_export_thunks or :test_support, or in the case of
+  # release binaries, on //chrome_elf, which re-exports the thunks.
+  source_set("crash_export_thunk_include") {
+    sources = [ "crash_export_thunks.h" ]
+  }
+
+  # This source set contains a set of functions that allow using the crashpad
+  # handler across a module boundary. The intent is for these functions to be
+  # built into a dynamic library, which the user of the crashpad handler then
+  # has a link-time dependency on. This will result in an import dependency
+  # from the user to the dynamic library that will be bound at load time.
+  # In a single-module project, this source set can alternatively be included in
+  # the sole module, in which case the implementation will be bound at link
+  # time.
+  source_set("crash_export_thunks") {
+    sources = [
+      "crash_export_thunks.cc",
+      "crash_export_thunks.h",
+    ]
+
+    deps = [
+      ":app",
+      "//base",
+      "//third_party/crashpad/crashpad/client",
+    ]
+  }
+
+  # This source set contains a set of test stubs for the functions above.
+  # time.
+  source_set("crash_export_stubs") {
+    testonly = true
+
+    sources = [
+      "crash_export_stubs.cc",
+      "crash_export_thunks.h",
+    ]
+
+    deps = [
+      ":app",
+      "//base",
+    ]
+  }
+}
+
+if (is_mac || is_android) {
+  # We build a chromium-specific crashpad_handler executable so that we can
+  # define custom UserStreamDataSources.
+  executable("chrome_crashpad_handler") {
+    sources = [ "chrome_crashpad_handler.cc" ]
+
+    deps = [
+      ":crashpad_handler_main",
+      "//third_party/crashpad/crashpad/handler",
+    ]
+
+    if (is_mac && is_component_build) {
+      ldflags = [
+        # The handler is in
+        # Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/A/Helpers/
+        # or
+        # Content Shell.app/Contents/Frameworks/Content Shell Framework.framework/Versions/C/Helpers/
+        # so set rpath up to the base.
+        "-rpath",
+        "@loader_path/../../../../../../..",
+
+        # The handler can also be executed in an unbundled framework at
+        # Chromium Framework.framework/Versions/A/Helpers/
+        "-rpath",
+        "@loader_path/../../../..",
+
+        # The handler can be executed from headless_browsertests in Helpers/
+        "-rpath",
+        "@loader_path/..",
+      ]
+    }
+  }
+}
+
+if (is_android) {
+  # There is not any normal way to package native executables in an Android APK.
+  # It is normal to package native code as a loadable module but Android's APK
+  # installer will ignore files not named like a shared object, so give the
+  # handler executable an acceptable name.
+  copy("chrome_crashpad_handler_named_as_so") {
+    deps = [ ":chrome_crashpad_handler" ]
+
+    sources = [ "$root_out_dir/chrome_crashpad_handler" ]
+
+    outputs = [ "$root_out_dir/libchrome_crashpad_handler.so" ]
+  }
+}
+
+# This source set provides the functionality required for tests, which on Windows
+# link the export thunks directly into the test binary.
+source_set("test_support") {
+  testonly = true
+
+  deps = [ ":lib" ]
+
+  if (is_win) {
+    deps += [ ":crash_export_stubs" ]
+  }
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "fallback_crash_handler_launcher_win_unittest.cc",
+    "fallback_crash_handler_win_unittest.cc",
+    "fallback_crash_handling_win_unittest.cc",
+  ]
+  deps = [
+    ":lib",
+    "//base",
+    "//base/test:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+
+  if (is_win) {
+    deps += [
+      ":run_as_crashpad_handler",
+      "//third_party/breakpad:client",
+      "//third_party/crashpad/crashpad/client:client",
+      "//third_party/crashpad/crashpad/snapshot:snapshot",
+      "//third_party/crashpad/crashpad/util",
+    ]
+  }
+}
diff --git a/components/crash/core/app/DEPS b/components/crash/core/app/DEPS
new file mode 100644
index 0000000..ee38145
--- /dev/null
+++ b/components/crash/core/app/DEPS
@@ -0,0 +1,13 @@
+include_rules = [
+  "+sandbox",
+  "+third_party/breakpad",
+
+  "+components/crash/android/jni_headers",
+  "+components/browser_watcher/activity_report_user_stream_data_source.h",
+  "+components/gwp_asan/buildflags/buildflags.h",
+  "+components/gwp_asan/crash_handler/crash_handler.h",
+  "+services/service_manager/embedder/descriptors.h",
+  "+services/service_manager/embedder/switches.h",
+  "+third_party/crashpad",
+  "+third_party/lss/linux_syscall_support.h",
+]
diff --git a/components/crash/content/app/breakpad_linux.cc b/components/crash/core/app/breakpad_linux.cc
similarity index 99%
rename from components/crash/content/app/breakpad_linux.cc
rename to components/crash/core/app/breakpad_linux.cc
index c1edb2b..192b0a7 100644
--- a/components/crash/content/app/breakpad_linux.cc
+++ b/components/crash/core/app/breakpad_linux.cc
@@ -5,7 +5,7 @@
 // For linux_syscall_support.h. This makes it safe to call embedded system
 // calls when in seccomp mode.
 
-#include "components/crash/content/app/breakpad_linux.h"
+#include "components/crash/core/app/breakpad_linux.h"
 
 #include <fcntl.h>
 #include <poll.h>
@@ -41,8 +41,8 @@
 #include "base/strings/string_util.h"
 #include "base/threading/thread_checker.h"
 #include "build/build_config.h"
-#include "components/crash/content/app/breakpad_linux_impl.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/breakpad_linux_impl.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 #include "components/crash/core/common/crash_keys.h"
 #include "services/service_manager/embedder/descriptors.h"
 #include "third_party/breakpad/breakpad/src/client/linux/crash_generation/crash_generation_client.h"
@@ -63,7 +63,7 @@
 #include "third_party/lss/linux_syscall_support.h"
 
 #if defined(OS_CHROMEOS)
-#include "components/crash/content/app/crash_switches.h"
+#include "components/crash/core/app/crash_switches.h"
 #include "services/service_manager/embedder/switches.h"  // nogncheck
 #endif
 
diff --git a/components/crash/content/app/breakpad_linux.h b/components/crash/core/app/breakpad_linux.h
similarity index 93%
rename from components/crash/content/app/breakpad_linux.h
rename to components/crash/core/app/breakpad_linux.h
index 6e95af6..9ea8037 100644
--- a/components/crash/content/app/breakpad_linux.h
+++ b/components/crash/core/app/breakpad_linux.h
@@ -4,8 +4,8 @@
 
 // Public interface for enabling Breakpad on Linux systems.
 
-#ifndef COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_H_
-#define COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_H_
+#ifndef COMPONENTS_CRASH_CORE_APP_BREAKPAD_LINUX_H_
+#define COMPONENTS_CRASH_CORE_APP_BREAKPAD_LINUX_H_
 
 #include <signal.h>
 #include <string>
@@ -71,4 +71,4 @@
 void SetFirstChanceExceptionHandler(bool (*handler)(int, siginfo_t*, void*));
 }  // namespace breakpad
 
-#endif  // COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_H_
+#endif  // COMPONENTS_CRASH_CORE_APP_BREAKPAD_LINUX_H_
diff --git a/components/crash/content/app/breakpad_linux_impl.h b/components/crash/core/app/breakpad_linux_impl.h
similarity index 90%
rename from components/crash/content/app/breakpad_linux_impl.h
rename to components/crash/core/app/breakpad_linux_impl.h
index 4d46598..ee75a02 100644
--- a/components/crash/content/app/breakpad_linux_impl.h
+++ b/components/crash/core/app/breakpad_linux_impl.h
@@ -5,14 +5,14 @@
 // Internal header file for the Linux breakpad implementation. This file is
 // shared between crash_handler_host_linux.cc and breakpad_linux.cc.
 
-#ifndef COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_IMPL_H_
-#define COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_IMPL_H_
+#ifndef COMPONENTS_CRASH_CORE_APP_BREAKPAD_LINUX_IMPL_H_
+#define COMPONENTS_CRASH_CORE_APP_BREAKPAD_LINUX_IMPL_H_
 
 #include <stddef.h>
 #include <stdint.h>
 #include <sys/types.h>
 
-#include "components/crash/content/app/breakpad_linux.h"
+#include "components/crash/core/app/breakpad_linux.h"
 #include "components/crash/core/common/crash_key_internal.h"
 
 namespace breakpad {
@@ -60,4 +60,4 @@
 
 }  // namespace breakpad
 
-#endif  // COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_IMPL_H_
+#endif  // COMPONENTS_CRASH_CORE_APP_BREAKPAD_LINUX_IMPL_H_
diff --git a/components/crash/content/app/chrome_crashpad_handler.cc b/components/crash/core/app/chrome_crashpad_handler.cc
similarity index 100%
rename from components/crash/content/app/chrome_crashpad_handler.cc
rename to components/crash/core/app/chrome_crashpad_handler.cc
diff --git a/components/crash/content/app/client_upload_info.cc b/components/crash/core/app/client_upload_info.cc
similarity index 83%
rename from components/crash/content/app/client_upload_info.cc
rename to components/crash/core/app/client_upload_info.cc
index 59fa42f..bcfbe85 100644
--- a/components/crash/content/app/client_upload_info.cc
+++ b/components/crash/core/app/client_upload_info.cc
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/client_upload_info.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/client_upload_info.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 
 namespace crash_reporter {
 
diff --git a/components/crash/content/app/client_upload_info.h b/components/crash/core/app/client_upload_info.h
similarity index 81%
rename from components/crash/content/app/client_upload_info.h
rename to components/crash/core/app/client_upload_info.h
index 0e14544..96753d0 100644
--- a/components/crash/content/app/client_upload_info.h
+++ b/components/crash/core/app/client_upload_info.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_CRASH_CONTENT_APP_CLIENT_UPLOAD_INFO_H_
-#define COMPONENTS_CRASH_CONTENT_APP_CLIENT_UPLOAD_INFO_H_
+#ifndef COMPONENTS_CRASH_CORE_APP_CLIENT_UPLOAD_INFO_H_
+#define COMPONENTS_CRASH_CORE_APP_CLIENT_UPLOAD_INFO_H_
 
 #include <string>
 
@@ -25,4 +25,4 @@
 
 }  // namespace crash_reporter
 
-#endif  // COMPONENTS_CRASH_CONTENT_APP_CLIENT_UPLOAD_INFO_H_
+#endif  // COMPONENTS_CRASH_CORE_APP_CLIENT_UPLOAD_INFO_H_
diff --git a/components/crash/content/app/crash_export_stubs.cc b/components/crash/core/app/crash_export_stubs.cc
similarity index 91%
rename from components/crash/content/app/crash_export_stubs.cc
rename to components/crash/core/app/crash_export_stubs.cc
index 4f9ff3c..7769893 100644
--- a/components/crash/content/app/crash_export_stubs.cc
+++ b/components/crash/core/app/crash_export_stubs.cc
@@ -9,8 +9,8 @@
 #include <windows.h>
 
 #include "build/build_config.h"
-#include "components/crash/content/app/crash_export_thunks.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crash_export_thunks.h"
+#include "components/crash/core/app/crashpad.h"
 
 void RequestSingleCrashUpload_ExportThunk(const char* local_id) {}
 
diff --git a/components/crash/content/app/crash_export_thunks.cc b/components/crash/core/app/crash_export_thunks.cc
similarity index 93%
rename from components/crash/content/app/crash_export_thunks.cc
rename to components/crash/core/app/crash_export_thunks.cc
index f2f8887..88a19481 100644
--- a/components/crash/content/app/crash_export_thunks.cc
+++ b/components/crash/core/app/crash_export_thunks.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/crash_export_thunks.h"
+#include "components/crash/core/app/crash_export_thunks.h"
 
 #include <algorithm>
 #include <type_traits>
@@ -11,8 +11,8 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
-#include "components/crash/content/app/crashpad.h"
-#include "components/crash/content/app/dump_hung_process_with_ptype.h"
+#include "components/crash/core/app/crashpad.h"
+#include "components/crash/core/app/dump_hung_process_with_ptype.h"
 #include "third_party/crashpad/crashpad/client/crashpad_client.h"
 
 void RequestSingleCrashUpload_ExportThunk(const char* local_id) {
diff --git a/components/crash/content/app/crash_export_thunks.h b/components/crash/core/app/crash_export_thunks.h
similarity index 94%
rename from components/crash/content/app/crash_export_thunks.h
rename to components/crash/core/app/crash_export_thunks.h
index 5e9b7aa0..bd8cd86 100644
--- a/components/crash/content/app/crash_export_thunks.h
+++ b/components/crash/core/app/crash_export_thunks.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_CRASH_CONTENT_APP_CRASH_EXPORT_THUNKS_H_
-#define COMPONENTS_CRASH_CONTENT_APP_CRASH_EXPORT_THUNKS_H_
+#ifndef COMPONENTS_CRASH_CORE_APP_CRASH_EXPORT_THUNKS_H_
+#define COMPONENTS_CRASH_CORE_APP_CRASH_EXPORT_THUNKS_H_
 
 #include <stddef.h>
 #include <time.h>
@@ -70,4 +70,4 @@
 
 }  // extern "C"
 
-#endif  // COMPONENTS_CRASH_CONTENT_APP_CRASH_EXPORT_THUNKS_H_
+#endif  // COMPONENTS_CRASH_CORE_APP_CRASH_EXPORT_THUNKS_H_
diff --git a/components/crash/content/app/crash_reporter_client.cc b/components/crash/core/app/crash_reporter_client.cc
similarity index 98%
rename from components/crash/content/app/crash_reporter_client.cc
rename to components/crash/core/app/crash_reporter_client.cc
index bb131fb..e778f68 100644
--- a/components/crash/content/app/crash_reporter_client.cc
+++ b/components/crash/core/app/crash_reporter_client.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 
 #include "build/build_config.h"
 
diff --git a/components/crash/content/app/crash_reporter_client.h b/components/crash/core/app/crash_reporter_client.h
similarity index 97%
rename from components/crash/content/app/crash_reporter_client.h
rename to components/crash/core/app/crash_reporter_client.h
index eb72649..9cc78fc2 100644
--- a/components/crash/content/app/crash_reporter_client.h
+++ b/components/crash/core/app/crash_reporter_client.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_CRASH_CONTENT_APP_CRASH_REPORTER_CLIENT_H_
-#define COMPONENTS_CRASH_CONTENT_APP_CRASH_REPORTER_CLIENT_H_
+#ifndef COMPONENTS_CRASH_CORE_APP_CRASH_REPORTER_CLIENT_H_
+#define COMPONENTS_CRASH_CORE_APP_CRASH_REPORTER_CLIENT_H_
 
 #include <string>
 
@@ -214,4 +214,4 @@
 
 }  // namespace crash_reporter
 
-#endif  // COMPONENTS_CRASH_CONTENT_APP_CRASH_REPORTER_CLIENT_H_
+#endif  // COMPONENTS_CRASH_CORE_APP_CRASH_REPORTER_CLIENT_H_
diff --git a/components/crash/content/app/crash_switches.cc b/components/crash/core/app/crash_switches.cc
similarity index 95%
rename from components/crash/content/app/crash_switches.cc
rename to components/crash/core/app/crash_switches.cc
index 8f6b3e9..066fff80f 100644
--- a/components/crash/content/app/crash_switches.cc
+++ b/components/crash/core/app/crash_switches.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/crash_switches.h"
+#include "components/crash/core/app/crash_switches.h"
 
 namespace crash_reporter {
 namespace switches {
diff --git a/components/crash/content/app/crash_switches.h b/components/crash/core/app/crash_switches.h
similarity index 74%
rename from components/crash/content/app/crash_switches.h
rename to components/crash/core/app/crash_switches.h
index ae16dfd..02bd8c4d 100644
--- a/components/crash/content/app/crash_switches.h
+++ b/components/crash/core/app/crash_switches.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_CRASH_CONTENT_APP_CRASH_SWITCHES_H_
-#define COMPONENTS_CRASH_CONTENT_APP_CRASH_SWITCHES_H_
+#ifndef COMPONENTS_CRASH_CORE_APP_CRASH_SWITCHES_H_
+#define COMPONENTS_CRASH_CORE_APP_CRASH_SWITCHES_H_
 
 #include "build/build_config.h"
 
@@ -23,4 +23,4 @@
 }  // namespace switches
 }  // namespace crash_reporter
 
-#endif  // COMPONENTS_CRASH_CONTENT_APP_CRASH_SWITCHES_H_
+#endif  // COMPONENTS_CRASH_CORE_APP_CRASH_SWITCHES_H_
diff --git a/components/crash/content/app/crashpad.cc b/components/crash/core/app/crashpad.cc
similarity index 98%
rename from components/crash/content/app/crashpad.cc
rename to components/crash/core/app/crashpad.cc
index 222d62a..290b469 100644
--- a/components/crash/content/app/crashpad.cc
+++ b/components/crash/core/app/crashpad.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 
 #include <stddef.h>
 #include <string.h>
@@ -27,7 +27,7 @@
 #include "base/system/sys_info.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 #include "third_party/crashpad/crashpad/client/annotation.h"
 #include "third_party/crashpad/crashpad/client/annotation_list.h"
 #include "third_party/crashpad/crashpad/client/crash_report_database.h"
@@ -41,7 +41,7 @@
 #endif  // OS_POSIX
 
 #if defined(OS_WIN)
-#include "components/crash/content/app/crash_export_thunks.h"
+#include "components/crash/core/app/crash_export_thunks.h"
 #endif
 
 namespace crash_reporter {
diff --git a/components/crash/content/app/crashpad.h b/components/crash/core/app/crashpad.h
similarity index 98%
rename from components/crash/content/app/crashpad.h
rename to components/crash/core/app/crashpad.h
index c19e672..e4a3a1e5 100644
--- a/components/crash/content/app/crashpad.h
+++ b/components/crash/core/app/crashpad.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_CRASH_CONTENT_APP_CRASHPAD_H_
-#define COMPONENTS_CRASH_CONTENT_APP_CRASHPAD_H_
+#ifndef COMPONENTS_CRASH_CORE_APP_CRASHPAD_H_
+#define COMPONENTS_CRASH_CORE_APP_CRASHPAD_H_
 
 #include <time.h>
 
@@ -246,4 +246,4 @@
 
 }  // namespace crash_reporter
 
-#endif  // COMPONENTS_CRASH_CONTENT_APP_CRASHPAD_H_
+#endif  // COMPONENTS_CRASH_CORE_APP_CRASHPAD_H_
diff --git a/components/crash/content/app/crashpad_android.cc b/components/crash/core/app/crashpad_android.cc
similarity index 99%
rename from components/crash/content/app/crashpad_android.cc
rename to components/crash/core/app/crashpad_android.cc
index 8a1902d..860aee4 100644
--- a/components/crash/content/app/crashpad_android.cc
+++ b/components/crash/core/app/crashpad_android.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 
 #include <dlfcn.h>
 #include <string.h>
@@ -34,7 +34,7 @@
 #include "build/branding_buildflags.h"
 #include "build/build_config.h"
 #include "components/crash/android/jni_headers/PackagePaths_jni.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 #include "sandbox/linux/services/syscall_wrappers.h"
 #include "services/service_manager/embedder/descriptors.h"
 #include "third_party/crashpad/crashpad/client/annotation.h"
diff --git a/components/crash/content/app/crashpad_handler_main.cc b/components/crash/core/app/crashpad_handler_main.cc
similarity index 100%
rename from components/crash/content/app/crashpad_handler_main.cc
rename to components/crash/core/app/crashpad_handler_main.cc
diff --git a/components/crash/content/app/crashpad_linux.cc b/components/crash/core/app/crashpad_linux.cc
similarity index 97%
rename from components/crash/content/app/crashpad_linux.cc
rename to components/crash/core/app/crashpad_linux.cc
index 6b35ceb..5d31d09 100644
--- a/components/crash/content/app/crashpad_linux.cc
+++ b/components/crash/core/app/crashpad_linux.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 
 #include <pthread.h>
 #include <sys/prctl.h>
@@ -16,8 +16,8 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "build/branding_buildflags.h"
-#include "components/crash/content/app/crash_reporter_client.h"
-#include "components/crash/content/app/crash_switches.h"
+#include "components/crash/core/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_switches.h"
 #include "sandbox/linux/services/namespace_sandbox.h"
 #include "services/service_manager/embedder/descriptors.h"
 #include "third_party/crashpad/crashpad/client/crashpad_client.h"
diff --git a/components/crash/content/app/crashpad_mac.mm b/components/crash/core/app/crashpad_mac.mm
similarity index 97%
rename from components/crash/content/app/crashpad_mac.mm
rename to components/crash/core/app/crashpad_mac.mm
index c2de9fe..b579521 100644
--- a/components/crash/content/app/crashpad_mac.mm
+++ b/components/crash/core/app/crashpad_mac.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 
 #include <CoreFoundation/CoreFoundation.h>
 #include <string.h>
@@ -21,7 +21,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/sys_string_conversions.h"
 #include "build/branding_buildflags.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 #include "third_party/crashpad/crashpad/client/crash_report_database.h"
 #include "third_party/crashpad/crashpad/client/crashpad_client.h"
 #include "third_party/crashpad/crashpad/client/crashpad_info.h"
diff --git a/components/crash/content/app/crashpad_win.cc b/components/crash/core/app/crashpad_win.cc
similarity index 96%
rename from components/crash/content/app/crashpad_win.cc
rename to components/crash/core/app/crashpad_win.cc
index 4e6d305..669f5bea 100644
--- a/components/crash/content/app/crashpad_win.cc
+++ b/components/crash/core/app/crashpad_win.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 
 #include <memory>
 
@@ -18,9 +18,9 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/branding_buildflags.h"
 #include "build/build_config.h"
-#include "components/crash/content/app/crash_export_thunks.h"
-#include "components/crash/content/app/crash_reporter_client.h"
-#include "components/crash/content/app/crash_switches.h"
+#include "components/crash/core/app/crash_export_thunks.h"
+#include "components/crash/core/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_switches.h"
 #include "third_party/crashpad/crashpad/client/crashpad_client.h"
 #include "third_party/crashpad/crashpad/client/crashpad_info.h"
 #include "third_party/crashpad/crashpad/client/simulate_crash_win.h"
diff --git a/components/crash/content/app/dump_hung_process_with_ptype.cc b/components/crash/core/app/dump_hung_process_with_ptype.cc
similarity index 89%
rename from components/crash/content/app/dump_hung_process_with_ptype.cc
rename to components/crash/core/app/dump_hung_process_with_ptype.cc
index 5112ad9..583c16d 100644
--- a/components/crash/content/app/dump_hung_process_with_ptype.cc
+++ b/components/crash/core/app/dump_hung_process_with_ptype.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/minidump_with_crashpad_info.h"
+#include "components/crash/core/app/minidump_with_crashpad_info.h"
 
 #include "base/files/file_util.h"
-#include "components/crash/content/app/crash_export_thunks.h"
-#include "components/crash/content/app/crash_reporter_client.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crash_export_thunks.h"
+#include "components/crash/core/app/crash_reporter_client.h"
+#include "components/crash/core/app/crashpad.h"
 #include "third_party/crashpad/crashpad/client/crash_report_database.h"
 #include "third_party/crashpad/crashpad/client/crashpad_info.h"
 #include "third_party/crashpad/crashpad/client/settings.h"
diff --git a/components/crash/content/app/dump_hung_process_with_ptype.h b/components/crash/core/app/dump_hung_process_with_ptype.h
similarity index 77%
rename from components/crash/content/app/dump_hung_process_with_ptype.h
rename to components/crash/core/app/dump_hung_process_with_ptype.h
index 8eff4bb5..c37074e9 100644
--- a/components/crash/content/app/dump_hung_process_with_ptype.h
+++ b/components/crash/core/app/dump_hung_process_with_ptype.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_CRASH_CONTENT_APP_DUMP_HUNG_PROCESS_WITH_PTYPE_H_
-#define COMPONENTS_CRASH_CONTENT_APP_DUMP_HUNG_PROCESS_WITH_PTYPE_H_
+#ifndef COMPONENTS_CRASH_CORE_APP_DUMP_HUNG_PROCESS_WITH_PTYPE_H_
+#define COMPONENTS_CRASH_CORE_APP_DUMP_HUNG_PROCESS_WITH_PTYPE_H_
 
 #include "base/process/process.h"
 
@@ -20,4 +20,4 @@
 
 }  // namespace crash_reporter
 
-#endif  // COMPONENTS_CRASH_CONTENT_APP_DUMP_HUNG_PROCESS_WITH_PTYPE_H_
+#endif  // COMPONENTS_CRASH_CORE_APP_DUMP_HUNG_PROCESS_WITH_PTYPE_H_
diff --git a/components/crash/content/app/fallback_crash_handler_launcher_win.cc b/components/crash/core/app/fallback_crash_handler_launcher_win.cc
similarity index 98%
rename from components/crash/content/app/fallback_crash_handler_launcher_win.cc
rename to components/crash/core/app/fallback_crash_handler_launcher_win.cc
index 947bb0c..b5707d8 100644
--- a/components/crash/content/app/fallback_crash_handler_launcher_win.cc
+++ b/components/crash/core/app/fallback_crash_handler_launcher_win.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/fallback_crash_handler_launcher_win.h"
+#include "components/crash/core/app/fallback_crash_handler_launcher_win.h"
 
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
diff --git a/components/crash/content/app/fallback_crash_handler_launcher_win.h b/components/crash/core/app/fallback_crash_handler_launcher_win.h
similarity index 92%
rename from components/crash/content/app/fallback_crash_handler_launcher_win.h
rename to components/crash/core/app/fallback_crash_handler_launcher_win.h
index 96ba9c0..080f8fc 100644
--- a/components/crash/content/app/fallback_crash_handler_launcher_win.h
+++ b/components/crash/core/app/fallback_crash_handler_launcher_win.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLER_LAUNCHER_WIN_H_
-#define COMPONENTS_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLER_LAUNCHER_WIN_H_
+#ifndef COMPONENTS_CRASH_CORE_APP_FALLBACK_CRASH_HANDLER_LAUNCHER_WIN_H_
+#define COMPONENTS_CRASH_CORE_APP_FALLBACK_CRASH_HANDLER_LAUNCHER_WIN_H_
 
 #include "base/command_line.h"
 #include "base/files/file_path.h"
@@ -65,4 +65,4 @@
 
 }  // namespace crash_reporter
 
-#endif  // COMPONENTS_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLER_LAUNCHER_WIN_H_
+#endif  // COMPONENTS_CRASH_CORE_APP_FALLBACK_CRASH_HANDLER_LAUNCHER_WIN_H_
diff --git a/components/crash/content/app/fallback_crash_handler_launcher_win_unittest.cc b/components/crash/core/app/fallback_crash_handler_launcher_win_unittest.cc
similarity index 98%
rename from components/crash/content/app/fallback_crash_handler_launcher_win_unittest.cc
rename to components/crash/core/app/fallback_crash_handler_launcher_win_unittest.cc
index c19ae79..5b199db1 100644
--- a/components/crash/content/app/fallback_crash_handler_launcher_win_unittest.cc
+++ b/components/crash/core/app/fallback_crash_handler_launcher_win_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/fallback_crash_handler_launcher_win.h"
+#include "components/crash/core/app/fallback_crash_handler_launcher_win.h"
 
 #include <dbghelp.h>
 
diff --git a/components/crash/content/app/fallback_crash_handler_win.cc b/components/crash/core/app/fallback_crash_handler_win.cc
similarity index 97%
rename from components/crash/content/app/fallback_crash_handler_win.cc
rename to components/crash/core/app/fallback_crash_handler_win.cc
index 56677a2..d26e387 100644
--- a/components/crash/content/app/fallback_crash_handler_win.cc
+++ b/components/crash/core/app/fallback_crash_handler_win.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/fallback_crash_handler_win.h"
+#include "components/crash/core/app/fallback_crash_handler_win.h"
 
 #include <dbghelp.h>
 #include <psapi.h>
@@ -21,7 +21,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/win/scoped_handle.h"
 #include "base/win/win_util.h"
-#include "components/crash/content/app/minidump_with_crashpad_info.h"
+#include "components/crash/core/app/minidump_with_crashpad_info.h"
 
 namespace crash_reporter {
 
diff --git a/components/crash/content/app/fallback_crash_handler_win.h b/components/crash/core/app/fallback_crash_handler_win.h
similarity index 90%
rename from components/crash/content/app/fallback_crash_handler_win.h
rename to components/crash/core/app/fallback_crash_handler_win.h
index 579bcb5..e9ac6ca 100644
--- a/components/crash/content/app/fallback_crash_handler_win.h
+++ b/components/crash/core/app/fallback_crash_handler_win.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLER_WIN_H_
-#define COMPONENTS_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLER_WIN_H_
+#ifndef COMPONENTS_CRASH_CORE_APP_FALLBACK_CRASH_HANDLER_WIN_H_
+#define COMPONENTS_CRASH_CORE_APP_FALLBACK_CRASH_HANDLER_WIN_H_
 
 #include <windows.h>
 
@@ -59,4 +59,4 @@
 
 }  // namespace crash_reporter
 
-#endif  // COMPONENTS_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLER_WIN_H_
+#endif  // COMPONENTS_CRASH_CORE_APP_FALLBACK_CRASH_HANDLER_WIN_H_
diff --git a/components/crash/content/app/fallback_crash_handler_win_unittest.cc b/components/crash/core/app/fallback_crash_handler_win_unittest.cc
similarity index 98%
rename from components/crash/content/app/fallback_crash_handler_win_unittest.cc
rename to components/crash/core/app/fallback_crash_handler_win_unittest.cc
index e21fecbe..60a25b53 100644
--- a/components/crash/content/app/fallback_crash_handler_win_unittest.cc
+++ b/components/crash/core/app/fallback_crash_handler_win_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/fallback_crash_handler_win.h"
+#include "components/crash/core/app/fallback_crash_handler_win.h"
 
 #include <map>
 #include <memory>
@@ -21,7 +21,7 @@
 #include "base/threading/platform_thread.h"
 #include "base/win/scoped_handle.h"
 #include "base/win/win_util.h"
-#include "components/crash/content/app/fallback_crash_handler_launcher_win.h"
+#include "components/crash/core/app/fallback_crash_handler_launcher_win.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/multiprocess_func_list.h"
 #include "third_party/crashpad/crashpad/client/crash_report_database.h"
diff --git a/components/crash/content/app/fallback_crash_handling_win.cc b/components/crash/core/app/fallback_crash_handling_win.cc
similarity index 92%
rename from components/crash/content/app/fallback_crash_handling_win.cc
rename to components/crash/core/app/fallback_crash_handling_win.cc
index b67db03..a77a57c 100644
--- a/components/crash/content/app/fallback_crash_handling_win.cc
+++ b/components/crash/core/app/fallback_crash_handling_win.cc
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/fallback_crash_handling_win.h"
+#include "components/crash/core/app/fallback_crash_handling_win.h"
 
 #include <memory>
 
 #include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/logging.h"
-#include "components/crash/content/app/crash_switches.h"
-#include "components/crash/content/app/fallback_crash_handler_launcher_win.h"
-#include "components/crash/content/app/fallback_crash_handler_win.h"
+#include "components/crash/core/app/crash_switches.h"
+#include "components/crash/core/app/fallback_crash_handler_launcher_win.h"
+#include "components/crash/core/app/fallback_crash_handler_win.h"
 
 namespace crash_reporter {
 
diff --git a/components/crash/content/app/fallback_crash_handling_win.h b/components/crash/core/app/fallback_crash_handling_win.h
similarity index 88%
rename from components/crash/content/app/fallback_crash_handling_win.h
rename to components/crash/core/app/fallback_crash_handling_win.h
index ebf5d9f..df7222c 100644
--- a/components/crash/content/app/fallback_crash_handling_win.h
+++ b/components/crash/core/app/fallback_crash_handling_win.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLING_WIN_H_
-#define COMPONENTS_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLING_WIN_H_
+#ifndef COMPONENTS_CRASH_CORE_APP_FALLBACK_CRASH_HANDLING_WIN_H_
+#define COMPONENTS_CRASH_CORE_APP_FALLBACK_CRASH_HANDLING_WIN_H_
 
 #include <stdint.h>
 #include <string>
@@ -40,4 +40,4 @@
 
 }  // namespace crash_reporter
 
-#endif  // COMPONENTS_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLING_WIN_H_
+#endif  // COMPONENTS_CRASH_CORE_APP_FALLBACK_CRASH_HANDLING_WIN_H_
diff --git a/components/crash/content/app/fallback_crash_handling_win_unittest.cc b/components/crash/core/app/fallback_crash_handling_win_unittest.cc
similarity index 97%
rename from components/crash/content/app/fallback_crash_handling_win_unittest.cc
rename to components/crash/core/app/fallback_crash_handling_win_unittest.cc
index 7df6b20..ddbc20e2a 100644
--- a/components/crash/content/app/fallback_crash_handling_win_unittest.cc
+++ b/components/crash/core/app/fallback_crash_handling_win_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/fallback_crash_handling_win.h"
+#include "components/crash/core/app/fallback_crash_handling_win.h"
 
 #include "base/base_switches.h"
 #include "base/command_line.h"
diff --git a/components/crash/content/app/minidump_with_crashpad_info.cc b/components/crash/core/app/minidump_with_crashpad_info.cc
similarity index 99%
rename from components/crash/content/app/minidump_with_crashpad_info.cc
rename to components/crash/core/app/minidump_with_crashpad_info.cc
index 845da1a..f3bc98d 100644
--- a/components/crash/content/app/minidump_with_crashpad_info.cc
+++ b/components/crash/core/app/minidump_with_crashpad_info.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/minidump_with_crashpad_info.h"
+#include "components/crash/core/app/minidump_with_crashpad_info.h"
 
 #include "base/files/file_util.h"
 #include "base/stl_util.h"
diff --git a/components/crash/content/app/minidump_with_crashpad_info.h b/components/crash/core/app/minidump_with_crashpad_info.h
similarity index 83%
rename from components/crash/content/app/minidump_with_crashpad_info.h
rename to components/crash/core/app/minidump_with_crashpad_info.h
index 358a206..76ab841 100644
--- a/components/crash/content/app/minidump_with_crashpad_info.h
+++ b/components/crash/core/app/minidump_with_crashpad_info.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_CRASH_CONTENT_APP_MINIDUMP_WITH_CRASHPAD_INFO_H_
-#define COMPONENTS_CRASH_CONTENT_APP_MINIDUMP_WITH_CRASHPAD_INFO_H_
+#ifndef COMPONENTS_CRASH_CORE_APP_MINIDUMP_WITH_CRASHPAD_INFO_H_
+#define COMPONENTS_CRASH_CORE_APP_MINIDUMP_WITH_CRASHPAD_INFO_H_
 
 // Needed for dbghelp.h.
 #include <windows.h>
@@ -33,4 +33,4 @@
 
 }  // namespace crash_reporter
 
-#endif  // COMPONENTS_CRASH_CONTENT_APP_MINIDUMP_WITH_CRASHPAD_INFO_H_
+#endif  // COMPONENTS_CRASH_CORE_APP_MINIDUMP_WITH_CRASHPAD_INFO_H_
diff --git a/components/crash/content/app/run_as_crashpad_handler_win.cc b/components/crash/core/app/run_as_crashpad_handler_win.cc
similarity index 98%
rename from components/crash/content/app/run_as_crashpad_handler_win.cc
rename to components/crash/core/app/run_as_crashpad_handler_win.cc
index 4507e30..bbd94c3 100644
--- a/components/crash/content/app/run_as_crashpad_handler_win.cc
+++ b/components/crash/core/app/run_as_crashpad_handler_win.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/crash/content/app/run_as_crashpad_handler_win.h"
+#include "components/crash/core/app/run_as_crashpad_handler_win.h"
 
 #include <algorithm>
 #include <memory>
diff --git a/components/crash/content/app/run_as_crashpad_handler_win.h b/components/crash/core/app/run_as_crashpad_handler_win.h
similarity index 85%
rename from components/crash/content/app/run_as_crashpad_handler_win.h
rename to components/crash/core/app/run_as_crashpad_handler_win.h
index 2f2c090a..0c9c2e9c 100644
--- a/components/crash/content/app/run_as_crashpad_handler_win.h
+++ b/components/crash/core/app/run_as_crashpad_handler_win.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_CRASH_CONTENT_APP_RUN_AS_CRASHPAD_HANDLER_WIN_H_
-#define COMPONENTS_CRASH_CONTENT_APP_RUN_AS_CRASHPAD_HANDLER_WIN_H_
+#ifndef COMPONENTS_CRASH_CORE_APP_RUN_AS_CRASHPAD_HANDLER_WIN_H_
+#define COMPONENTS_CRASH_CORE_APP_RUN_AS_CRASHPAD_HANDLER_WIN_H_
 
 namespace base {
 class CommandLine;
@@ -30,4 +30,4 @@
 
 }  // namespace crash_reporter
 
-#endif  // COMPONENTS_CRASH_CONTENT_APP_RUN_AS_CRASHPAD_HANDLER_WIN_H_
+#endif  // COMPONENTS_CRASH_CORE_APP_RUN_AS_CRASHPAD_HANDLER_WIN_H_
diff --git a/components/dom_distiller/content/browser/distiller_javascript_service_impl.cc b/components/dom_distiller/content/browser/distiller_javascript_service_impl.cc
index faf05be..81c4d731 100644
--- a/components/dom_distiller/content/browser/distiller_javascript_service_impl.cc
+++ b/components/dom_distiller/content/browser/distiller_javascript_service_impl.cc
@@ -4,15 +4,18 @@
 
 #include "components/dom_distiller/content/browser/distiller_javascript_service_impl.h"
 
+#include "base/strings/string_number_conversions.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 
 namespace dom_distiller {
 
 DistillerJavaScriptServiceImpl::DistillerJavaScriptServiceImpl(
-    DistillerUIHandle* distiller_ui_handle)
-    : distiller_ui_handle_(distiller_ui_handle) {}
+    DistillerUIHandle* distiller_ui_handle,
+    DistilledPagePrefs* distilled_page_prefs)
+    : distiller_ui_handle_(distiller_ui_handle),
+      distilled_page_prefs_(distilled_page_prefs) {}
 
-DistillerJavaScriptServiceImpl::~DistillerJavaScriptServiceImpl() {}
+DistillerJavaScriptServiceImpl::~DistillerJavaScriptServiceImpl() = default;
 
 void DistillerJavaScriptServiceImpl::HandleDistillerOpenSettingsCall() {
   if (!distiller_ui_handle_) {
@@ -22,12 +25,22 @@
   distiller_ui_handle_->OpenSettings();
 }
 
+void DistillerJavaScriptServiceImpl::HandleStoreThemePref(mojom::Theme theme) {
+  distilled_page_prefs_->SetTheme(theme);
+}
+
+void DistillerJavaScriptServiceImpl::HandleStoreFontFamilyPref(
+    mojom::FontFamily font_family) {
+  distilled_page_prefs_->SetFontFamily(font_family);
+}
+
 void CreateDistillerJavaScriptService(
     DistillerUIHandle* distiller_ui_handle,
+    DistilledPagePrefs* distilled_page_prefs,
     mojo::PendingReceiver<mojom::DistillerJavaScriptService> receiver) {
-  mojo::MakeSelfOwnedReceiver(
-      std::make_unique<DistillerJavaScriptServiceImpl>(distiller_ui_handle),
-      std::move(receiver));
+  mojo::MakeSelfOwnedReceiver(std::make_unique<DistillerJavaScriptServiceImpl>(
+                                  distiller_ui_handle, distilled_page_prefs),
+                              std::move(receiver));
 }
 
 }  // namespace dom_distiller
diff --git a/components/dom_distiller/content/browser/distiller_javascript_service_impl.h b/components/dom_distiller/content/browser/distiller_javascript_service_impl.h
index b5c5dd8..30f8a85 100644
--- a/components/dom_distiller/content/browser/distiller_javascript_service_impl.h
+++ b/components/dom_distiller/content/browser/distiller_javascript_service_impl.h
@@ -7,16 +7,21 @@
 
 #include "base/macros.h"
 #include "components/dom_distiller/content/common/mojom/distiller_javascript_service.mojom.h"
+#include "components/dom_distiller/core/distilled_page_prefs.h"
 #include "components/dom_distiller/core/distiller_ui_handle.h"
+#include "components/dom_distiller/core/mojom/distilled_page_prefs.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 
 namespace dom_distiller {
 
+class DistilledPagePrefs;
+
 // This is the receiving end of "distiller" JavaScript object calls.
 class DistillerJavaScriptServiceImpl
     : public mojom::DistillerJavaScriptService {
  public:
-  DistillerJavaScriptServiceImpl(DistillerUIHandle* distiller_ui_handle);
+  DistillerJavaScriptServiceImpl(DistillerUIHandle* distiller_ui_handle,
+                                 DistilledPagePrefs* distilled_page_prefs);
   ~DistillerJavaScriptServiceImpl() override;
 
   // Mojo mojom::DistillerJavaScriptService implementation.
@@ -24,8 +29,12 @@
   // Show the Android view containing Reader Mode settings.
   void HandleDistillerOpenSettingsCall() override;
 
+  void HandleStoreThemePref(mojom::Theme theme) override;
+  void HandleStoreFontFamilyPref(mojom::FontFamily font_family) override;
+
  private:
   DistillerUIHandle* distiller_ui_handle_;
+  DistilledPagePrefs* distilled_page_prefs_;
 
   DISALLOW_COPY_AND_ASSIGN(DistillerJavaScriptServiceImpl);
 };
@@ -33,6 +42,7 @@
 // static
 void CreateDistillerJavaScriptService(
     DistillerUIHandle* distiller_ui_handle,
+    DistilledPagePrefs* distilled_page_prefs,
     mojo::PendingReceiver<mojom::DistillerJavaScriptService> receiver);
 
 }  // namespace dom_distiller
diff --git a/components/dom_distiller/content/common/mojom/BUILD.gn b/components/dom_distiller/content/common/mojom/BUILD.gn
index b256810..e62e5a1 100644
--- a/components/dom_distiller/content/common/mojom/BUILD.gn
+++ b/components/dom_distiller/content/common/mojom/BUILD.gn
@@ -10,4 +10,5 @@
     "distiller_javascript_service.mojom",
     "distiller_page_notifier_service.mojom",
   ]
+  deps = [ "//components/dom_distiller/core/mojom" ]
 }
diff --git a/components/dom_distiller/content/common/mojom/distiller_javascript_service.mojom b/components/dom_distiller/content/common/mojom/distiller_javascript_service.mojom
index ffe03ba..2650950 100644
--- a/components/dom_distiller/content/common/mojom/distiller_javascript_service.mojom
+++ b/components/dom_distiller/content/common/mojom/distiller_javascript_service.mojom
@@ -4,10 +4,16 @@
 
 module dom_distiller.mojom;
 
+import "components/dom_distiller/core/mojom/distilled_page_prefs.mojom";
+
 // This service is implemented by the browser process and is used by the
 // renderer when a distiller JavaScript function is called.
 interface DistillerJavaScriptService {
   // Open the Android view containing settings for Reader Mode; the
   // "distiller.openSettings" function.
   HandleDistillerOpenSettingsCall();
+
+  // Store updated appearance settings as synced prefs.
+  HandleStoreThemePref(Theme theme);
+  HandleStoreFontFamilyPref(FontFamily font_family);
 };
diff --git a/components/dom_distiller/content/renderer/BUILD.gn b/components/dom_distiller/content/renderer/BUILD.gn
index 3cd2cf7..9bffb97 100644
--- a/components/dom_distiller/content/renderer/BUILD.gn
+++ b/components/dom_distiller/content/renderer/BUILD.gn
@@ -19,6 +19,7 @@
     "//base",
     "//components/dom_distiller/content/common/mojom",
     "//components/dom_distiller/core",
+    "//components/dom_distiller/core/mojom",
     "//content/public/common",
     "//content/public/renderer",
     "//gin",
diff --git a/components/dom_distiller/content/renderer/distiller_native_javascript.cc b/components/dom_distiller/content/renderer/distiller_native_javascript.cc
index b67f7fb..ce41552 100644
--- a/components/dom_distiller/content/renderer/distiller_native_javascript.cc
+++ b/components/dom_distiller/content/renderer/distiller_native_javascript.cc
@@ -9,6 +9,8 @@
 #include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/dom_distiller/content/common/mojom/distiller_javascript_service.mojom.h"
+#include "components/dom_distiller/core/distilled_page_prefs.h"
+#include "components/dom_distiller/core/mojom/distilled_page_prefs.mojom.h"
 #include "content/public/renderer/render_frame.h"
 #include "gin/arguments.h"
 #include "gin/function_template.h"
@@ -22,7 +24,21 @@
     content::RenderFrame* render_frame)
     : render_frame_(render_frame) {}
 
-DistillerNativeJavaScript::~DistillerNativeJavaScript() {}
+DistillerNativeJavaScript::~DistillerNativeJavaScript() = default;
+
+void DistillerNativeJavaScript::StoreIntTheme(int int_theme) {
+  auto theme = static_cast<mojom::Theme>(int_theme);
+  if (!mojom::IsKnownEnumValue(theme))
+    return;
+  distiller_js_service_->HandleStoreThemePref(theme);
+}
+
+void DistillerNativeJavaScript::StoreIntFontFamily(int int_font_family) {
+  auto font_family = static_cast<mojom::FontFamily>(int_font_family);
+  if (!mojom::IsKnownEnumValue(font_family))
+    return;
+  distiller_js_service_->HandleStoreFontFamilyPref(font_family);
+}
 
 void DistillerNativeJavaScript::AddJavaScriptObjectToFrame(
     v8::Local<v8::Context> context) {
@@ -46,6 +62,15 @@
       base::BindRepeating(
           &mojom::DistillerJavaScriptService::HandleDistillerOpenSettingsCall,
           base::Unretained(distiller_js_service_.get())));
+
+  BindFunctionToObject(isolate, distiller_obj, "storeThemePref",
+                       base::Bind(&DistillerNativeJavaScript::StoreIntTheme,
+                                  base::Unretained(this)));
+
+  BindFunctionToObject(
+      isolate, distiller_obj, "storeFontFamilyPref",
+      base::Bind(&DistillerNativeJavaScript::StoreIntFontFamily,
+                 base::Unretained(this)));
 }
 
 template <typename Sig>
diff --git a/components/dom_distiller/content/renderer/distiller_native_javascript.h b/components/dom_distiller/content/renderer/distiller_native_javascript.h
index ce21ee7..56ff876 100644
--- a/components/dom_distiller/content/renderer/distiller_native_javascript.h
+++ b/components/dom_distiller/content/renderer/distiller_native_javascript.h
@@ -35,6 +35,11 @@
   // Make sure the mojo service is connected.
   void EnsureServiceConnected();
 
+  // Wrappers to convert integer representations of the pref enums, then send
+  // the enum values to the browser process.
+  void StoreIntTheme(int theme);
+  void StoreIntFontFamily(int font_family);
+
   content::RenderFrame* render_frame_;
   mojo::Remote<mojom::DistillerJavaScriptService> distiller_js_service_;
 };
diff --git a/components/dom_distiller/core/javascript/dom_distiller_viewer.js b/components/dom_distiller/core/javascript/dom_distiller_viewer.js
index d32d1b0..4387308 100644
--- a/components/dom_distiller/core/javascript/dom_distiller_viewer.js
+++ b/components/dom_distiller/core/javascript/dom_distiller_viewer.js
@@ -64,22 +64,6 @@
   document.body.setAttribute('dir', direction);
 }
 
-// These classes must agree with the font classes in distilledpage.css.
-const fontFamilyClasses = ['sans-serif', 'serif', 'monospace'];
-function useFontFamily(fontFamily) {
-  fontFamilyClasses.forEach(
-      (element) =>
-          document.body.classList.toggle(element, element === fontFamily));
-}
-
-// These classes must agree with the theme classes in distilledpage.css.
-const themeClasses = ['light', 'dark', 'sepia'];
-function useTheme(theme) {
-  themeClasses.forEach(
-      (element) => document.body.classList.toggle(element, element === theme));
-  updateToolbarColor(theme);
-}
-
 function getClassFromElement(element, classList) {
   let foundClass = classList[0];
   classList.forEach((cls) => {
@@ -90,6 +74,38 @@
   return foundClass;
 }
 
+// These classes must agree with the font classes in distilledpage.css.
+// The order must also agree with the corresponding enum in
+// distilled_page_prefs.mojom.
+const fontFamilyClasses = ['sans-serif', 'serif', 'monospace'];
+function useFontFamily(fontFamily) {
+  fontFamilyClasses.forEach(
+      (element) =>
+          document.body.classList.toggle(element, element === fontFamily));
+  distiller.storeFontFamilyPref(fontFamilyClasses.indexOf(fontFamily));
+}
+
+// These classes must agree with the theme classes in distilledpage.css.
+// The order must also agree with the corresponding enum in
+// distilled_page_prefs.mojom.
+const themeClasses = ['light', 'dark', 'sepia'];
+function useTheme(theme) {
+  themeClasses.forEach(
+      (element) => document.body.classList.toggle(element, element === theme));
+  updateToolbarColor(theme);
+  distiller.storeThemePref(themeClasses.indexOf(theme));
+}
+
+// TODO(https://crbug.com/1027612): Rename this so that the distinction between
+// it and useTheme() is more obvious.
+function updateThemeSelection(theme) {
+  const selectedElem =
+      document.querySelector('.theme-option input[value="' + theme + '"');
+  if (selectedElem) {
+    selectedElem.checked = true;
+  }
+}
+
 function updateToolbarColor(theme) {
   let toolbarColor;
   if (theme === 'sepia') {
diff --git a/components/gwp_asan/client/BUILD.gn b/components/gwp_asan/client/BUILD.gn
index 8145cf7..53c0793 100644
--- a/components/gwp_asan/client/BUILD.gn
+++ b/components/gwp_asan/client/BUILD.gn
@@ -47,7 +47,7 @@
   ]
 
   if (is_android) {
-    deps += [ "//components/crash/content/app" ]
+    deps += [ "//components/crash/core/app" ]
   }
 }
 
diff --git a/components/gwp_asan/client/DEPS b/components/gwp_asan/client/DEPS
index 24a3177..7c3c991 100644
--- a/components/gwp_asan/client/DEPS
+++ b/components/gwp_asan/client/DEPS
@@ -1,4 +1,4 @@
 include_rules = [
+  "+components/crash/core/app/crashpad.h",
   "+components/crash/core/common/crash_key.h",
-  "+components/crash/content/app/crashpad.h",
 ]
diff --git a/components/gwp_asan/client/guarded_page_allocator.cc b/components/gwp_asan/client/guarded_page_allocator.cc
index 3c1bc44..7031854b 100644
--- a/components/gwp_asan/client/guarded_page_allocator.cc
+++ b/components/gwp_asan/client/guarded_page_allocator.cc
@@ -22,7 +22,7 @@
 #include "components/gwp_asan/common/pack_stack_trace.h"
 
 #if defined(OS_ANDROID)
-#include "components/crash/content/app/crashpad.h"  // nogncheck
+#include "components/crash/core/app/crashpad.h"  // nogncheck
 #endif
 
 #if defined(OS_MACOSX)
diff --git a/components/gwp_asan/crash_handler/BUILD.gn b/components/gwp_asan/crash_handler/BUILD.gn
index dbb66c0a..c06efe9 100644
--- a/components/gwp_asan/crash_handler/BUILD.gn
+++ b/components/gwp_asan/crash_handler/BUILD.gn
@@ -62,9 +62,8 @@
       metadata = {
         shared_libraries = [ "$root_out_dir/libchrome_crashpad_handler.so" ]
       }
-      deps += [
-        "//components/crash/content/app:chrome_crashpad_handler_named_as_so",
-      ]
+      deps +=
+          [ "//components/crash/core/app:chrome_crashpad_handler_named_as_so" ]
     }
   }
 }
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index b91ce442..070e945 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -514,6 +514,7 @@
     "android_affiliation/facet_manager_unittest.cc",
     "browser_save_password_progress_logger_unittest.cc",
     "bulk_leak_check_service_unittest.cc",
+    "compromised_credentials_observer_unittest.cc",
     "compromised_credentials_table_unittest.cc",
     "credential_cache_unittest.cc",
     "credential_manager_impl_unittest.cc",
diff --git a/components/password_manager/core/browser/compromised_credentials_observer.cc b/components/password_manager/core/browser/compromised_credentials_observer.cc
index f923c62..5340aea 100644
--- a/components/password_manager/core/browser/compromised_credentials_observer.cc
+++ b/components/password_manager/core/browser/compromised_credentials_observer.cc
@@ -36,23 +36,29 @@
       !base::FeatureList::IsEnabled(password_manager::features::kPasswordCheck))
     return;
 
-  // If the change is an UPDATE and the password did not change, there is
-  // nothing to remove. If the change is an ADD there is also nothing to remove.
-  if (changes[0].type() == PasswordStoreChange::ADD ||
-      (changes[0].type() == PasswordStoreChange::UPDATE &&
-       !changes[0].password_changed())) {
-    return;
+  for (const PasswordStoreChange& change : changes) {
+    // New passwords are not interesting.
+    if (change.type() == PasswordStoreChange::ADD)
+      continue;
+    // Updates are interesting only when they change the password value.
+    if (change.type() == PasswordStoreChange::UPDATE &&
+        !change.password_changed())
+      continue;
+    auto reason = RemoveCompromisedCredentialsReason::kUpdate;
+    if (change.type() == PasswordStoreChange::REMOVE &&
+        std::none_of(changes.begin(), changes.end(), [](const auto& change) {
+          return change.type() == PasswordStoreChange::ADD;
+        })) {
+      reason = RemoveCompromisedCredentialsReason::kRemove;
+    }
+    store_->RemoveCompromisedCredentials(change.form().signon_realm,
+                                         change.form().username_value, reason);
+    UMA_HISTOGRAM_ENUMERATION(
+        "PasswordManager.RemoveCompromisedCredentials",
+        reason == RemoveCompromisedCredentialsReason::kUpdate
+            ? PasswordStoreChange::UPDATE
+            : PasswordStoreChange::REMOVE);
   }
-
-  // An internal update could be a (REMOVE + ADD) or (UPDATE).
-  RemoveCompromisedCredentialsReason reason =
-      changes.size() != 1 || changes[0].type() == PasswordStoreChange::UPDATE
-          ? RemoveCompromisedCredentialsReason::kUpdate
-          : RemoveCompromisedCredentialsReason::kRemove;
-  store_->RemoveCompromisedCredentials(
-      changes[0].form().signon_realm, changes[0].form().username_value, reason);
-  UMA_HISTOGRAM_ENUMERATION("PasswordManager.RemoveCompromisedCredentials",
-                            changes[0].type());
 }
 
-}  // namespace password_manager
\ No newline at end of file
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/compromised_credentials_observer_unittest.cc b/components/password_manager/core/browser/compromised_credentials_observer_unittest.cc
new file mode 100644
index 0000000..13196eb
--- /dev/null
+++ b/components/password_manager/core/browser/compromised_credentials_observer_unittest.cc
@@ -0,0 +1,167 @@
+// 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/password_manager/core/browser/compromised_credentials_observer.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#include "components/password_manager/core/browser/mock_password_store.h"
+#include "components/password_manager/core/browser/password_store_change.h"
+#include "components/password_manager/core/common/password_manager_features.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+namespace {
+
+constexpr char kHistogramName[] =
+    "PasswordManager.RemoveCompromisedCredentials";
+constexpr char kSite[] = "https://example.com/path";
+constexpr char kUsername[] = "peter";
+constexpr char kUsernameNew[] = "ana";
+
+autofill::PasswordForm TestForm(base::StringPiece username) {
+  autofill::PasswordForm form;
+  form.origin = GURL(kSite);
+  form.signon_realm = form.origin.GetOrigin().spec();
+  form.username_value = base::ASCIIToUTF16(username);
+  form.password_value = base::ASCIIToUTF16("12345");
+  return form;
+}
+
+class CompromisedCredentialsObserverTest : public testing::Test {
+ public:
+  CompromisedCredentialsObserverTest() {
+    feature_list_.InitAndEnableFeature(features::kPasswordCheck);
+    mock_store_->Init(nullptr);
+    observer_.Initialize();
+  }
+
+  ~CompromisedCredentialsObserverTest() override {
+    mock_store_->ShutdownOnUIThread();
+  }
+
+  void WaitForPasswordStore() { task_environment_.RunUntilIdle(); }
+  MockPasswordStore& store() { return *mock_store_; }
+  base::HistogramTester& histogram_tester() { return histogram_tester_; }
+  PasswordStore::Observer& observer() { return observer_; }
+
+ private:
+  base::test::SingleThreadTaskEnvironment task_environment_;
+  base::test::ScopedFeatureList feature_list_;
+  scoped_refptr<MockPasswordStore> mock_store_ =
+      base::MakeRefCounted<testing::StrictMock<MockPasswordStore>>();
+  base::HistogramTester histogram_tester_;
+  CompromisedCredentialsObserver observer_{mock_store_.get()};
+};
+
+TEST_F(CompromisedCredentialsObserverTest, DeletePassword) {
+  const autofill::PasswordForm form = TestForm(kUsername);
+  EXPECT_CALL(store(), RemoveCompromisedCredentialsImpl(
+                           form.signon_realm, form.username_value,
+                           RemoveCompromisedCredentialsReason::kRemove));
+  observer().OnLoginsChanged(
+      {PasswordStoreChange(PasswordStoreChange::REMOVE, form)});
+  WaitForPasswordStore();
+  histogram_tester().ExpectUniqueSample(kHistogramName,
+                                        PasswordStoreChange::REMOVE, 1);
+}
+
+TEST_F(CompromisedCredentialsObserverTest, UpdateFormNoPasswordChange) {
+  const autofill::PasswordForm form = TestForm(kUsername);
+  EXPECT_CALL(store(), RemoveCompromisedCredentialsImpl).Times(0);
+  observer().OnLoginsChanged(
+      {PasswordStoreChange(PasswordStoreChange::UPDATE, form, 1000, false)});
+  WaitForPasswordStore();
+  histogram_tester().ExpectTotalCount(kHistogramName, 0);
+}
+
+TEST_F(CompromisedCredentialsObserverTest, UpdatePassword) {
+  const autofill::PasswordForm form = TestForm(kUsername);
+  EXPECT_CALL(store(), RemoveCompromisedCredentialsImpl(
+                           form.signon_realm, form.username_value,
+                           RemoveCompromisedCredentialsReason::kUpdate));
+  observer().OnLoginsChanged(
+      {PasswordStoreChange(PasswordStoreChange::UPDATE, form, 1000, true)});
+  WaitForPasswordStore();
+  histogram_tester().ExpectUniqueSample(kHistogramName,
+                                        PasswordStoreChange::UPDATE, 1);
+}
+
+TEST_F(CompromisedCredentialsObserverTest, UpdateTwice) {
+  const autofill::PasswordForm form = TestForm(kUsername);
+  EXPECT_CALL(store(), RemoveCompromisedCredentialsImpl(
+                           form.signon_realm, form.username_value,
+                           RemoveCompromisedCredentialsReason::kUpdate));
+  observer().OnLoginsChanged(
+      {PasswordStoreChange(PasswordStoreChange::UPDATE, TestForm(kUsernameNew),
+                           1000, false),
+       PasswordStoreChange(PasswordStoreChange::UPDATE, form, 1001, true)});
+  WaitForPasswordStore();
+  histogram_tester().ExpectUniqueSample(kHistogramName,
+                                        PasswordStoreChange::UPDATE, 1);
+}
+
+TEST_F(CompromisedCredentialsObserverTest, AddPassword) {
+  const autofill::PasswordForm form = TestForm(kUsername);
+  EXPECT_CALL(store(), RemoveCompromisedCredentialsImpl).Times(0);
+  observer().OnLoginsChanged(
+      {PasswordStoreChange(PasswordStoreChange::ADD, form)});
+  WaitForPasswordStore();
+  histogram_tester().ExpectTotalCount(kHistogramName, 0);
+}
+
+TEST_F(CompromisedCredentialsObserverTest, AddReplacePassword) {
+  autofill::PasswordForm form = TestForm(kUsername);
+  PasswordStoreChange remove(PasswordStoreChange::REMOVE, form);
+  form.password_value = base::ASCIIToUTF16("new_password_12345");
+  PasswordStoreChange add(PasswordStoreChange::ADD, form);
+  EXPECT_CALL(store(), RemoveCompromisedCredentialsImpl(
+                           form.signon_realm, form.username_value,
+                           RemoveCompromisedCredentialsReason::kUpdate));
+  observer().OnLoginsChanged({remove, add});
+  WaitForPasswordStore();
+  histogram_tester().ExpectUniqueSample(kHistogramName,
+                                        PasswordStoreChange::UPDATE, 1);
+}
+
+TEST_F(CompromisedCredentialsObserverTest, UpdateWithPrimaryKey) {
+  const autofill::PasswordForm old_form = TestForm(kUsername);
+  PasswordStoreChange remove(PasswordStoreChange::REMOVE, old_form);
+  PasswordStoreChange add(PasswordStoreChange::ADD, TestForm(kUsernameNew));
+  EXPECT_CALL(store(), RemoveCompromisedCredentialsImpl(
+                           old_form.signon_realm, old_form.username_value,
+                           RemoveCompromisedCredentialsReason::kUpdate));
+  observer().OnLoginsChanged({remove, add});
+  WaitForPasswordStore();
+  histogram_tester().ExpectUniqueSample(kHistogramName,
+                                        PasswordStoreChange::UPDATE, 1);
+}
+
+TEST_F(CompromisedCredentialsObserverTest, UpdateWithPrimaryKey_RemoveTwice) {
+  const autofill::PasswordForm old_form = TestForm(kUsername);
+  PasswordStoreChange remove_old(PasswordStoreChange::REMOVE, old_form);
+  const autofill::PasswordForm conflicting_new_form = TestForm(kUsernameNew);
+  PasswordStoreChange remove_conflicting(PasswordStoreChange::REMOVE,
+                                         conflicting_new_form);
+  PasswordStoreChange add(PasswordStoreChange::ADD, TestForm(kUsernameNew));
+  EXPECT_CALL(store(), RemoveCompromisedCredentialsImpl(
+                           old_form.signon_realm, old_form.username_value,
+                           RemoveCompromisedCredentialsReason::kUpdate));
+  EXPECT_CALL(store(), RemoveCompromisedCredentialsImpl(
+                           conflicting_new_form.signon_realm,
+                           conflicting_new_form.username_value,
+                           RemoveCompromisedCredentialsReason::kUpdate));
+  observer().OnLoginsChanged({remove_old, remove_conflicting, add});
+  WaitForPasswordStore();
+  histogram_tester().ExpectUniqueSample(kHistogramName,
+                                        PasswordStoreChange::UPDATE, 2);
+}
+
+}  // namespace
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/form_parsing/password_field_prediction.cc b/components/password_manager/core/browser/form_parsing/password_field_prediction.cc
index 0803d97..3bdfb029 100644
--- a/components/password_manager/core/browser/form_parsing/password_field_prediction.cc
+++ b/components/password_manager/core/browser/form_parsing/password_field_prediction.cc
@@ -9,6 +9,7 @@
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/signatures_util.h"
 
+using autofill::AutofillField;
 using autofill::FieldSignature;
 using autofill::FormData;
 using autofill::FormStructure;
@@ -16,6 +17,31 @@
 
 namespace password_manager {
 
+namespace {
+
+ServerFieldType GetServerType(const AutofillField& field) {
+  // The main server predictions is in |field.server_type()| but the server can
+  // send additional predictions in |field.server_predictions()|. This function
+  // chooses relevant for Password Manager predictions.
+
+  // 1. If there is cvc prediction returns it.
+  for (const auto& predictions : field.server_predictions()) {
+    if (predictions.type() == autofill::CREDIT_CARD_VERIFICATION_CODE)
+      return ServerFieldType(predictions.type());
+  }
+
+  // 2. If there is password related prediction returns it.
+  for (const auto& predictions : field.server_predictions()) {
+    ServerFieldType type = ServerFieldType(predictions.type());
+    if (DeriveFromServerFieldType(type) != CredentialFieldType::kNone)
+      return type;
+  }
+
+  // 3. Returns the main prediction.
+  return field.server_type();
+}
+}  // namespace
+
 CredentialFieldType DeriveFromServerFieldType(ServerFieldType type) {
   switch (type) {
     case autofill::USERNAME:
@@ -67,7 +93,7 @@
 
   std::vector<PasswordFieldPrediction> field_predictions;
   for (const auto& field : form_structure) {
-    ServerFieldType server_type = field->server_type();
+    ServerFieldType server_type = GetServerType(*field);
 
     if (!explicit_confirmation_hint_present &&
         (server_type == autofill::ACCOUNT_CREATION_PASSWORD ||
diff --git a/components/password_manager/core/browser/form_parsing/password_field_prediction_unittest.cc b/components/password_manager/core/browser/form_parsing/password_field_prediction_unittest.cc
index b33c9cb..05b787c 100644
--- a/components/password_manager/core/browser/form_parsing/password_field_prediction_unittest.cc
+++ b/components/password_manager/core/browser/form_parsing/password_field_prediction_unittest.cc
@@ -16,6 +16,7 @@
 using autofill::ACCOUNT_CREATION_PASSWORD;
 using autofill::AutofillField;
 using autofill::CONFIRMATION_PASSWORD;
+using autofill::CREDIT_CARD_VERIFICATION_CODE;
 using autofill::EMAIL_ADDRESS;
 using autofill::FormData;
 using autofill::FormFieldData;
@@ -44,6 +45,7 @@
     ServerFieldType input_type;
     ServerFieldType expected_type;
     bool may_use_prefilled_placeholder;
+    std::vector<ServerFieldType> additional_types;
   } test_fields[] = {
       {"full_name", "text", UNKNOWN_TYPE, UNKNOWN_TYPE, false},
       // Password Manager is interested only in credential related types.
@@ -51,7 +53,19 @@
       {"username", "text", USERNAME, USERNAME, true},
       {"Password", "password", PASSWORD, PASSWORD, false},
       {"confirm_password", "password", CONFIRMATION_PASSWORD,
-       CONFIRMATION_PASSWORD, true}};
+       CONFIRMATION_PASSWORD, true},
+      // username in |additional_types| takes precedence.
+      {"email", "text", EMAIL_ADDRESS, USERNAME, false, {USERNAME}},
+      // cvc in |additional_types| takes precedence.
+      {"cvc",
+       "password",
+       PASSWORD,
+       CREDIT_CARD_VERIFICATION_CODE,
+       false,
+       {CREDIT_CARD_VERIFICATION_CODE}},
+      // non-password, non-cvc types in |additional_types| are ignored.
+      {"email", "text", UNKNOWN_TYPE, UNKNOWN_TYPE, false, {EMAIL_ADDRESS}},
+  };
 
   FormData form_data;
   for (size_t i = 0; i < base::size(test_fields); ++i) {
@@ -68,10 +82,16 @@
     AutofillField* field = form_structure.field(i);
     field->set_server_type(test_fields[i].input_type);
 
-    FieldPrediction prediction;
-    prediction.set_may_use_prefilled_placeholder(
+    std::vector<FieldPrediction> predictions(1);
+    predictions[0].set_may_use_prefilled_placeholder(
         test_fields[i].may_use_prefilled_placeholder);
-    field->set_server_predictions({prediction});
+
+    for (ServerFieldType type : test_fields[i].additional_types) {
+      FieldPrediction additional_prediction;
+      additional_prediction.set_type(type);
+      predictions.push_back(additional_prediction);
+    }
+    field->set_server_predictions(predictions);
   }
 
   constexpr int driver_id = 1000;
diff --git a/components/password_manager/core/browser/password_autofill_manager.cc b/components/password_manager/core/browser/password_autofill_manager.cc
index eb057a2..39f054d 100644
--- a/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/components/password_manager/core/browser/password_autofill_manager.cc
@@ -337,7 +337,7 @@
     return;
   UpdatePopup(ReplaceUnlockButtonWithLoadingIndicator(
       autofill_client_->GetPopupSuggestions(), unlock_item));
-  autofill_client_->PinPopupViewUntilUpdate();
+  autofill_client_->PinPopupView();
   password_client_->TriggerReauthForAccount(
       account_id,
       base::BindOnce(&PasswordAutofillManager::OnUnlockReauthCompleted,
diff --git a/components/password_manager/core/browser/password_autofill_manager_unittest.cc b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
index adba7c0..9da59c8 100644
--- a/components/password_manager/core/browser/password_autofill_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
@@ -156,7 +156,7 @@
                     bool autoselect_first_suggestion,
                     PopupType popup_type,
                     base::WeakPtr<autofill::AutofillPopupDelegate> delegate));
-  MOCK_METHOD0(PinPopupViewUntilUpdate, void());
+  MOCK_METHOD0(PinPopupView, void());
   MOCK_CONST_METHOD0(GetPopupSuggestions,
                      base::span<const autofill::Suggestion>());
   MOCK_METHOD2(UpdatePopup,
@@ -438,7 +438,7 @@
                autofill::POPUP_ITEM_ID_PASSWORD_ENTRY,
                autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY}))),
           PopupType::kPasswords));
-  EXPECT_CALL(autofill_client, PinPopupViewUntilUpdate);
+  EXPECT_CALL(autofill_client, PinPopupView);
   EXPECT_CALL(client, TriggerReauthForAccount(kAliceId, _));
   EXPECT_CALL(autofill_client, GetPopupSuggestions())
       .WillOnce(Return(CreateTestSuggestions(
@@ -470,7 +470,7 @@
                autofill::POPUP_ITEM_ID_PASSWORD_ENTRY,
                autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY}))),
           PopupType::kPasswords));
-  EXPECT_CALL(autofill_client, PinPopupViewUntilUpdate);
+  EXPECT_CALL(autofill_client, PinPopupView);
   EXPECT_CALL(client, TriggerReauthForAccount(kAliceId, _));
   EXPECT_CALL(autofill_client, GetPopupSuggestions())
       .WillOnce(Return(CreateTestSuggestions(
@@ -501,7 +501,7 @@
   EXPECT_CALL(autofill_client, UpdatePopup);
 
   // As soon as the waiting state is pending, the next update resets the popup.
-  EXPECT_CALL(autofill_client, PinPopupViewUntilUpdate).WillOnce([&] {
+  EXPECT_CALL(autofill_client, PinPopupView).WillOnce([&] {
     testing::Mock::VerifyAndClear(&autofill_client);
     EXPECT_CALL(autofill_client, GetPopupSuggestions)
         .WillOnce(Return(CreateTestSuggestions(
@@ -546,7 +546,7 @@
   EXPECT_CALL(autofill_client, UpdatePopup);
 
   // As soon as the waiting state is pending, the next update resets the popup.
-  EXPECT_CALL(autofill_client, PinPopupViewUntilUpdate).WillOnce([&] {
+  EXPECT_CALL(autofill_client, PinPopupView).WillOnce([&] {
     testing::Mock::VerifyAndClear(&autofill_client);
     EXPECT_CALL(autofill_client, GetPopupSuggestions)
         .WillOnce(Return(CreateTestSuggestions(
@@ -587,7 +587,7 @@
       .WillOnce(Return(CreateTestSuggestions(
           /*has_opt_in_and_fill=*/true, /*has_opt_in_and_generate*/ false)));
   EXPECT_CALL(autofill_client, UpdatePopup);
-  EXPECT_CALL(autofill_client, PinPopupViewUntilUpdate);
+  EXPECT_CALL(autofill_client, PinPopupView);
 
   EXPECT_CALL(client, TriggerReauthForAccount(kAliceId, _))
       .WillOnce([](const auto& id, auto reauth_callback) {
@@ -618,7 +618,7 @@
       .WillOnce(Return(CreateTestSuggestions(
           /*has_opt_in_and_fill=*/false, /*has_opt_in_and_generate*/ true)));
   EXPECT_CALL(autofill_client, UpdatePopup);
-  EXPECT_CALL(autofill_client, PinPopupViewUntilUpdate);
+  EXPECT_CALL(autofill_client, PinPopupView);
 
   EXPECT_CALL(client, TriggerReauthForAccount(kAliceId, _))
       .WillOnce([](const auto& id, auto reauth_callback) {
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index e87978f..6f1abbd 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -828,6 +828,9 @@
 }
 
 void PasswordManager::OnLoginSuccessful() {
+  if (autofill_assistant_mode_ == AutofillAssistantMode::kManuallyCuratedScript)
+    return;
+
   std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
   if (password_manager_util::IsLoggingActive(client_)) {
     logger.reset(
diff --git a/components/password_manager/core/browser/password_manager.h b/components/password_manager/core/browser/password_manager.h
index 13f35ced..ff29b59 100644
--- a/components/password_manager/core/browser/password_manager.h
+++ b/components/password_manager/core/browser/password_manager.h
@@ -35,7 +35,7 @@
 namespace autofill {
 struct FormData;
 class FormStructure;
-}
+}  // namespace autofill
 
 namespace password_manager {
 
@@ -47,6 +47,18 @@
 class PasswordManagerMetricsRecorder;
 struct PossibleUsernameData;
 
+// Define the modes of collaboration between Password Manager and Autofill
+// Assistant (who handles form submissions, whether to show prompts or not).
+enum class AutofillAssistantMode {
+  // Autofill Assistant is not running. Password Manager operates in the regular
+  // mode - it handles submissions and shows prompts.
+  kNotRunning = 0,
+  // Autofill Assistant runs a manually curated script. The password manager
+  // is basically off - it does not handle submissions and therefore does not
+  // show prompts. The script does all the work instead.
+  kManuallyCuratedScript
+};
+
 // Per-tab password manager. Handles creation and management of UI elements,
 // receiving password form data from the renderer and managing the password
 // database through the PasswordStore.
@@ -179,6 +191,11 @@
   // Notifies that Credential Management API function store() is called.
   void NotifyStorePasswordCalled();
 
+  void set_autofill_assistance_mode(
+      AutofillAssistantMode autofill_assistant_mode) {
+    autofill_assistant_mode_ = autofill_assistant_mode;
+  }
+
 #if defined(OS_IOS)
   // TODO(https://crbug.com/866444): Use these methods instead olds ones when
   // the old parser is gone.
@@ -354,6 +371,11 @@
 
   base::Optional<PossibleUsernameData> possible_username_;
 
+  // By default Autofill Assistant is not running. Password Manager handles
+  // submissions and shows prompts.
+  AutofillAssistantMode autofill_assistant_mode_ =
+      AutofillAssistantMode::kNotRunning;
+
   DISALLOW_COPY_AND_ASSIGN(PasswordManager);
 };
 
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index 33eb222..e2908c9 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -218,7 +218,9 @@
   arg0->OnGetPasswordStoreResults(std::vector<std::unique_ptr<PasswordForm>>());
 }
 
-ACTION_P(SaveToScopedPtr, scoped) { scoped->reset(arg0); }
+ACTION_P(SaveToScopedPtr, scoped) {
+  scoped->reset(arg0);
+}
 
 ACTION(DeletePtr) {
   delete arg0;
@@ -591,41 +593,41 @@
 }
 
 TEST_F(PasswordManagerTest, GeneratedPasswordFormSubmitEmptyStore) {
-    // Test that generated passwords are stored without asking the user.
-    std::vector<FormData> observed;
-    FormData form_data(MakeFormDataWithOnlyNewPasswordField());
-    observed.push_back(form_data);
-    EXPECT_CALL(*store_, GetLogins(_, _))
-        .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
-    manager()->OnPasswordFormsParsed(&driver_, observed);
-    manager()->OnPasswordFormsRendered(&driver_, observed, true);
+  // Test that generated passwords are stored without asking the user.
+  std::vector<FormData> observed;
+  FormData form_data(MakeFormDataWithOnlyNewPasswordField());
+  observed.push_back(form_data);
+  EXPECT_CALL(*store_, GetLogins(_, _))
+      .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
+  manager()->OnPasswordFormsParsed(&driver_, observed);
+  manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
-    // Simulate the user generating the password and submitting the form.
-    EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
-        .WillRepeatedly(Return(true));
-    EXPECT_CALL(*store_, AddLogin(_));
-    manager()->OnPresaveGeneratedPassword(&driver_, form_data,
-                                          form_data.fields[1].value);
-    OnPasswordFormSubmitted(form_data);
+  // Simulate the user generating the password and submitting the form.
+  EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(*store_, AddLogin(_));
+  manager()->OnPresaveGeneratedPassword(&driver_, form_data,
+                                        form_data.fields[1].value);
+  OnPasswordFormSubmitted(form_data);
 
-    // The user should not need to confirm saving as they have already given
-    // consent by using the generated password. The form should be saved once
-    // navigation occurs. The client will be informed that automatic saving has
-    // occurred.
-    EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
-    PasswordForm form_to_save;
-    EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, _))
-        .WillOnce(SaveArg<0>(&form_to_save));
-    EXPECT_CALL(client_, AutomaticPasswordSaveIndicator());
+  // The user should not need to confirm saving as they have already given
+  // consent by using the generated password. The form should be saved once
+  // navigation occurs. The client will be informed that automatic saving has
+  // occurred.
+  EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
+  PasswordForm form_to_save;
+  EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, _))
+      .WillOnce(SaveArg<0>(&form_to_save));
+  EXPECT_CALL(client_, AutomaticPasswordSaveIndicator());
 
-    // Now the password manager waits for the navigation to complete.
-    observed.clear();
-    manager()->OnPasswordFormsParsed(&driver_, observed);
-    manager()->OnPasswordFormsRendered(&driver_, observed, true);
-    EXPECT_EQ(form_data.fields[0].value, form_to_save.username_value);
-    // What was "new password" field in the submitted form, becomes the current
-    // password field in the form to save.
-    EXPECT_EQ(form_data.fields[1].value, form_to_save.password_value);
+  // Now the password manager waits for the navigation to complete.
+  observed.clear();
+  manager()->OnPasswordFormsParsed(&driver_, observed);
+  manager()->OnPasswordFormsRendered(&driver_, observed, true);
+  EXPECT_EQ(form_data.fields[0].value, form_to_save.username_value);
+  // What was "new password" field in the submitted form, becomes the current
+  // password field in the form to save.
+  EXPECT_EQ(form_data.fields[1].value, form_to_save.password_value);
 }
 
 #if defined(OS_IOS)
@@ -1349,34 +1351,33 @@
 
 TEST_F(PasswordManagerTest,
        HashSavedOnGaiaFormWithSkipSavePasswordAndToNTPNavigation) {
-    EXPECT_CALL(*store_, GetLogins(_, _))
-        .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
-    EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0);
-    FormData form_data(MakeSimpleGAIAFormData());
-    // Simulate that this is Gaia form that should be ignored for
-    // saving/filling.
-    form_data.is_gaia_with_skip_save_password_form = true;
-    EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
-        .WillRepeatedly(Return(true));
-    manager()->OnPasswordFormsParsed(&driver_, {form_data});
+  EXPECT_CALL(*store_, GetLogins(_, _))
+      .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
+  EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0);
+  FormData form_data(MakeSimpleGAIAFormData());
+  // Simulate that this is Gaia form that should be ignored for
+  // saving/filling.
+  form_data.is_gaia_with_skip_save_password_form = true;
+  EXPECT_CALL(client_, IsSavingAndFillingEnabled(form_data.url))
+      .WillRepeatedly(Return(true));
+  manager()->OnPasswordFormsParsed(&driver_, {form_data});
 
-    ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
-        .WillByDefault(Return(true));
-    ON_CALL(*client_.GetStoreResultFilter(), ShouldSave(_))
-        .WillByDefault(Return(false));
-    ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
-        .WillByDefault(Return(true));
+  ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
+      .WillByDefault(Return(true));
+  ON_CALL(*client_.GetStoreResultFilter(), ShouldSave(_))
+      .WillByDefault(Return(false));
+  ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
+      .WillByDefault(Return(true));
 
-    EXPECT_CALL(
-        *store_,
-        SaveGaiaPasswordHash(
-            "googleuser", form_data.fields[1].value,
-            /*is_primary_account=*/true,
-            metrics_util::GaiaPasswordHashChange::SAVED_IN_CONTENT_AREA));
+  EXPECT_CALL(*store_,
+              SaveGaiaPasswordHash(
+                  "googleuser", form_data.fields[1].value,
+                  /*is_primary_account=*/true,
+                  metrics_util::GaiaPasswordHashChange::SAVED_IN_CONTENT_AREA));
 
-    EXPECT_CALL(client_, IsNewTabPage()).WillRepeatedly(Return(true));
-    OnPasswordFormSubmitted(form_data);
-    manager()->DidNavigateMainFrame(false);
+  EXPECT_CALL(client_, IsNewTabPage()).WillRepeatedly(Return(true));
+  OnPasswordFormSubmitted(form_data);
+  manager()->DidNavigateMainFrame(false);
 }
 #endif
 
@@ -2974,28 +2975,28 @@
       .WillRepeatedly(Return(true));
   EXPECT_CALL(*store_, GetLogins(_, _))
       .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
-    manager()->OnPasswordFormsParsed(nullptr, {});
+  manager()->OnPasswordFormsParsed(nullptr, {});
 
-    base::HistogramTester histogram_tester;
-    ukm::TestAutoSetUkmRecorder test_ukm_recorder;
-    auto metrics_recorder = std::make_unique<PasswordManagerMetricsRecorder>(
-        1234, GURL("http://example.com"));
-    EXPECT_CALL(client_, GetMetricsRecorder())
-        .WillRepeatedly(Return(metrics_recorder.get()));
+  base::HistogramTester histogram_tester;
+  ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+  auto metrics_recorder = std::make_unique<PasswordManagerMetricsRecorder>(
+      1234, GURL("http://example.com"));
+  EXPECT_CALL(client_, GetMetricsRecorder())
+      .WillRepeatedly(Return(metrics_recorder.get()));
 
-    FormData unobserved_form_data = MakeSimpleFormData();
-    manager()->OnPasswordFormSubmitted(nullptr, unobserved_form_data);
+  FormData unobserved_form_data = MakeSimpleFormData();
+  manager()->OnPasswordFormSubmitted(nullptr, unobserved_form_data);
 
-    histogram_tester.ExpectUniqueSample(
-        "PasswordManager.ProvisionalSaveFailure",
-        PasswordManagerMetricsRecorder::NO_MATCHING_FORM, 1);
-    // Flush the UKM reports.
-    EXPECT_CALL(client_, GetMetricsRecorder()).WillRepeatedly(Return(nullptr));
-    metrics_recorder.reset();
-    CheckMetricHasValue(
-        test_ukm_recorder, ukm::builders::PageWithPassword::kEntryName,
-        ukm::builders::PageWithPassword::kProvisionalSaveFailureName,
-        PasswordManagerMetricsRecorder::NO_MATCHING_FORM);
+  histogram_tester.ExpectUniqueSample(
+      "PasswordManager.ProvisionalSaveFailure",
+      PasswordManagerMetricsRecorder::NO_MATCHING_FORM, 1);
+  // Flush the UKM reports.
+  EXPECT_CALL(client_, GetMetricsRecorder()).WillRepeatedly(Return(nullptr));
+  metrics_recorder.reset();
+  CheckMetricHasValue(
+      test_ukm_recorder, ukm::builders::PageWithPassword::kEntryName,
+      ukm::builders::PageWithPassword::kProvisionalSaveFailureName,
+      PasswordManagerMetricsRecorder::NO_MATCHING_FORM);
 }
 
 namespace {
@@ -3186,23 +3187,23 @@
 // Tests that no save prompt from form manager is shown when Credentials
 // Management API function store is called.
 TEST_F(PasswordManagerTest, NoSavePromptAfterStoreCalled) {
-    EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
-        .WillRepeatedly(Return(true));
+  EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
+      .WillRepeatedly(Return(true));
 
-    FormData form_data(MakeSimpleFormData());
-    EXPECT_CALL(*store_, GetLogins(_, _))
-        .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
+  FormData form_data(MakeSimpleFormData());
+  EXPECT_CALL(*store_, GetLogins(_, _))
+      .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
 
-    manager()->OnPasswordFormsParsed(&driver_, {form_data});
+  manager()->OnPasswordFormsParsed(&driver_, {form_data});
 
-    // Simulate that navigator.credentials.store function is called.
-    manager()->NotifyStorePasswordCalled();
+  // Simulate that navigator.credentials.store function is called.
+  manager()->NotifyStorePasswordCalled();
 
-    OnPasswordFormSubmitted(form_data);
-    EXPECT_FALSE(manager()->GetSubmittedManagerForTest());
-    EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
+  OnPasswordFormSubmitted(form_data);
+  EXPECT_FALSE(manager()->GetSubmittedManagerForTest());
+  EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
 
-    manager()->OnPasswordFormsRendered(&driver_, {}, true);
+  manager()->OnPasswordFormsRendered(&driver_, {}, true);
 }
 
 // Check that on non-password form, saving and filling fallbacks are available
@@ -3541,4 +3542,30 @@
                                      true /* did stop loading */);
 }
 
+TEST_F(PasswordManagerTest, NoPromptAutofillAssistantManuallyCuratedScript) {
+  manager()->set_autofill_assistance_mode(
+      AutofillAssistantMode::kManuallyCuratedScript);
+
+  PasswordForm form(MakeSimpleForm());
+  EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.origin))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(*store_, GetLogins)
+      .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
+  manager()->OnPasswordFormsParsed(&driver_, {form.form_data});
+
+  std::unique_ptr<PasswordFormManagerForUI> form_manager_to_save;
+  EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, false, false))
+      .WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
+  manager()->ShowManualFallbackForSaving(&driver_, form.form_data);
+  ASSERT_TRUE(form_manager_to_save);
+  EXPECT_THAT(form_manager_to_save->GetPendingCredentials(), FormMatches(form));
+
+  // Check that a save prompt is not shown.
+  EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr).Times(0);
+
+  manager()->DidNavigateMainFrame(true /* form_may_be_submitted */);
+  manager()->OnPasswordFormsRendered(&driver_, {} /* observed */,
+                                     true /* did stop loading */);
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_save_manager_impl.cc b/components/password_manager/core/browser/password_save_manager_impl.cc
index 49b1e62f3..89ddc45 100644
--- a/components/password_manager/core/browser/password_save_manager_impl.cc
+++ b/components/password_manager/core/browser/password_save_manager_impl.cc
@@ -37,6 +37,27 @@
   return {form.new_password_value, form.new_password_element};
 }
 
+PasswordForm PendingCredentialsForNewCredentials(
+    const PasswordForm& parsed_submitted_form,
+    const FormData& observed_form,
+    const base::string16& password_element,
+    bool is_http_auth,
+    bool is_credential_api_save) {
+  if (is_http_auth || is_credential_api_save)
+    return parsed_submitted_form;
+
+  PasswordForm pending_credentials = parsed_submitted_form;
+  pending_credentials.form_data = observed_form;
+  // The password value will be filled in later, remove any garbage for now.
+  pending_credentials.password_value.clear();
+  // The password element should be determined earlier in |PasswordToSave|.
+  pending_credentials.password_element = password_element;
+  // The new password's value and element name should be empty.
+  pending_credentials.new_password_value.clear();
+  pending_credentials.new_password_element.clear();
+  return pending_credentials;
+}
+
 // Helper to get the platform specific identifier by which autofill and password
 // manager refer to a field. See http://crbug.com/896594
 base::string16 GetPlatformSpecificIdentifier(const FormFieldData& field) {
@@ -130,6 +151,85 @@
   votes_uploader_ = votes_uploader;
 }
 
+// static
+PendingCredentialsState PasswordSaveManagerImpl::FillPendingCredentials(
+    const PasswordForm& parsed_submitted_form,
+    const FormData& observed_form,
+    const FormData& submitted_form,
+    const base::Optional<base::string16>& generated_password,
+    bool is_http_auth,
+    bool is_credential_api_save,
+    const PasswordForm* similar_saved_form,
+    PasswordForm* pending_credentials) {
+  DCHECK(pending_credentials);
+
+  PendingCredentialsState pending_credentials_state =
+      PendingCredentialsState::NONE;
+
+  ValueElementPair password_to_save(PasswordToSave(parsed_submitted_form));
+  // Check if there are previously saved credentials (that were available to
+  // autofilling) matching the actually submitted credentials.
+  if (similar_saved_form) {
+    // A similar credential exists in the store already.
+    *pending_credentials = *similar_saved_form;
+    pending_credentials_state = PendingCredentialsState::EQUAL_TO_SAVED_MATCH;
+    if (pending_credentials->password_value != password_to_save.first) {
+      pending_credentials_state = PendingCredentialsState::UPDATE;
+    } else if (pending_credentials->is_public_suffix_match) {
+      // If the autofilled credentials were a PSL match, store a copy with the
+      // current origin and signon realm. This ensures that on the next visit, a
+      // precise match is found.
+      pending_credentials_state = PendingCredentialsState::AUTOMATIC_SAVE;
+      // Update credential to reflect that it has been used for submission.
+      // If this isn't updated, then password generation uploads are off for
+      // sites where PSL matching is required to fill the login form, as two
+      // PASSWORD votes are uploaded per saved password instead of one.
+      password_manager_util::UpdateMetadataForUsage(pending_credentials);
+
+      // Update |pending_credentials| in order to be able correctly save it.
+      pending_credentials->origin = parsed_submitted_form.origin;
+      pending_credentials->signon_realm = parsed_submitted_form.signon_realm;
+      pending_credentials->action = parsed_submitted_form.action;
+    }
+  } else {
+    pending_credentials_state = PendingCredentialsState::NEW_LOGIN;
+    // No stored credentials can be matched to the submitted form. Offer to
+    // save new credentials.
+    *pending_credentials = PendingCredentialsForNewCredentials(
+        parsed_submitted_form, observed_form, password_to_save.second,
+        is_http_auth, is_credential_api_save);
+  }
+  pending_credentials->password_value =
+      generated_password.value_or(password_to_save.first);
+  pending_credentials->date_last_used = base::Time::Now();
+  pending_credentials->form_has_autofilled_value =
+      parsed_submitted_form.form_has_autofilled_value;
+  pending_credentials->all_possible_passwords =
+      parsed_submitted_form.all_possible_passwords;
+  CopyFieldPropertiesMasks(submitted_form, &pending_credentials->form_data);
+
+  // If we're dealing with an API-driven provisionally saved form, then take
+  // the server provided values. We don't do this for non-API forms, as
+  // those will never have those members set.
+  if (parsed_submitted_form.type == PasswordForm::Type::kApi) {
+    pending_credentials->skip_zero_click =
+        parsed_submitted_form.skip_zero_click;
+    pending_credentials->display_name = parsed_submitted_form.display_name;
+    pending_credentials->federation_origin =
+        parsed_submitted_form.federation_origin;
+    pending_credentials->icon_url = parsed_submitted_form.icon_url;
+    // It's important to override |signon_realm| for federated credentials
+    // because it has format "federation://" + origin_host + "/" +
+    // federation_host
+    pending_credentials->signon_realm = parsed_submitted_form.signon_realm;
+  }
+
+  if (generated_password.has_value())
+    pending_credentials->type = PasswordForm::Type::kGenerated;
+
+  return pending_credentials_state;
+}
+
 void PasswordSaveManagerImpl::CreatePendingCredentials(
     const PasswordForm& parsed_submitted_form,
     const FormData& observed_form,
@@ -140,86 +240,46 @@
 
   // This function might be called multiple times so set variables that are
   // changed in this function to initial states.
-  pending_credentials_state_ = PendingCredentialsState::NONE;
+  pending_credentials_ = PasswordForm();
   votes_uploader_->set_password_overridden(false);
 
-  ValueElementPair password_to_save(PasswordToSave(parsed_submitted_form));
-  // Look for the actually submitted credentials in the list of previously saved
-  // credentials that were available to autofilling.
-  const PasswordForm* saved_form = password_manager_util::GetMatchForUpdating(
-      parsed_submitted_form, form_fetcher_->GetBestMatches());
-  if (saved_form) {
-    // A similar credential exists in the store already.
-    pending_credentials_ = *saved_form;
-    pending_credentials_state_ = PendingCredentialsState::EQUAL_TO_SAVED_MATCH;
-    if (pending_credentials_.password_value != password_to_save.first) {
-      pending_credentials_state_ = PendingCredentialsState::UPDATE;
-      votes_uploader_->set_password_overridden(true);
-    } else if (pending_credentials_.is_public_suffix_match) {
-      // If the autofilled credentials were a PSL match, store a copy with the
-      // current origin and signon realm. This ensures that on the next visit, a
-      // precise match is found.
-      pending_credentials_state_ = PendingCredentialsState::AUTOMATIC_SAVE;
-      // Update credential to reflect that it has been used for submission.
-      // If this isn't updated, then password generation uploads are off for
-      // sites where PSL matching is required to fill the login form, as two
-      // PASSWORD votes are uploaded per saved password instead of one.
-      password_manager_util::UpdateMetadataForUsage(&pending_credentials_);
-
-      // Update |pending_credentials_| in order to be able correctly save it.
-      pending_credentials_.origin = parsed_submitted_form.origin;
-      pending_credentials_.signon_realm = parsed_submitted_form.signon_realm;
-      pending_credentials_.action = parsed_submitted_form.action;
-    }
-  } else {
-    pending_credentials_state_ = PendingCredentialsState::NEW_LOGIN;
-    // No stored credentials can be matched to the submitted form. Offer to
-    // save new credentials.
-    CreatePendingCredentialsForNewCredentials(
-        parsed_submitted_form, observed_form, password_to_save.second,
-        is_http_auth, is_credential_api_save);
-    // Generate username correction votes.
-    bool username_correction_found =
-        votes_uploader_->FindCorrectedUsernameElement(
-            form_fetcher_->GetAllRelevantMatches(),
-            parsed_submitted_form.username_value,
-            parsed_submitted_form.password_value);
-    UMA_HISTOGRAM_BOOLEAN("PasswordManager.UsernameCorrectionFound",
-                          username_correction_found);
-    if (username_correction_found) {
-      metrics_recorder_->RecordDetailedUserAction(
-          password_manager::PasswordFormMetricsRecorder::DetailedUserAction::
-              kCorrectedUsernameInForm);
-    }
-  }
-  pending_credentials_.password_value =
-      HasGeneratedPassword() ? generation_manager_->generated_password()
-                             : password_to_save.first;
-  pending_credentials_.date_last_used = base::Time::Now();
-  pending_credentials_.form_has_autofilled_value =
-      parsed_submitted_form.form_has_autofilled_value;
-  pending_credentials_.all_possible_passwords =
-      parsed_submitted_form.all_possible_passwords;
-  CopyFieldPropertiesMasks(submitted_form, &pending_credentials_.form_data);
-
-  // If we're dealing with an API-driven provisionally saved form, then take
-  // the server provided values. We don't do this for non-API forms, as
-  // those will never have those members set.
-  if (parsed_submitted_form.type == PasswordForm::Type::kApi) {
-    pending_credentials_.skip_zero_click =
-        parsed_submitted_form.skip_zero_click;
-    pending_credentials_.display_name = parsed_submitted_form.display_name;
-    pending_credentials_.federation_origin =
-        parsed_submitted_form.federation_origin;
-    pending_credentials_.icon_url = parsed_submitted_form.icon_url;
-    // It's important to override |signon_realm| for federated credentials
-    // because it has format "federation://" + origin_host + "/" +
-    // federation_host
-    pending_credentials_.signon_realm = parsed_submitted_form.signon_realm;
-  }
-
+  base::Optional<base::string16> generated_password;
   if (HasGeneratedPassword())
-    pending_credentials_.type = PasswordForm::Type::kGenerated;
+    generated_password = generation_manager_->generated_password();
+
+  pending_credentials_state_ = FillPendingCredentials(
+      parsed_submitted_form, observed_form, submitted_form, generated_password,
+      is_http_auth, is_credential_api_save,
+      password_manager_util::GetMatchForUpdating(
+          parsed_submitted_form, form_fetcher_->GetBestMatches()),
+      &pending_credentials_);
+
+  switch (pending_credentials_state_) {
+    case PendingCredentialsState::NEW_LOGIN: {
+      // Generate username correction votes.
+      bool username_correction_found =
+          votes_uploader_->FindCorrectedUsernameElement(
+              form_fetcher_->GetAllRelevantMatches(),
+              parsed_submitted_form.username_value,
+              parsed_submitted_form.password_value);
+      UMA_HISTOGRAM_BOOLEAN("PasswordManager.UsernameCorrectionFound",
+                            username_correction_found);
+      if (username_correction_found) {
+        metrics_recorder_->RecordDetailedUserAction(
+            password_manager::PasswordFormMetricsRecorder::DetailedUserAction::
+                kCorrectedUsernameInForm);
+      }
+      break;
+    }
+    case PendingCredentialsState::UPDATE:
+      votes_uploader_->set_password_overridden(true);
+      break;
+    case PendingCredentialsState::NONE:
+    case PendingCredentialsState::AUTOMATIC_SAVE:
+    case PendingCredentialsState::EQUAL_TO_SAVED_MATCH:
+      // Nothing to be done in these cases.
+      break;
+  }
 }
 
 void PasswordSaveManagerImpl::ResetPendingCrednetials() {
@@ -361,28 +421,6 @@
   return result;
 }
 
-void PasswordSaveManagerImpl::CreatePendingCredentialsForNewCredentials(
-    const PasswordForm& parsed_submitted_form,
-    const FormData& observed_form,
-    const base::string16& password_element,
-    bool is_http_auth,
-    bool is_credential_api_save) {
-  if (is_http_auth || is_credential_api_save) {
-    pending_credentials_ = parsed_submitted_form;
-    return;
-  }
-
-  pending_credentials_ = parsed_submitted_form;
-  pending_credentials_.form_data = observed_form;
-  // The password value will be filled in later, remove any garbage for now.
-  pending_credentials_.password_value.clear();
-  // The password element should be determined earlier in |PasswordToSave|.
-  pending_credentials_.password_element = password_element;
-  // The new password's value and element name should be empty.
-  pending_credentials_.new_password_value.clear();
-  pending_credentials_.new_password_element.clear();
-}
-
 void PasswordSaveManagerImpl::SavePendingToStore(
     const PasswordForm& parsed_submitted_form,
     bool update) {
diff --git a/components/password_manager/core/browser/password_save_manager_impl.h b/components/password_manager/core/browser/password_save_manager_impl.h
index 2dc5bc9..25e36036 100644
--- a/components/password_manager/core/browser/password_save_manager_impl.h
+++ b/components/password_manager/core/browser/password_save_manager_impl.h
@@ -38,8 +38,8 @@
             scoped_refptr<PasswordFormMetricsRecorder> metrics_recorder,
             VotesUploader* votes_uploader) override;
 
-  // Create pending credentials from |parsed_submitted_form| and
-  // |parsed_observed_form| and |submitted_form|.
+  // Create pending credentials from |parsed_submitted_form|, |observed_form|
+  // and |submitted_form|.
   void CreatePendingCredentials(
       const autofill::PasswordForm& parsed_submitted_form,
       const autofill::FormData& observed_form,
@@ -84,6 +84,16 @@
 #endif
 
  protected:
+  static PendingCredentialsState FillPendingCredentials(
+      const autofill::PasswordForm& parsed_submitted_form,
+      const autofill::FormData& observed_form,
+      const autofill::FormData& submitted_form,
+      const base::Optional<base::string16>& generated_password,
+      bool is_http_auth,
+      bool is_credential_api_save,
+      const autofill::PasswordForm* similar_saved_form,
+      autofill::PasswordForm* pending_credentials);
+
   // Returns the form_saver to be used for generated passwords. Subclasses will
   // override this method to provide different logic for get the form saver.
   virtual FormSaver* GetFormSaverForGeneration();
@@ -119,15 +129,6 @@
   const FormFetcher* form_fetcher_;
 
  private:
-  // Create pending credentials from provisionally saved form when this form
-  // represents credentials that were not previously saved.
-  void CreatePendingCredentialsForNewCredentials(
-      const autofill::PasswordForm& parsed_submitted_form,
-      const autofill::FormData& observed_form,
-      const base::string16& password_element,
-      bool is_http_auth,
-      bool is_credential_api_save);
-
   // Save/update |pending_credentials_| to the password store.
   void SavePendingToStore(const autofill::PasswordForm& parsed_submitted_form,
                           bool update);
diff --git a/components/password_manager/core/browser/password_store_change.cc b/components/password_manager/core/browser/password_store_change.cc
index c5c82e08..dbdecf015 100644
--- a/components/password_manager/core/browser/password_store_change.cc
+++ b/components/password_manager/core/browser/password_store_change.cc
@@ -9,6 +9,8 @@
 std::ostream& operator<<(std::ostream& os,
                          const PasswordStoreChange& password_store_change) {
   return os << "type: " << password_store_change.type()
+            << ", primary key: " << password_store_change.primary_key()
+            << ", password change: " << password_store_change.password_changed()
             << ", password form: " << password_store_change.form();
 }
 
diff --git a/components/policy/core/common/mock_configuration_policy_provider.cc b/components/policy/core/common/mock_configuration_policy_provider.cc
index 2c0eecf..90a3596c 100644
--- a/components/policy/core/common/mock_configuration_policy_provider.cc
+++ b/components/policy/core/common/mock_configuration_policy_provider.cc
@@ -28,7 +28,7 @@
       .CopyFrom(policy);
   UpdatePolicy(std::move(bundle));
   bool spin_run_loop = base::MessageLoopCurrent::IsSet();
-#if defined(IS_IOS)
+#if defined(OS_IOS)
   // On iOS, the UI message loop does not support RunUntilIdle().
   spin_run_loop &= !base::MessageLoopCurrentForUI::IsSet();
 #endif  // defined(OS_IOS)
diff --git a/components/policy/core/common/policy_pref_names.cc b/components/policy/core/common/policy_pref_names.cc
index 2086d56..68b94ed 100644
--- a/components/policy/core/common/policy_pref_names.cc
+++ b/components/policy/core/common/policy_pref_names.cc
@@ -15,6 +15,10 @@
 // See the SafeSitesFilterBehavior policy for details.
 const char kSafeSitesFilterBehavior[] = "policy.safe_sites_filter_behavior";
 
+// A list of system features to be disabled (see policy
+// "SystemFeaturesDisableList").
+const char kSystemFeaturesDisableList[] = "policy.system_features_disable_list";
+
 // Blocks access to the listed host patterns.
 const char kUrlBlacklist[] = "policy.url_blacklist";
 
diff --git a/components/policy/core/common/policy_pref_names.h b/components/policy/core/common/policy_pref_names.h
index be4d543a..f5bc2481f 100644
--- a/components/policy/core/common/policy_pref_names.h
+++ b/components/policy/core/common/policy_pref_names.h
@@ -10,14 +10,15 @@
 namespace policy {
 namespace policy_prefs {
 
+POLICY_EXPORT extern const char kCloudManagementEnrollmentMandatory[];
+POLICY_EXPORT extern const char kCloudPolicyOverridesPlatformPolicy[];
 POLICY_EXPORT extern const char kLastPolicyStatisticsUpdate[];
+POLICY_EXPORT extern const char kNativeWindowOcclusionEnabled[];
 POLICY_EXPORT extern const char kSafeSitesFilterBehavior[];
+POLICY_EXPORT extern const char kSystemFeaturesDisableList[];
 POLICY_EXPORT extern const char kUrlBlacklist[];
 POLICY_EXPORT extern const char kUrlWhitelist[];
 POLICY_EXPORT extern const char kUserPolicyRefreshRate[];
-POLICY_EXPORT extern const char kCloudManagementEnrollmentMandatory[];
-POLICY_EXPORT extern const char kCloudPolicyOverridesPlatformPolicy[];
-POLICY_EXPORT extern const char kNativeWindowOcclusionEnabled[];
 
 }  // namespace policy_prefs
 }  // namespace policy
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 045367b4..838877b 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -9433,6 +9433,49 @@
       If the policy is left not set, the shelf will be be positioned at the bottom of the screen by default and the user can change the shelf's position.''',
     },
     {
+      'name': 'SystemFeaturesDisableList',
+      'owners': ['file://components/policy/resources/OWNERS', 'ayaelattar@chromium.org'],
+      'type': 'string-enum-list',
+      'schema': {
+        'type': 'array',
+        'items' : {
+          'type': 'string',
+          'enum': [
+            'settings',
+            'camera',
+          ],
+        },
+      },
+      'items': [
+        {
+          'name': 'settings',
+          'value': 'settings',
+          'caption': '''Settings feature''',
+        },
+        {
+          'name': 'camera',
+          'value': 'camera',
+          'caption': '''Camera''',
+        },
+      ],
+      'supported_on': ['chrome_os:83-'],
+      'future': True,
+      'features': {
+        'can_be_recommended': False,
+        'dynamic_refresh': True,
+        'per_profile': False,
+      },
+      'example_value': ['camera', 'settings'],
+      'id': 689,
+      'caption': '''Configure the camera and the settings feature to be disabled''',
+      'tags': [],
+      'desc': '''Allows you to set a list of <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> features to be disabled.
+
+      Disabling any of these features means that the user can't access it from the UI and will see it as "disabled by admin".
+
+      If the policy is left not set, all <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> features will be enabled by default and the user can use any of them.''',
+    },
+    {
       'name': 'UserDisplayName',
       'owners': ['file://components/policy/resources/OWNERS'],
       'type': 'string',
@@ -11694,7 +11737,7 @@
       },
       'example_value': 1,
       'id': 674,
-      'caption': '''Determine the availability of variations on ChromeOS''',
+      'caption': '''Determine the availability of variations on <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph>''',
       'tags': [],
       'desc': '''Configuring this policy allows to specify which variations are allowed to be applied on an enterprise-managed <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> device.
 
@@ -21153,6 +21196,6 @@
   ],
   'placeholders': [],
   'deleted_policy_ids': [412, 546, 562, 569, 578],
-  'highest_id_currently_used': 688,
+  'highest_id_currently_used': 689,
   'highest_atomic_group_id_currently_used': 38
 }
diff --git a/components/safe_search_api/safe_search/safe_search_url_checker_client.cc b/components/safe_search_api/safe_search/safe_search_url_checker_client.cc
index 8b3d5be..ce6cb49 100644
--- a/components/safe_search_api/safe_search/safe_search_url_checker_client.cc
+++ b/components/safe_search_api/safe_search/safe_search_url_checker_client.cc
@@ -32,15 +32,12 @@
 const char kSafeSearchApiUrl[] =
     "https://safesearch.googleapis.com/v1:classify";
 const char kDataContentType[] = "application/x-www-form-urlencoded";
-const char kDataFormat[] = "key=%s&urls=%s&region_code=%s";
+const char kDataFormat[] = "key=%s&urls=%s";
 
 // Builds the POST data for SafeSearch API requests.
-std::string BuildRequestData(const std::string& api_key,
-                             const GURL& url,
-                             const std::string& region_code) {
+std::string BuildRequestData(const std::string& api_key, const GURL& url) {
   std::string query = net::EscapeQueryParamValue(url.spec(), true);
-  return base::StringPrintf(kDataFormat, api_key.c_str(), query.c_str(),
-                            region_code.c_str());
+  return base::StringPrintf(kDataFormat, api_key.c_str(), query.c_str());
 }
 
 // Parses a SafeSearch API |response| and stores the result in |is_porn|,
@@ -98,11 +95,9 @@
 SafeSearchURLCheckerClient::SafeSearchURLCheckerClient(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const net::NetworkTrafficAnnotationTag& traffic_annotation,
-    const std::string& country,
     const std::string& api_key)
     : url_loader_factory_(std::move(url_loader_factory)),
       traffic_annotation_(traffic_annotation),
-      country_(country),
       api_key_(api_key) {}
 
 SafeSearchURLCheckerClient::~SafeSearchURLCheckerClient() = default;
@@ -117,8 +112,8 @@
   std::unique_ptr<network::SimpleURLLoader> simple_url_loader =
       network::SimpleURLLoader::Create(std::move(resource_request),
                                        traffic_annotation_);
-  simple_url_loader->AttachStringForUpload(
-      BuildRequestData(api_key_, url, country_), kDataContentType);
+  simple_url_loader->AttachStringForUpload(BuildRequestData(api_key_, url),
+                                           kDataContentType);
   checks_in_progress_.push_front(std::make_unique<Check>(
       url, std::move(simple_url_loader), std::move(callback)));
   auto it = checks_in_progress_.begin();
diff --git a/components/safe_search_api/safe_search/safe_search_url_checker_client.h b/components/safe_search_api/safe_search/safe_search_url_checker_client.h
index 49af799..d22a517 100644
--- a/components/safe_search_api/safe_search/safe_search_url_checker_client.h
+++ b/components/safe_search_api/safe_search/safe_search_url_checker_client.h
@@ -26,12 +26,9 @@
 // via a callback.
 class SafeSearchURLCheckerClient : public URLCheckerClient {
  public:
-  // |country| should be a two-letter country code (ISO 3166-1 alpha-2), e.g.,
-  // "us". Optional
   SafeSearchURLCheckerClient(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const net::NetworkTrafficAnnotationTag& traffic_annotation,
-      const std::string& country = std::string(),
       const std::string& api_key = google_apis::GetAPIKey());
 
   ~SafeSearchURLCheckerClient() override;
@@ -52,7 +49,6 @@
 
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   const net::NetworkTrafficAnnotationTag traffic_annotation_;
-  const std::string country_;
   const std::string api_key_;
 
   CheckList checks_in_progress_;
diff --git a/components/sessions/core/session_service_commands.cc b/components/sessions/core/session_service_commands.cc
index 136db74..f7c5bf3 100644
--- a/components/sessions/core/session_service_commands.cc
+++ b/components/sessions/core/session_service_commands.cc
@@ -666,15 +666,7 @@
           if (!iter.ReadUInt32(&color_int))
             return true;
 
-          // Check for the existence of the enum value in the color set, which
-          // is the source of truth for allowed colors in tab groups. If the
-          // enum value doesn't exist, fall back to kGrey per UX preference.
-          tab_groups::TabGroupColorId color_id =
-              static_cast<tab_groups::TabGroupColorId>(color_int);
-          group->visual_data = tab_groups::TabGroupVisualData(
-              title, base::Contains(tab_groups::GetTabGroupColorSet(), color_id)
-                         ? color_id
-                         : tab_groups::TabGroupColorId::kGrey);
+          group->visual_data = tab_groups::TabGroupVisualData(title, color_int);
         }
         break;
       }
diff --git a/components/sessions/core/tab_restore_service_impl.cc b/components/sessions/core/tab_restore_service_impl.cc
index e6f675f..6b81da7 100644
--- a/components/sessions/core/tab_restore_service_impl.cc
+++ b/components/sessions/core/tab_restore_service_impl.cc
@@ -983,15 +983,8 @@
         current_tab->group =
             tab_groups::TabGroupId::FromRawToken(group_token.value());
 
-        // Check for the existence of the enum value in the color set, which is
-        // the source of truth for allowed colors in tab groups. If the enum
-        // value doesn't exist, fall back to kGrey per UX preference.
-        tab_groups::TabGroupColorId color_id =
-            static_cast<tab_groups::TabGroupColorId>(color_int);
-        current_tab->group_visual_data = tab_groups::TabGroupVisualData(
-            title, base::Contains(tab_groups::GetTabGroupColorSet(), color_id)
-                       ? color_id
-                       : tab_groups::TabGroupColorId::kGrey);
+        current_tab->group_visual_data =
+            tab_groups::TabGroupVisualData(title, color_int);
         break;
       }
 
diff --git a/components/tab_groups/tab_group_color.cc b/components/tab_groups/tab_group_color.cc
index ff2b37ad..c27cfa8 100644
--- a/components/tab_groups/tab_group_color.cc
+++ b/components/tab_groups/tab_group_color.cc
@@ -10,42 +10,27 @@
 #include "components/strings/grit/components_strings.h"
 #include "third_party/skia/include/utils/SkRandom.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/gfx/color_palette.h"
 
 namespace tab_groups {
 
-const base::flat_map<TabGroupColorId, TabGroupColor>& GetTabGroupColorSet() {
-  static const base::NoDestructor<
-      base::flat_map<TabGroupColorId, TabGroupColor>>
-      kTabGroupColors(
-          {{TabGroupColorId::kGrey,
-            TabGroupColor{gfx::kGoogleGrey700, gfx::kGoogleGrey400,
-                          l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_GREY)}},
-           {TabGroupColorId::kBlue,
-            TabGroupColor{gfx::kGoogleBlue600, gfx::kGoogleBlue300,
-                          l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_BLUE)}},
-           {TabGroupColorId::kRed,
-            TabGroupColor{gfx::kGoogleRed600, gfx::kGoogleRed300,
-                          l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_RED)}},
-           {TabGroupColorId::kYellow,
-            TabGroupColor{
-                gfx::kGoogleYellow900, gfx::kGoogleYellow300,
-                l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_YELLOW)}},
-           {TabGroupColorId::kGreen,
-            TabGroupColor{
-                gfx::kGoogleGreen600, gfx::kGoogleGreen300,
-                l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_GREEN)}},
-           {TabGroupColorId::kPink,
-            TabGroupColor{gfx::kGooglePink700, gfx::kGooglePink300,
-                          l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_PINK)}},
-           {TabGroupColorId::kPurple,
-            TabGroupColor{
-                gfx::kGooglePurple600, gfx::kGooglePurple200,
-                l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_PURPLE)}},
-           {TabGroupColorId::kCyan,
-            TabGroupColor{
-                gfx::kGoogleCyan900, gfx::kGoogleCyan300,
-                l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_CYAN)}}});
+const ColorLabelMap& GetTabGroupColorLabelMap() {
+  static const base::NoDestructor<ColorLabelMap> kTabGroupColors(
+      {{TabGroupColorId::kGrey,
+        l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_GREY)},
+       {TabGroupColorId::kBlue,
+        l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_BLUE)},
+       {TabGroupColorId::kRed,
+        l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_RED)},
+       {TabGroupColorId::kYellow,
+        l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_YELLOW)},
+       {TabGroupColorId::kGreen,
+        l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_GREEN)},
+       {TabGroupColorId::kPink,
+        l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_PINK)},
+       {TabGroupColorId::kPurple,
+        l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_PURPLE)},
+       {TabGroupColorId::kCyan,
+        l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_CYAN)}});
   return *kTabGroupColors;
 }
 
diff --git a/components/tab_groups/tab_group_color.h b/components/tab_groups/tab_group_color.h
index 5adf93aa..0123b6a 100644
--- a/components/tab_groups/tab_group_color.h
+++ b/components/tab_groups/tab_group_color.h
@@ -39,17 +39,13 @@
   // Next value: 8
 };
 
-struct COMPONENT_EXPORT(TAB_GROUPS) TabGroupColor {
-  SkColor light_theme_color;
-  SkColor dark_theme_color;
-  base::string16 label;
-};
+using ColorLabelMap = base::flat_map<TabGroupColorId, base::string16>;
 
-// Returns the source of truth for what colors tab groups can currently have.
+// Returns a map of TabGroupColorIds to their string labels.
 // When reading color IDs from disk, always verify against the keys in this
-// map for valid values, and fall back to kGrey if it doesn't exist.
+// map for valid values.
 COMPONENT_EXPORT(TAB_GROUPS)
-const base::flat_map<TabGroupColorId, TabGroupColor>& GetTabGroupColorSet();
+const ColorLabelMap& GetTabGroupColorLabelMap();
 
 }  // namespace tab_groups
 
diff --git a/components/tab_groups/tab_group_visual_data.cc b/components/tab_groups/tab_group_visual_data.cc
index 7444ac7..92e952e3 100644
--- a/components/tab_groups/tab_group_visual_data.cc
+++ b/components/tab_groups/tab_group_visual_data.cc
@@ -4,19 +4,25 @@
 
 #include "components/tab_groups/tab_group_visual_data.h"
 
+#include "base/stl_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/tab_groups/tab_group_color.h"
 
 namespace tab_groups {
 
-TabGroupVisualData::TabGroupVisualData() {
-  title_ = base::ASCIIToUTF16("");
-  color_ = tab_groups::TabGroupColorId::kGrey;
-}
+TabGroupVisualData::TabGroupVisualData()
+    : TabGroupVisualData(base::string16(), TabGroupColorId::kGrey) {}
 
 TabGroupVisualData::TabGroupVisualData(base::string16 title,
                                        tab_groups::TabGroupColorId color)
-    : title_(title), color_(color) {}
+    : title_(std::move(title)), color_(color) {}
+
+TabGroupVisualData::TabGroupVisualData(base::string16 title, uint32_t color_int)
+    : title_(std::move(title)), color_(TabGroupColorId::kGrey) {
+  auto color_id = static_cast<tab_groups::TabGroupColorId>(color_int);
+  if (base::Contains(tab_groups::GetTabGroupColorLabelMap(), color_id))
+    color_ = color_id;
+}
 
 }  // namespace tab_groups
diff --git a/components/tab_groups/tab_group_visual_data.h b/components/tab_groups/tab_group_visual_data.h
index 6b814a9e..7509bc5c 100644
--- a/components/tab_groups/tab_group_visual_data.h
+++ b/components/tab_groups/tab_group_visual_data.h
@@ -19,8 +19,8 @@
  public:
   // Construct a TabGroupVisualData with placeholder name and random color.
   TabGroupVisualData();
-
   TabGroupVisualData(base::string16 title, tab_groups::TabGroupColorId color);
+  TabGroupVisualData(base::string16 title, uint32_t color_int);
 
   TabGroupVisualData(const TabGroupVisualData& other) = default;
   TabGroupVisualData(TabGroupVisualData&& other) = default;
diff --git a/content/browser/cross_origin_opener_policy_browsertest.cc b/content/browser/cross_origin_opener_policy_browsertest.cc
index 5291cce..5d6139ce 100644
--- a/content/browser/cross_origin_opener_policy_browsertest.cc
+++ b/content/browser/cross_origin_opener_policy_browsertest.cc
@@ -6,6 +6,7 @@
 #include "components/network_session_configurator/common/network_switches.h"
 #include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
@@ -17,6 +18,8 @@
 
 namespace content {
 
+namespace {
+
 class CrossOriginOpenerPolicyBrowserTest : public ContentBrowserTest {
  public:
   CrossOriginOpenerPolicyBrowserTest()
@@ -39,10 +42,14 @@
 
     https_server()->ServeFilesFromSourceDirectory(GetTestDataFilePath());
     SetupCrossSiteRedirector(https_server());
-    https_server()->SetSSLConfig(net::EmbeddedTestServer::CERT_OK);
     ASSERT_TRUE(https_server()->Start());
   }
 
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    ContentBrowserTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
+  }
+
   WebContentsImpl* web_contents() const {
     return static_cast<WebContentsImpl*>(shell()->web_contents());
   }
@@ -723,4 +730,68 @@
       1u);
 }
 
+IN_PROC_BROWSER_TEST_F(CrossOriginOpenerPolicyBrowserTest,
+                       IsolateInNewProcessDespiteLimitReached) {
+  // Set a process limit of 1 for testing.
+  RenderProcessHostImpl::SetMaxRendererProcessCount(1);
+
+  // Navigate to a starting page.
+  GURL starting_page(https_server()->GetURL("a.com", "/title1.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), starting_page));
+
+  // Open a popup with CrossOriginOpenerPolicy and CrossOriginEmbedderPolicy
+  // set.
+  ShellAddedObserver shell_observer;
+  EXPECT_TRUE(ExecJs(current_frame_host(),
+                     "window.open('/page_with_coop_and_coep.html')"));
+
+  auto* popup_webcontents =
+      static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents());
+  WaitForLoadStop(popup_webcontents);
+
+  // The page and its popup should be in different processes even though the
+  // process limit was reached.
+  // TODO(clamy, pmeuleman, ahemery): The assert below should be false once we
+  // fix the process reuse for COOP.
+  EXPECT_EQ(current_frame_host()->GetProcess(),
+            popup_webcontents->GetMainFrame()->GetProcess());
+}
+
+IN_PROC_BROWSER_TEST_F(CrossOriginOpenerPolicyBrowserTest,
+                       NoProcessReuseForCOOPProcesses) {
+  // Set a process limit of 1 for testing.
+  RenderProcessHostImpl::SetMaxRendererProcessCount(1);
+
+  // Navigate to a starting page with CrossOriginOpenerPolicy and
+  // CrossOriginEmbedderPolicy set.
+  GURL starting_page(
+      https_server()->GetURL("a.com", "/page_with_coop_and_coep.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), starting_page));
+
+  // Open a popup without CrossOriginOpenerPolicy and CrossOriginEmbedderPolicy
+  // set.
+  ShellAddedObserver shell_observer;
+  EXPECT_TRUE(ExecJs(current_frame_host(), "window.open('/title1.html')"));
+
+  auto* popup_webcontents =
+      static_cast<WebContentsImpl*>(shell_observer.GetShell()->web_contents());
+  WaitForLoadStop(popup_webcontents);
+
+  // The page and its popup should be in different processes even though the
+  // process limit was reached.
+  // TODO(clamy, pmeuleman, ahemery): The assert below should be false once we
+  // fix the process reuse for COOP.
+  EXPECT_EQ(current_frame_host()->GetProcess(),
+            popup_webcontents->GetMainFrame()->GetProcess());
+
+  // Navigate to a new page without COOP and COEP. Because of process reuse, it
+  // is placed in the popup process.
+  GURL final_page(https_server()->GetURL("a.com", "/title1.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), final_page));
+  EXPECT_EQ(current_frame_host()->GetProcess(),
+            popup_webcontents->GetMainFrame()->GetProcess());
+}
+
+}  // namespace
+
 }  // namespace content
diff --git a/content/browser/frame_host/render_frame_host_impl_browsertest.cc b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
index 6a0ce955..3d47f53 100644
--- a/content/browser/frame_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
@@ -4040,6 +4040,9 @@
 
 class MockObject : public blink::mojom::RemoteObject {
  public:
+  explicit MockObject(
+      mojo::PendingReceiver<blink::mojom::RemoteObject> receiver)
+      : receiver_(this, std::move(receiver)) {}
   void HasMethod(const std::string& name, HasMethodCallback callback) override {
     bool has_method =
         std::find(kMainObject.methods.begin(), kMainObject.methods.end(),
@@ -4061,6 +4064,9 @@
       result->value = blink::mojom::RemoteInvocationResultValue::NewNumberValue(
           kMainObject.id);
     } else if (name == "readArray") {
+      EXPECT_EQ(1U, arguments.size());
+      EXPECT_TRUE(arguments[0]->is_array_value());
+      num_elements_received_ = arguments[0]->get_array_value().size();
       result->value =
           blink::mojom::RemoteInvocationResultValue::NewBooleanValue(true);
     } else if (name == "getInnerObject") {
@@ -4069,6 +4075,12 @@
     }
     std::move(callback).Run(std::move(result));
   }
+
+  int get_num_elements_received() const { return num_elements_received_; }
+
+ private:
+  int num_elements_received_ = 0;
+  mojo::Receiver<blink::mojom::RemoteObject> receiver_;
 };
 
 class MockObjectHost : public blink::mojom::RemoteObjectHost {
@@ -4077,8 +4089,7 @@
       int32_t object_id,
       mojo::PendingReceiver<blink::mojom::RemoteObject> receiver) override {
     if (object_id == kMainObject.id) {
-      mojo::MakeSelfOwnedReceiver(std::make_unique<MockObject>(),
-                                  std::move(receiver));
+      mock_object_ = std::make_unique<MockObject>(std::move(receiver));
     } else if (object_id == kInnerObject.id) {
       mojo::MakeSelfOwnedReceiver(std::make_unique<MockInnerObject>(),
                                   std::move(receiver));
@@ -4093,8 +4104,11 @@
     return receiver_.BindNewPipeAndPassRemote();
   }
 
+  MockObject* GetMockObject() const { return mock_object_.get(); }
+
  private:
   mojo::Receiver<blink::mojom::RemoteObjectHost> receiver_{this};
+  std::unique_ptr<MockObject> mock_object_;
 };
 
 class RemoteObjectInjector : public WebContentsObserver {
@@ -4102,6 +4116,8 @@
   explicit RemoteObjectInjector(WebContents* web_contents)
       : WebContentsObserver(web_contents) {}
 
+  const MockObjectHost& GetObjectHost() const { return host_; }
+
  private:
   void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
     mojo::Remote<blink::mojom::RemoteObjectGateway> gateway;
@@ -4185,6 +4201,8 @@
 
   std::string kScript = "testObject.readArray([6, 8, 2]);";
   EXPECT_TRUE(EvalJs(web_contents, kScript).error.empty());
+  EXPECT_EQ(
+      3, injector.GetObjectHost().GetMockObject()->get_num_elements_received());
 }
 
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index f912529..7d6ed1e 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -850,18 +850,19 @@
  private:
   // network::mojom::URLLoaderClient implementation:
   void OnReceiveResponse(network::mojom::URLResponseHeadPtr head) override {
-    // Wait for OnStartLoadingResponseBody() before sending anything to the
-    // renderer process.
-    if (!response_body_.is_valid()) {
-      head_ = std::move(head);
-      return;
-    }
+    head_ = std::move(head);
+  }
+
+  // network::mojom::URLLoaderClient implementation:
+  void OnStartLoadingResponseBody(
+      mojo::ScopedDataPipeConsumerHandle response_body) override {
+    response_body_ = std::move(response_body);
     received_response_ = true;
 
     // If the default loader (network) was used to handle the URL load request
     // we need to see if the interceptors want to potentially create a new
     // loader for the response. e.g. AppCache.
-    if (MaybeCreateLoaderForResponse(&head))
+    if (MaybeCreateLoaderForResponse(&head_))
       return;
 
     network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints;
@@ -879,8 +880,8 @@
     // This needs to be after the URLLoader has been moved to
     // |url_loader_client_endpoints| in order to abort the request, to avoid
     // receiving unexpected call.
-    if (head->headers &&
-        head->headers->response_code() == net::HTTP_NOT_MODIFIED) {
+    if (head_->headers &&
+        head_->headers->response_code() == net::HTTP_NOT_MODIFIED) {
       // Call CancelWithError instead of OnComplete so that if there is an
       // intercepting URLLoaderFactory it gets notified.
       url_loader_->CancelWithError(
@@ -889,18 +890,16 @@
       return;
     }
 
-    bool is_download;
-
-    bool must_download = download_utils::MustDownload(url_, head->headers.get(),
-                                                      head->mime_type);
-    bool known_mime_type = blink::IsSupportedMimeType(head->mime_type);
+    bool must_download = download_utils::MustDownload(
+        url_, head_->headers.get(), head_->mime_type);
+    bool known_mime_type = blink::IsSupportedMimeType(head_->mime_type);
 
 #if BUILDFLAG(ENABLE_PLUGINS)
-    if (!head->intercepted_by_plugin && !must_download && !known_mime_type) {
+    if (!head_->intercepted_by_plugin && !must_download && !known_mime_type) {
       // No plugin throttles intercepted the response. Ask if the plugin
       // registered to PluginService wants to handle the request.
       CheckPluginAndContinueOnReceiveResponse(
-          std::move(head), std::move(url_loader_client_endpoints),
+          std::move(head_), std::move(url_loader_client_endpoints),
           true /* is_download_if_not_handled_by_plugin */,
           std::vector<WebPluginInfo>());
       return;
@@ -908,10 +907,10 @@
 #endif
 
     // When a plugin intercepted the response, we don't want to download it.
-    is_download =
-        !head->intercepted_by_plugin && (must_download || !known_mime_type);
+    bool is_download =
+        !head_->intercepted_by_plugin && (must_download || !known_mime_type);
 
-    CallOnReceivedResponse(std::move(head),
+    CallOnReceivedResponse(std::move(head_),
                            std::move(url_loader_client_endpoints), is_download);
   }
 
@@ -1009,12 +1008,6 @@
 
   void OnTransferSizeUpdated(int32_t transfer_size_diff) override {}
 
-  void OnStartLoadingResponseBody(
-      mojo::ScopedDataPipeConsumerHandle response_body) override {
-    response_body_ = std::move(response_body);
-    OnReceiveResponse(std::move(head_));
-  }
-
   void OnComplete(const network::URLLoaderCompletionStatus& status) override {
     UMA_HISTOGRAM_BOOLEAN(
         "Navigation.URLLoaderNetworkService.OnCompleteHasSSLInfo",
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 2ddb57d..5be521e 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -15551,6 +15551,131 @@
   }
 }
 
+// Tests that main_frame_scroll_offset is not shared by frames in the same
+// process. This is a regression test for https://crbug.com/1063760.
+//
+// Set up the frame tree to be A(B1(C1(D1)),B2(C2(D2))) where B1 is above B2.
+// Scroll page A up to the point that B1(C1(D1)) intersects with A, while
+// B2(C2(D2)) is still within A. After the scrolling, C1 will see the up-to-date
+// value of the main_frame_scroll_offset, while in C2 it's still 0 because there
+// was no animation frame scheduled. Finally, we hide D2 to force an update in
+// C2. It's expected that the main_frame_scroll_offset sent from C2 to D2 is 0
+// rather than the current scroll offset.
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, MainFrameScrollOffset) {
+  GURL a_url = embedded_test_server()->GetURL(
+      "a.com", "/frame_tree/scrollable_page_with_two_frames.html");
+  GURL b_url = embedded_test_server()->GetURL(
+      "b.com", "/frame_tree/page_with_large_iframe.html");
+  GURL c_url = embedded_test_server()->GetURL(
+      "c.com", "/frame_tree/page_with_large_iframe.html");
+  GURL d_url = embedded_test_server()->GetURL("d.com", "/title1.html");
+
+  EXPECT_TRUE(NavigateToURL(shell(), a_url));
+  FrameTreeNode* a_node = web_contents()->GetFrameTree()->root();
+
+  FrameTreeNode* b1_node = a_node->child_at(0);
+  NavigateFrameToURL(b1_node, b_url);
+
+  FrameTreeNode* c1_node = b1_node->child_at(0);
+  NavigateFrameToURL(c1_node, c_url);
+
+  FrameTreeNode* d1_node = c1_node->child_at(0);
+  NavigateFrameToURL(d1_node, d_url);
+
+  FrameTreeNode* b2_node = a_node->child_at(1);
+  NavigateFrameToURL(b2_node, b_url);
+
+  FrameTreeNode* c2_node = b2_node->child_at(0);
+  NavigateFrameToURL(c2_node, c_url);
+
+  FrameTreeNode* d2_node = c2_node->child_at(0);
+  NavigateFrameToURL(d2_node, d_url);
+
+  float scale_factor = 1.0f;
+  if (IsUseZoomForDSFEnabled())
+    scale_factor = GetFrameDeviceScaleFactor(shell()->web_contents());
+
+  // This will intercept messages sent from C1 to D1, describing D1's viewport
+  // intersection.
+  scoped_refptr<UpdateViewportIntersectionMessageFilter>
+      c1_to_d1_message_filter = new UpdateViewportIntersectionMessageFilter();
+  c1_node->current_frame_host()->GetProcess()->AddFilter(
+      c1_to_d1_message_filter.get());
+
+  // This will intercept messages sent from C2 to D2, describing D2's viewport
+  // intersection.
+  scoped_refptr<UpdateViewportIntersectionMessageFilter>
+      c2_to_d2_message_filter = new UpdateViewportIntersectionMessageFilter();
+  c2_node->current_frame_host()->GetProcess()->AddFilter(
+      c2_to_d2_message_filter.get());
+
+  // Run requestAnimationFrame in A, B1, C1, B2, C2 to make sure initial layout
+  // has completed and initial IPCs are sent.
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(a_node->current_frame_host(), "", "")
+                  .error.empty());
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(b1_node->current_frame_host(), "", "")
+                  .error.empty());
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(c1_node->current_frame_host(), "", "")
+                  .error.empty());
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(b2_node->current_frame_host(), "", "")
+                  .error.empty());
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(c2_node->current_frame_host(), "", "")
+                  .error.empty());
+
+  c1_to_d1_message_filter->Clear();
+  c2_to_d2_message_filter->Clear();
+
+  // Scroll page A up to the point that B1(C1(D1)) intersects with A, while
+  // B2(C2(D2)) is still within A.
+  int scroll_offset_y = d1_node->render_manager()
+                            ->GetProxyToParent()
+                            ->cross_process_frame_connector()
+                            ->intersection_state()
+                            .viewport_offset.y() +
+                        1;
+  ASSERT_TRUE(
+      ExecJs(a_node->current_frame_host(),
+             content::JsReplace("window.scrollTo(0, $1)", scroll_offset_y)));
+  c1_to_d1_message_filter->Wait();
+
+  // After the scrolling, C1 will see the up-to-date value of the
+  // main_frame_scroll_offset, while in C2 it's still 0 because there was no
+  // animation frame scheduled.
+  EXPECT_FALSE(c2_to_d2_message_filter->MessageReceived());
+
+  EXPECT_EQ(c1_to_d1_message_filter->GetIntersectionState()
+                .main_frame_scroll_offset.y(),
+            static_cast<int>(scroll_offset_y * scale_factor));
+
+  // Run requestAnimationFrame in A, B1, C1, B2, C2 to make sure IPCs are sent.
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(a_node->current_frame_host(), "", "")
+                  .error.empty());
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(b1_node->current_frame_host(), "", "")
+                  .error.empty());
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(c1_node->current_frame_host(), "", "")
+                  .error.empty());
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(b2_node->current_frame_host(), "", "")
+                  .error.empty());
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(c2_node->current_frame_host(), "", "")
+                  .error.empty());
+
+  c1_to_d1_message_filter->Clear();
+  c2_to_d2_message_filter->Clear();
+
+  // Hide D2 to force an update in C2. It's expected that the
+  // main_frame_scroll_offset sent from C2 to D2 is 0 rather than the current
+  // scroll offset.
+  ASSERT_TRUE(ExecJs(
+      c2_node->current_frame_host(),
+      "let f = document.getElementsByTagName('iframe')[0];f.style.display = "
+      "'none';"));
+
+  c2_to_d2_message_filter->Wait();
+  EXPECT_EQ(c2_to_d2_message_filter->GetIntersectionState()
+                .main_frame_scroll_offset.y(),
+            0);
+}
+
 class SitePerProcessCompositorViewportBrowserTest
     : public SitePerProcessBrowserTest,
       public testing::WithParamInterface<double> {
diff --git a/content/public/test/android/BUILD.gn b/content/public/test/android/BUILD.gn
index 56f3c1fb..d3ecdb5 100644
--- a/content/public/test/android/BUILD.gn
+++ b/content/public/test/android/BUILD.gn
@@ -20,6 +20,7 @@
     "//base:base_java_test_support",
     "//content/public/android:content_java",
     "//third_party/android_support_test_runner:runner_java",
+    "//third_party/hamcrest:hamcrest_java",
     "//third_party/junit:junit",
     "//ui/android:ui_java",
     "//ui/android:ui_java_test_support",
@@ -41,10 +42,10 @@
     "javatests/src/org/chromium/content_public/browser/test/util/CriteriaHelper.java",
     "javatests/src/org/chromium/content_public/browser/test/util/DOMUtils.java",
     "javatests/src/org/chromium/content_public/browser/test/util/DomAutomationController.java",
-    "javatests/src/org/chromium/content_public/browser/test/util/EqualityCriteria.java",
     "javatests/src/org/chromium/content_public/browser/test/util/HistoryUtils.java",
     "javatests/src/org/chromium/content_public/browser/test/util/JavaScriptUtils.java",
     "javatests/src/org/chromium/content_public/browser/test/util/KeyUtils.java",
+    "javatests/src/org/chromium/content_public/browser/test/util/MatcherCriteria.java",
     "javatests/src/org/chromium/content_public/browser/test/util/RenderProcessLimit.java",
     "javatests/src/org/chromium/content_public/browser/test/util/TestCallbackHelperContainer.java",
     "javatests/src/org/chromium/content_public/browser/test/util/TestInputMethodManagerWrapper.java",
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/Criteria.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/Criteria.java
index 3080c23..2697e9e3 100644
--- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/Criteria.java
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/Criteria.java
@@ -4,6 +4,9 @@
 
 package org.chromium.content_public.browser.test.util;
 
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+
 import java.util.concurrent.Callable;
 
 /**
@@ -78,6 +81,21 @@
      * @return A Criteria that will check the equality of the passed in data.
      */
     public static <T> Criteria equals(T expectedValue, Callable<T> actualValueCallable) {
-        return new EqualityCriteria<T>(expectedValue, actualValueCallable);
+        return new MatcherCriteria<>(actualValueCallable, Matchers.equalTo(expectedValue));
+    }
+
+    /**
+     * Constructs a Criteria that determines if the actual value matches the specified matching
+     * criteria.
+     *
+     * @param <T> The type of value whose equality will be tested.
+     * @param actualValueCallable A {@link Callable} that provides a way of getting the current
+     *                            actual value.
+     * @param matcher Determines if the current value matches the desired expectation.
+     * @return A Criteria that will determine if the current value matches the expected criteria.
+     */
+    public static <T> Criteria checkThat(
+            Callable<T> actualValueCallable, Matcher<? super T> matcher) {
+        return new MatcherCriteria<>(actualValueCallable, matcher);
     }
 }
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/EqualityCriteria.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/EqualityCriteria.java
deleted file mode 100644
index afd3501..0000000
--- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/EqualityCriteria.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.content_public.browser.test.util;
-
-import java.util.concurrent.Callable;
-
-/**
- * Extension of the Criteria that handles object equality while providing a standard error message.
- *
- * @param <T> The type of value whose equality will be tested.
- */
-class EqualityCriteria<T> extends Criteria {
-    private final T mExpectedValue;
-    private final Callable<T> mActualValueCallable;
-
-    /**
-     * Construct the EqualityCriteria with the given expected value.
-     * @param expectedValue The value that is expected to determine the success of the criteria.
-     */
-    public EqualityCriteria(T expectedValue, Callable<T> actualValueCallable) {
-        mExpectedValue = expectedValue;
-        mActualValueCallable = actualValueCallable;
-    }
-
-    @Override
-    public final boolean isSatisfied() {
-        T actualValue = null;
-        try {
-            actualValue = mActualValueCallable.call();
-        } catch (Exception ex) {
-            updateFailureReason("Exception occurred: " + ex.getMessage());
-            ex.printStackTrace();
-            return false;
-        }
-
-        updateFailureReason(
-                "Values did not match. Expected: " + mExpectedValue + ", actual: " + actualValue);
-        if (mExpectedValue == null) {
-            return actualValue == null;
-        }
-        return mExpectedValue.equals(actualValue);
-    }
-}
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/MatcherCriteria.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/MatcherCriteria.java
new file mode 100644
index 0000000..8c2e989
--- /dev/null
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/MatcherCriteria.java
@@ -0,0 +1,55 @@
+// 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.content_public.browser.test.util;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.StringDescription;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Criteria that supports the Matcher assertion patterns.
+ *
+ * @param <T> The type of value being tested.
+ */
+public class MatcherCriteria<T> extends Criteria {
+    private final Matcher<? super T> mMatcher;
+    private final Callable<T> mActualValueCallable;
+
+    /**
+     * Construct the MatcherCriteria with a specific matcher and a means of fetching the current
+     * actual value.
+     * @param actualValueCallable Provides access to the current value.
+     * @param matcher Determines if the current value matches the desired expectation.
+     */
+    public MatcherCriteria(Callable<T> actualValueCallable, Matcher<? super T> matcher) {
+        mActualValueCallable = actualValueCallable;
+        mMatcher = matcher;
+    }
+
+    @Override
+    public final boolean isSatisfied() {
+        T actualValue = null;
+        try {
+            actualValue = mActualValueCallable.call();
+        } catch (Exception ex) {
+            updateFailureReason("Exception occurred: " + ex.getMessage());
+            ex.printStackTrace();
+            return false;
+        }
+
+        if (mMatcher.matches(actualValue)) return true;
+
+        Description description = new StringDescription();
+        description.appendText("Expected: ")
+                .appendDescriptionOf(mMatcher)
+                .appendText(System.lineSeparator())
+                .appendText("     but: ");
+        mMatcher.describeMismatch(actualValue, description);
+        updateFailureReason(description.toString());
+        return false;
+    }
+}
diff --git a/content/public/test/test_launcher.cc b/content/public/test/test_launcher.cc
index 656a50d..52812f5a 100644
--- a/content/public/test/test_launcher.cc
+++ b/content/public/test/test_launcher.cc
@@ -99,7 +99,7 @@
           "  --test-launcher-jobs=N\n"
           "    Sets the number of parallel test jobs to N.\n"
           "\n"
-          "  --single_process\n"
+          "  --single-process-tests\n"
           "    Runs the tests and the launcher in the same process. Useful\n"
           "    for debugging a specific test in a debugger.\n"
           "\n"
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index 3e4958b..d5d26d63 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -248,9 +248,9 @@
     "//cc",
     "//cc/paint",
     "//components/cdm/renderer",
-    "//components/crash/content/app",
-    "//components/crash/content/app:test_support",
     "//components/crash/content/browser",
+    "//components/crash/core/app",
+    "//components/crash/core/app:test_support",
     "//components/crash/core/common:crash_key",
     "//components/download/content/factory",
     "//components/download/public/background_service:public",
@@ -325,9 +325,9 @@
 
   if (is_fuchsia) {
     deps -= [
-      "//components/crash/content/app",
-      "//components/crash/content/app:test_support",
       "//components/crash/content/browser",
+      "//components/crash/core/app",
+      "//components/crash/core/app:test_support",
     ]
     deps += [ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.policy" ]
   }
@@ -675,7 +675,7 @@
     testonly = true
     sources = [ "$root_out_dir/chrome_crashpad_handler" ]
     outputs = [ "{{bundle_contents_dir}}/Helpers/{{source_file_part}}" ]
-    public_deps = [ "//components/crash/content/app:chrome_crashpad_handler" ]
+    public_deps = [ "//components/crash/core/app:chrome_crashpad_handler" ]
     foreach(helper_params, content_mac_helpers) {
       sources += [
         "$root_out_dir/${content_shell_helper_name}${helper_params[2]}.app",
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn
index 35c5f9a..c5a9941f 100644
--- a/content/shell/android/BUILD.gn
+++ b/content/shell/android/BUILD.gn
@@ -193,7 +193,7 @@
       "//base:base_java",
       "//base:base_java_test_support",
       "//components/crash/android:java",
-      "//components/crash/content/app:chrome_crashpad_handler_named_as_so",
+      "//components/crash/core/app:chrome_crashpad_handler_named_as_so",
       "//content/public/android:content_java",
       "//content/public/test/android:android_test_message_pump_support_java",
       "//media/capture/video/android:capture_java",
diff --git a/content/shell/app/shell_crash_reporter_client.h b/content/shell/app/shell_crash_reporter_client.h
index 9982b9fc..dd40498 100644
--- a/content/shell/app/shell_crash_reporter_client.h
+++ b/content/shell/app/shell_crash_reporter_client.h
@@ -8,7 +8,7 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "build/build_config.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 
 namespace content {
 
diff --git a/content/shell/app/shell_main_delegate.cc b/content/shell/app/shell_main_delegate.cc
index 56cd1f3..7df3a645 100644
--- a/content/shell/app/shell_main_delegate.cc
+++ b/content/shell/app/shell_main_delegate.cc
@@ -68,7 +68,7 @@
 #endif
 
 #if !defined(OS_FUCHSIA)
-#include "components/crash/content/app/crashpad.h"  // nogncheck
+#include "components/crash/core/app/crashpad.h"  // nogncheck
 #endif
 
 #if defined(OS_MACOSX)
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index 383c922..06e23ef 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -77,8 +77,8 @@
 #endif
 
 #if defined(OS_LINUX) || defined(OS_ANDROID)
-#include "components/crash/content/app/crash_switches.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crash_switches.h"
+#include "components/crash/core/app/crashpad.h"
 #include "content/public/common/content_descriptors.h"
 #endif
 
diff --git a/content/shell/common/v8_crashpad_support_win.cc b/content/shell/common/v8_crashpad_support_win.cc
index 5c1e8ab..20cbe59 100644
--- a/content/shell/common/v8_crashpad_support_win.cc
+++ b/content/shell/common/v8_crashpad_support_win.cc
@@ -6,7 +6,7 @@
 
 #include <windows.h>
 #include "base/logging.h"
-#include "components/crash/content/app/crash_export_thunks.h"
+#include "components/crash/core/app/crash_export_thunks.h"
 #include "gin/public/debug.h"
 
 namespace v8_crashpad_support {
diff --git a/content/test/data/frame_tree/page_with_large_iframe.html b/content/test/data/frame_tree/page_with_large_iframe.html
new file mode 100644
index 0000000..73fa88e
--- /dev/null
+++ b/content/test/data/frame_tree/page_with_large_iframe.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<body>
+<style>
+iframe {
+  width: 90vw;
+  height: 90vh;
+}
+</style>
+<iframe></iframe>
+</body>
+</html>
diff --git a/content/test/data/frame_tree/scrollable_page_with_two_frames.html b/content/test/data/frame_tree/scrollable_page_with_two_frames.html
new file mode 100644
index 0000000..9706ecc
--- /dev/null
+++ b/content/test/data/frame_tree/scrollable_page_with_two_frames.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<style>
+iframe {
+  width: 90vw;
+  height: 40vh;
+}
+div {
+  position:absolute;
+  top: 10000px;
+  left: 50px;
+  width: 100px;
+  height: 100px;
+}
+</style>
+<html>
+<body>
+<iframe></iframe>
+<br>
+<iframe></iframe>
+<br>
+Scrollable page contains two iframes.
+<div></div>
+</body>
+</html>
diff --git a/content/test/data/page_with_coop_and_coep.html b/content/test/data/page_with_coop_and_coep.html
new file mode 100644
index 0000000..7a1f130
--- /dev/null
+++ b/content/test/data/page_with_coop_and_coep.html
@@ -0,0 +1,4 @@
+<html>
+<head></head>
+<body>This is a basic page returned with the COOP and COEP headers.</body>
+</html>
diff --git a/content/test/data/page_with_coop_and_coep.html.mock-http-headers b/content/test/data/page_with_coop_and_coep.html.mock-http-headers
new file mode 100644
index 0000000..858d4a51
--- /dev/null
+++ b/content/test/data/page_with_coop_and_coep.html.mock-http-headers
@@ -0,0 +1,4 @@
+HTTP/1.1 200 OK
+Content-Type: text/html
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/docs/branch_sheriff.md b/docs/branch_sheriff.md
index 517d2fa..8b42d87 100644
--- a/docs/branch_sheriff.md
+++ b/docs/branch_sheriff.md
@@ -94,8 +94,8 @@
 [chromiumdash-branches]: https://chromiumdash.appspot.com/branches
 [chromiumdash-releases]: https://chromiumdash.appspot.com/releases
 [chromiumdash-schedule]: https://chromiumdash.appspot.com/schedule
-[main-beta]: https://ci.chromium.org/p/chromium/g/main-beta/console
-[main-stable]: https://ci.chromium.org/p/chromium/g/main-stable/console
+[main-beta]: https://ci.chromium.org/p/chromium/g/main-m81/console
+[main-stable]: https://ci.chromium.org/p/chromium/g/main-m80/console
 [milestone-json]: https://goto.google.com/chrome-milestone-json
 [rotation-home]: https://goto.google.com/chrome-branch-sheriff-amer-west
 [rotation-config]: https://goto.google.com/chrome-branch-sheriff-amer-west-config
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index c65bf1a..575ad00 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -524,7 +524,7 @@
       "//chromeos/dbus/upstart",
       "//chromeos/login/login_state",
       "//chromeos/network",
-      "//components/crash/content/app:app",
+      "//components/crash/core/app:app",
     ]
   }
 }
diff --git a/extensions/browser/api/crash_report_private/BUILD.gn b/extensions/browser/api/crash_report_private/BUILD.gn
index 3851b012..7d9b411 100644
--- a/extensions/browser/api/crash_report_private/BUILD.gn
+++ b/extensions/browser/api/crash_report_private/BUILD.gn
@@ -14,7 +14,7 @@
   ]
 
   deps = [
-    "//components/crash/content/app",
+    "//components/crash/core/app",
     "//components/feedback",
     "//content/public/browser",
     "//extensions/common/api",
diff --git a/extensions/browser/api/crash_report_private/DEPS b/extensions/browser/api/crash_report_private/DEPS
index 2d6cbadb..e786bbf1 100644
--- a/extensions/browser/api/crash_report_private/DEPS
+++ b/extensions/browser/api/crash_report_private/DEPS
@@ -1,10 +1,10 @@
 include_rules = [
-  "+components/crash/content/app/client_upload_info.h",
+  "+components/crash/core/app/client_upload_info.h",
   "+components/feedback/anonymizer_tool.h",
 ]
 
 specific_include_rules = {
   "crash_report_private_apitest.cc": [
-    "+components/crash/content/app/crash_reporter_client.h",
+    "+components/crash/core/app/crash_reporter_client.h",
   ],
 }
diff --git a/extensions/browser/api/crash_report_private/crash_report_private_api.cc b/extensions/browser/api/crash_report_private/crash_report_private_api.cc
index ce34639..973f6e6c 100644
--- a/extensions/browser/api/crash_report_private/crash_report_private_api.cc
+++ b/extensions/browser/api/crash_report_private/crash_report_private_api.cc
@@ -9,7 +9,7 @@
 #include "base/system/sys_info.h"
 #include "base/task/post_task.h"
 #include "base/time/default_clock.h"
-#include "components/crash/content/app/client_upload_info.h"
+#include "components/crash/core/app/client_upload_info.h"
 #include "components/feedback/anonymizer_tool.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/extensions/browser/api/crash_report_private/crash_report_private_apitest.cc b/extensions/browser/api/crash_report_private/crash_report_private_apitest.cc
index 215f7c9..d53409f 100644
--- a/extensions/browser/api/crash_report_private/crash_report_private_apitest.cc
+++ b/extensions/browser/api/crash_report_private/crash_report_private_apitest.cc
@@ -4,7 +4,7 @@
 
 #include "base/system/sys_info.h"
 #include "base/test/simple_test_clock.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 #include "content/public/test/browser_task_environment.h"
 #include "extensions/browser/api/crash_report_private/crash_report_private_api.h"
 #include "extensions/browser/browsertest_util.h"
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn
index 498b91e..c8f6ce7 100644
--- a/extensions/shell/BUILD.gn
+++ b/extensions/shell/BUILD.gn
@@ -202,7 +202,7 @@
     ]
     deps += [
       "//build:branding_buildflags",
-      "//components/crash/content/app",
+      "//components/crash/core/app",
       "//components/crash/core/common",
       "//components/upload_list",
       "//components/version_info:generate_version_info",
diff --git a/extensions/shell/app/shell_crash_reporter_client.h b/extensions/shell/app/shell_crash_reporter_client.h
index c0e5a3f..62b4c85 100644
--- a/extensions/shell/app/shell_crash_reporter_client.h
+++ b/extensions/shell/app/shell_crash_reporter_client.h
@@ -6,7 +6,7 @@
 #define EXTENSIONS_SHELL_APP_SHELL_CRASH_REPORTER_CLIENT_H_
 
 #include "base/macros.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 
 namespace extensions {
 
diff --git a/extensions/shell/app/shell_main_delegate.cc b/extensions/shell/app/shell_main_delegate.cc
index e9fb180..27b627e3 100644
--- a/extensions/shell/app/shell_main_delegate.cc
+++ b/extensions/shell/app/shell_main_delegate.cc
@@ -45,8 +45,8 @@
 #endif
 
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-#include "components/crash/content/app/breakpad_linux.h"         // nogncheck
-#include "components/crash/content/app/crash_reporter_client.h"  // nogncheck
+#include "components/crash/core/app/breakpad_linux.h"         // nogncheck
+#include "components/crash/core/app/crash_reporter_client.h"  // nogncheck
 #include "extensions/shell/app/shell_crash_reporter_client.h"
 #endif
 
diff --git a/gpu/command_buffer/client/webgpu_implementation.cc b/gpu/command_buffer/client/webgpu_implementation.cc
index 7db6a3c..b43f4fa1 100644
--- a/gpu/command_buffer/client/webgpu_implementation.cc
+++ b/gpu/command_buffer/client/webgpu_implementation.cc
@@ -51,8 +51,6 @@
 
 // This function can only be called once for each WebGPUCommandSerializer
 // object (before any call of GetCmdSpace()).
-// TODO(jiawei.shao@intel.com): early return and directly call the callback
-// function when the connection to the GPU process has been lost.
 void WebGPUCommandSerializer::RequestDeviceCreation(
     uint32_t requested_adapter_id,
     const WGPUDeviceProperties& requested_device_properties) {
@@ -345,11 +343,19 @@
 }
 
 // GpuControlClient implementation.
+// TODO(jiawei.shao@intel.com): do other clean-ups when the context is lost.
 void WebGPUImplementation::OnGpuControlLostContext() {
-  NOTIMPLEMENTED();
+  OnGpuControlLostContextMaybeReentrant();
+
+  // This should never occur more than once.
+  DCHECK(!lost_context_callback_run_);
+  lost_context_callback_run_ = true;
+  if (!lost_context_callback_.is_null()) {
+    std::move(lost_context_callback_).Run();
+  }
 }
 void WebGPUImplementation::OnGpuControlLostContextMaybeReentrant() {
-  NOTIMPLEMENTED();
+  lost_ = true;
 }
 void WebGPUImplementation::OnGpuControlErrorMessage(const char* message,
                                                     int32_t id) {
@@ -553,6 +559,10 @@
     PowerPreference power_preference,
     base::OnceCallback<void(uint32_t, const WGPUDeviceProperties&)>
         request_adapter_callback) {
+  if (lost_) {
+    return false;
+  }
+
   // Now that we declare request_adapter_serial as an uint64, it can't overflow
   // because we just increment an uint64 by one.
   DawnRequestAdapterSerial request_adapter_serial = NextRequestAdapterSerial();
@@ -579,6 +589,10 @@
     base::OnceCallback<void(bool, DawnDeviceClientID)>
         request_device_callback) {
 #if BUILDFLAG(USE_DAWN)
+  if (lost_) {
+    return false;
+  }
+
   // Now that we declare device_client_id as an uint64, it can't overflow
   // because we just increment an uint64 by one.
   DawnDeviceClientID device_client_id = NextDeviceClientID();
diff --git a/gpu/command_buffer/client/webgpu_implementation.h b/gpu/command_buffer/client/webgpu_implementation.h
index 9a97ed4..6dfd3c5 100644
--- a/gpu/command_buffer/client/webgpu_implementation.h
+++ b/gpu/command_buffer/client/webgpu_implementation.h
@@ -193,6 +193,8 @@
       request_device_callback_map_;
   DawnDeviceClientID device_client_id_ = 0;
 
+  std::atomic_bool lost_{false};
+
   DISALLOW_COPY_AND_ASSIGN(WebGPUImplementation);
 };
 
diff --git a/gpu/command_buffer/tests/webgpu_test.cc b/gpu/command_buffer/tests/webgpu_test.cc
index 1bafc87..0e25112 100644
--- a/gpu/command_buffer/tests/webgpu_test.cc
+++ b/gpu/command_buffer/tests/webgpu_test.cc
@@ -25,6 +25,10 @@
 void OnRequestAdapterCallback(uint32_t adapter_service_id,
                               const WGPUDeviceProperties& properties) {}
 
+void CountCallback(int* count) {
+  (*count)++;
+}
+
 }  // anonymous namespace
 
 WebGPUTest::Options::Options() = default;
@@ -103,7 +107,7 @@
   dawnProcSetProcs(&procs);
 }
 
-webgpu::WebGPUInterface* WebGPUTest::webgpu() const {
+webgpu::WebGPUImplementation* WebGPUTest::webgpu() const {
   return context_->GetImplementation();
 }
 
@@ -164,4 +168,73 @@
   webgpu()->FlushCommands();
 }
 
+// Referred from GLES2ImplementationTest/ReportLoss
+TEST_F(WebGPUTest, ReportLoss) {
+  Initialize(WebGPUTest::Options());
+
+  if (!WebGPUSupported()) {
+    LOG(ERROR) << "Test skipped because WebGPU isn't supported";
+    return;
+  }
+
+  GpuControlClient* webgpu_as_client = webgpu();
+  int lost_count = 0;
+  webgpu()->SetLostContextCallback(base::BindOnce(&CountCallback, &lost_count));
+  EXPECT_EQ(0, lost_count);
+
+  webgpu_as_client->OnGpuControlLostContext();
+  // The lost context callback should be run when WebGPUImplementation is
+  // notified of the loss.
+  EXPECT_EQ(1, lost_count);
+}
+
+// Referred from GLES2ImplementationTest/ReportLossReentrant
+TEST_F(WebGPUTest, ReportLossReentrant) {
+  Initialize(WebGPUTest::Options());
+
+  if (!WebGPUSupported()) {
+    LOG(ERROR) << "Test skipped because WebGPU isn't supported";
+    return;
+  }
+
+  GpuControlClient* webgpu_as_client = webgpu();
+  int lost_count = 0;
+  webgpu()->SetLostContextCallback(base::BindOnce(&CountCallback, &lost_count));
+  EXPECT_EQ(0, lost_count);
+
+  webgpu_as_client->OnGpuControlLostContextMaybeReentrant();
+  // The lost context callback should not be run yet to avoid calling back into
+  // clients re-entrantly, and having them re-enter WebGPUImplementation.
+  EXPECT_EQ(0, lost_count);
+}
+
+TEST_F(WebGPUTest, RequestAdapterAfterContextLost) {
+  Initialize(WebGPUTest::Options());
+
+  if (!WebGPUSupported()) {
+    LOG(ERROR) << "Test skipped because WebGPU isn't supported";
+    return;
+  }
+
+  webgpu()->OnGpuControlLostContext();
+  ASSERT_FALSE(
+      webgpu()->RequestAdapterAsync(webgpu::PowerPreference::kDefault,
+                                    base::BindOnce(&OnRequestAdapterCallback)));
+}
+
+TEST_F(WebGPUTest, RequestDeviceAfterContextLost) {
+  Initialize(WebGPUTest::Options());
+
+  if (!WebGPUSupported()) {
+    LOG(ERROR) << "Test skipped because WebGPU isn't supported";
+    return;
+  }
+
+  webgpu()->OnGpuControlLostContext();
+  ASSERT_FALSE(webgpu()->RequestDeviceAsync(
+      kAdapterServiceID, {},
+      base::BindOnce(
+          [](bool success, webgpu::DawnDeviceClientID assigned_client_id) {})));
+}
+
 }  // namespace gpu
diff --git a/gpu/command_buffer/tests/webgpu_test.h b/gpu/command_buffer/tests/webgpu_test.h
index fa6a512..38697bc3 100644
--- a/gpu/command_buffer/tests/webgpu_test.h
+++ b/gpu/command_buffer/tests/webgpu_test.h
@@ -27,7 +27,7 @@
 
 namespace webgpu {
 
-class WebGPUInterface;
+class WebGPUImplementation;
 
 }  // namespace webgpu
 
@@ -51,7 +51,7 @@
 
   void Initialize(const Options& options);
 
-  webgpu::WebGPUInterface* webgpu() const;
+  webgpu::WebGPUImplementation* webgpu() const;
   SharedImageInterface* GetSharedImageInterface() const;
 
   void RunPendingTasks();
@@ -63,13 +63,14 @@
   };
   DeviceAndClientID GetNewDeviceAndClientID();
 
+  const uint32_t kAdapterServiceID = 0u;
+
  private:
   std::unique_ptr<viz::TestGpuServiceHolder> gpu_service_holder_;
   std::unique_ptr<WebGPUInProcessContext> context_;
   bool is_initialized_ = false;
 
   webgpu::DawnDeviceClientID next_device_client_id_ = 1;
-  const uint32_t kAdapterServiceID = 0u;
 };
 
 }  // namespace gpu
diff --git a/gpu/ipc/webgpu_in_process_context.cc b/gpu/ipc/webgpu_in_process_context.cc
index a5cecb7..1ee9974 100644
--- a/gpu/ipc/webgpu_in_process_context.cc
+++ b/gpu/ipc/webgpu_in_process_context.cc
@@ -95,7 +95,7 @@
   return command_buffer_->GetGpuFeatureInfo();
 }
 
-webgpu::WebGPUInterface* WebGPUInProcessContext::GetImplementation() {
+webgpu::WebGPUImplementation* WebGPUInProcessContext::GetImplementation() {
   return webgpu_implementation_.get();
 }
 
diff --git a/gpu/ipc/webgpu_in_process_context.h b/gpu/ipc/webgpu_in_process_context.h
index da208b87..b2097200 100644
--- a/gpu/ipc/webgpu_in_process_context.h
+++ b/gpu/ipc/webgpu_in_process_context.h
@@ -25,7 +25,6 @@
 struct SharedMemoryLimits;
 
 namespace webgpu {
-class WebGPUInterface;
 class WebGPUImplementation;
 }  // namespace webgpu
 
@@ -52,7 +51,7 @@
 
   // Allows direct access to the WebGPUImplementation so a
   // WebGPUInProcessContext can be used without making it current.
-  gpu::webgpu::WebGPUInterface* GetImplementation();
+  gpu::webgpu::WebGPUImplementation* GetImplementation();
   base::TestSimpleTaskRunner* GetTaskRunner();
 
   // Test only functions.
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index db85b6be..a6755c8 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -290,7 +290,7 @@
     deps += [ "//components/crash/content/browser" ]
   }
   if (is_component_build && is_win) {
-    deps += [ "//components/crash/content/app:crash_export_thunks" ]
+    deps += [ "//components/crash/core/app:crash_export_thunks" ]
   }
 
   if (is_linux && !is_chromeos) {
@@ -446,7 +446,7 @@
       deps += [ "//components/crash/content/browser" ]
     }
     if (is_win) {
-      deps += [ "//components/crash/content/app:crash_export_thunks" ]
+      deps += [ "//components/crash/core/app:crash_export_thunks" ]
     }
 
     if (enable_basic_printing) {
@@ -594,7 +594,7 @@
   }
 
   if (is_win) {
-    deps += [ "//components/crash/content/app:crash_export_thunks" ]
+    deps += [ "//components/crash/core/app:crash_export_thunks" ]
   }
 
   if (enable_basic_printing) {
@@ -611,7 +611,7 @@
   copy("mac_helpers") {
     sources = [ "$root_out_dir/chrome_crashpad_handler" ]
 
-    deps = [ "//components/crash/content/app:chrome_crashpad_handler" ]
+    deps = [ "//components/crash/core/app:chrome_crashpad_handler" ]
 
     outputs = [ "$root_out_dir/Helpers/{{source_file_part}}" ]
   }
@@ -817,8 +817,8 @@
     defines = [ "HEADLESS_USE_CRASHPAD" ]
 
     deps += [
-      "//components/crash/content/app:crash_export_thunks",
-      "//components/crash/content/app:run_as_crashpad_handler",
+      "//components/crash/core/app:crash_export_thunks",
+      "//components/crash/core/app:run_as_crashpad_handler",
       "//content:sandbox_helper_win",
       "//sandbox",
     ]
diff --git a/headless/DEPS b/headless/DEPS
index 6ad2b86b..51e7ce5 100644
--- a/headless/DEPS
+++ b/headless/DEPS
@@ -1,7 +1,7 @@
 include_rules = [
   "+components/cookie_config",
-  "+components/crash/content/app",
   "+components/crash/content/browser",
+  "+components/crash/core/app",
   "+components/crash/core/common/crash_key.h",
   "+components/os_crypt",
   "+components/services/print_compositor/public",
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc
index 122c3495..7e703faf 100644
--- a/headless/app/headless_shell.cc
+++ b/headless/app/headless_shell.cc
@@ -49,8 +49,8 @@
 #include "ui/gfx/geometry/size.h"
 
 #if defined(OS_WIN)
-#include "components/crash/content/app/crash_switches.h"
-#include "components/crash/content/app/run_as_crashpad_handler_win.h"
+#include "components/crash/core/app/crash_switches.h"
+#include "components/crash/core/app/run_as_crashpad_handler_win.h"
 #include "sandbox/win/src/sandbox_types.h"
 #endif
 
diff --git a/headless/lib/browser/headless_content_browser_client.cc b/headless/lib/browser/headless_content_browser_client.cc
index 3612acbb..6e4d4742 100644
--- a/headless/lib/browser/headless_content_browser_client.cc
+++ b/headless/lib/browser/headless_content_browser_client.cc
@@ -39,8 +39,8 @@
 
 #if defined(HEADLESS_USE_BREAKPAD)
 #include "base/debug/leak_annotations.h"
-#include "components/crash/content/app/breakpad_linux.h"
 #include "components/crash/content/browser/crash_handler_host_linux.h"
+#include "components/crash/core/app/breakpad_linux.h"
 #include "content/public/common/content_descriptors.h"
 #endif  // defined(HEADLESS_USE_BREAKPAD)
 
diff --git a/headless/lib/headless_content_main_delegate.cc b/headless/lib/headless_content_main_delegate.cc
index c313755..665ae63 100644
--- a/headless/lib/headless_content_main_delegate.cc
+++ b/headless/lib/headless_content_main_delegate.cc
@@ -47,11 +47,11 @@
 #endif
 
 #if defined(OS_MACOSX) || defined(OS_WIN)
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crashpad.h"
 #endif
 
 #if defined(OS_LINUX)
-#include "components/crash/content/app/breakpad_linux.h"
+#include "components/crash/core/app/breakpad_linux.h"
 #endif
 
 #if !defined(CHROME_MULTIPLE_DLL_BROWSER)
diff --git a/headless/lib/headless_crash_reporter_client.h b/headless/lib/headless_crash_reporter_client.h
index 68273bc..31da13e 100644
--- a/headless/lib/headless_crash_reporter_client.h
+++ b/headless/lib/headless_crash_reporter_client.h
@@ -9,7 +9,7 @@
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "build/build_config.h"
-#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/core/app/crash_reporter_client.h"
 
 namespace headless {
 
diff --git a/infra/config/buckets/try.star b/infra/config/buckets/try.star
index c992de4..928c89b 100644
--- a/infra/config/buckets/try.star
+++ b/infra/config/buckets/try.star
@@ -548,6 +548,7 @@
 try_.chromium_mac_ios_builder(
     name = 'ios-simulator-code-coverage',
     executable = 'recipe:chromium_trybot',
+    goma_backend = None,  # TODO(crbug.com/950413): Use goma.backend.RBE_PROD
     use_clang_coverage = True,
     properties = {
         'xcode_build_version': '11c29',
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index abd99d0..4761ae4 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -15421,7 +15421,6 @@
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
         properties_j: "$build/code_coverage:{\"use_clang_coverage\":true}"
-        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"tryserver.chromium.mac\""
         properties_j: "xcode_build_version:\"11c29\""
diff --git a/infra/config/lib/try.star b/infra/config/lib/try.star
index efc1b13..428c29e 100644
--- a/infra/config/lib/try.star
+++ b/infra/config/lib/try.star
@@ -177,13 +177,18 @@
   )
 
 
-def chromium_mac_ios_builder(*, name, executable='recipe:ios/try', **kwargs):
+def chromium_mac_ios_builder(
+    *,
+    name,
+    executable='recipe:ios/try',
+    goma_backend=builders.goma.backend.RBE_PROD,
+    **kwargs):
   return try_builder(
       name = name,
       caches = [builders.xcode_cache.x11c29],
       cores = None,
       executable = executable,
-      goma_backend=builders.goma.backend.RBE_PROD,
+      goma_backend = goma_backend,
       mastername = 'tryserver.chromium.mac',
       os = builders.os.MAC_ANY,
       **kwargs
diff --git a/ios/chrome/browser/device_sharing/OWNERS b/ios/chrome/browser/device_sharing/OWNERS
index 7030015a3..01e8c98 100644
--- a/ios/chrome/browser/device_sharing/OWNERS
+++ b/ios/chrome/browser/device_sharing/OWNERS
@@ -1,4 +1,4 @@
-erikchen@chromium.org
+marq@chromium.org
 
 # TEAM: ios-directory-owners@chromium.org
 # OS: iOS
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index aec8247..d3d1057c 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -579,6 +579,9 @@
     {"safe-browsing-available", flag_descriptions::kSafeBrowsingAvailableName,
      flag_descriptions::kSafeBrowsingAvailableDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(safe_browsing::kSafeBrowsingAvailableOnIOS)},
+    {"new-signin-architecture", flag_descriptions::kNewSigninArchitectureName,
+     flag_descriptions::kNewSigninArchitectureDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(kNewSigninArchitecture)},
 };
 
 // Add all switches from experimental flags to |command_line|.
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index cc008e2..b5370862 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -375,6 +375,11 @@
     "When enabled, the startup sign-in promo is always displayed when starting "
     "Chrome.";
 
+const char kNewSigninArchitectureName[] = "Enable new sign-in architecture";
+const char kNewSigninArchitectureDescription[] =
+    "When enabled uses the new sign-in architecture based on core Bling "
+    "design paradigms.";
+
 const char kSyncDeviceInfoInTransportModeName[] =
     "Enable syncing DeviceInfo in transport-only sync mode.";
 const char kSyncDeviceInfoInTransportModeDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 213eacb..c52e205 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -325,6 +325,10 @@
 extern const char kForceStartupSigninPromoName[];
 extern const char kForceStartupSigninPromoDescription[];
 
+// Title and description for the flag to enable the new sign-in architecture.
+extern const char kNewSigninArchitectureName[];
+extern const char kNewSigninArchitectureDescription[];
+
 // Title and description for the flag to allow syncing DeviceInfo in
 // transport-only mode.
 extern const char kSyncDeviceInfoInTransportModeName[];
diff --git a/ios/chrome/browser/metrics/BUILD.gn b/ios/chrome/browser/metrics/BUILD.gn
index 1d875cf..756a763 100644
--- a/ios/chrome/browser/metrics/BUILD.gn
+++ b/ios/chrome/browser/metrics/BUILD.gn
@@ -72,7 +72,8 @@
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/crash_report",
     "//ios/chrome/browser/google",
-    "//ios/chrome/browser/history:history",
+    "//ios/chrome/browser/history",
+    "//ios/chrome/browser/main:public",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/sync",
     "//ios/chrome/browser/tabs",
diff --git a/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.h b/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.h
index 46d8e6c..38b9ae4 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.h
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.h
@@ -44,6 +44,11 @@
   bool IsMetricsConsentGiven() override;
   bool IsIncognitoSessionActive() override;
 
+  // Static helper for |IsIncognitoSessionActive()|, suitable for binding into
+  // callbacks. |true| if any browser states have any incognito WebStates in any
+  // Browser.
+  static bool AreIncognitoTabsPresent();
+
   // MetricsStateManager which is passed as a parameter to service constructors.
   std::unique_ptr<metrics::MetricsStateManager> metrics_state_manager_;
 
diff --git a/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.mm b/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.mm
index 2dd96f1..316907f 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.mm
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.mm
@@ -14,10 +14,13 @@
 #include "components/rappor/rappor_service_impl.h"
 #include "components/variations/service/variations_service.h"
 #include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state_manager.h"
 #include "ios/chrome/browser/chrome_switches.h"
+#import "ios/chrome/browser/main/browser.h"
+#import "ios/chrome/browser/main/browser_list.h"
+#import "ios/chrome/browser/main/browser_list_factory.h"
 #include "ios/chrome/browser/metrics/ios_chrome_metrics_service_accessor.h"
 #include "ios/chrome/browser/metrics/ios_chrome_metrics_service_client.h"
-#include "ios/chrome/browser/tabs/tab_model_list.h"
 #include "ios/chrome/browser/variations/ios_chrome_variations_service_client.h"
 #include "ios/chrome/browser/variations/ios_ui_string_overrider_factory.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -64,7 +67,9 @@
 IOSChromeMetricsServicesManagerClient::CreateRapporServiceImpl() {
   DCHECK(thread_checker_.CalledOnValidThread());
   return std::make_unique<rappor::RapporServiceImpl>(
-      local_state_, base::Bind(&TabModelList::IsOffTheRecordSessionActive));
+      local_state_,
+      base::Bind(
+          &IOSChromeMetricsServicesManagerClient::AreIncognitoTabsPresent));
 }
 
 std::unique_ptr<variations::VariationsService>
@@ -114,5 +119,24 @@
 }
 
 bool IOSChromeMetricsServicesManagerClient::IsIncognitoSessionActive() {
-  return TabModelList::IsOffTheRecordSessionActive();
+  return AreIncognitoTabsPresent();
+}
+
+// static
+bool IOSChromeMetricsServicesManagerClient::AreIncognitoTabsPresent() {
+  std::vector<ChromeBrowserState*> browser_states =
+      GetApplicationContext()
+          ->GetChromeBrowserStateManager()
+          ->GetLoadedBrowserStates();
+
+  for (ChromeBrowserState* browser_state : browser_states) {
+    BrowserList* browser_list =
+        BrowserListFactory::GetForBrowserState(browser_state);
+    for (Browser* browser : browser_list->AllIncognitoBrowsers()) {
+      if (!browser->GetWebStateList()->empty()) {
+        return true;
+      }
+    }
+  }
+  return false;
 }
diff --git a/ios/chrome/browser/overscroll_actions/overscroll_actions_tab_helper.mm b/ios/chrome/browser/overscroll_actions/overscroll_actions_tab_helper.mm
index b17f6be..4c71dde 100644
--- a/ios/chrome/browser/overscroll_actions/overscroll_actions_tab_helper.mm
+++ b/ios/chrome/browser/overscroll_actions/overscroll_actions_tab_helper.mm
@@ -38,7 +38,6 @@
           ? OverscrollStyle::REGULAR_PAGE_INCOGNITO
           : OverscrollStyle::REGULAR_PAGE_NON_INCOGNITO;
   overscroll_actions_controller_.delegate = delegate;
-  overscroll_actions_controller_.browserState = browser_state;
 }
 
 OverscrollActionsTabHelper::OverscrollActionsTabHelper(web::WebState* web_state)
diff --git a/ios/chrome/browser/policy/configuration_policy_handler_list_factory.mm b/ios/chrome/browser/policy/configuration_policy_handler_list_factory.mm
index c642fb2..1e6572e 100644
--- a/ios/chrome/browser/policy/configuration_policy_handler_list_factory.mm
+++ b/ios/chrome/browser/policy/configuration_policy_handler_list_factory.mm
@@ -11,13 +11,21 @@
 #include "components/policy/core/browser/configuration_policy_handler_parameters.h"
 #include "components/policy/policy_constants.h"
 #include "ios/chrome/browser/policy/policy_features.h"
+#include "ios/chrome/browser/pref_names.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+using policy::PolicyToPreferenceMapEntry;
+using policy::SimplePolicyHandler;
+
 namespace {
 
+const PolicyToPreferenceMapEntry kSimplePolicyMap[] = {
+    {policy::key::kSearchSuggestEnabled, prefs::kSearchSuggestEnabled,
+     base::Value::Type::BOOLEAN}};
+
 void PopulatePolicyHandlerParameters(
     policy::PolicyHandlerParameters* parameters) {}
 
@@ -30,5 +38,12 @@
       std::make_unique<policy::ConfigurationPolicyHandlerList>(
           base::Bind(&PopulatePolicyHandlerParameters),
           base::Bind(&policy::GetChromePolicyDetails));
+
+  for (size_t i = 0; i < base::size(kSimplePolicyMap); ++i) {
+    handlers->AddHandler(std::make_unique<SimplePolicyHandler>(
+        kSimplePolicyMap[i].policy_name, kSimplePolicyMap[i].preference_path,
+        kSimplePolicyMap[i].value_type));
+  }
+
   return handlers;
 }
diff --git a/ios/chrome/browser/tabs/DEPS b/ios/chrome/browser/tabs/DEPS
deleted file mode 100644
index 9d89a21..0000000
--- a/ios/chrome/browser/tabs/DEPS
+++ /dev/null
@@ -1,21 +0,0 @@
-specific_include_rules = {
-  # TODO(crbug.com/620465): Remove tab.mm and tab_unittest.mm exceptions.
-  "^tab\.mm$": [
-    "+ios/web/web_state/web_state_impl.h",
-    "+ios/web/web_state/ui/crw_web_controller.h",
-    "+ios/web/navigation/navigation_manager_impl.h",
-  ],
-  "^tab_unittest\.mm$": [
-    "+ios/web/web_state/ui/crw_web_controller.h",
-    "+ios/web/navigation/navigation_manager_impl.h",
-    "+ios/web/navigation/navigation_context_impl.h",
-    "+ios/web/web_state/web_state_impl.h",
-    "+ios/web/test/fakes/crw_fake_back_forward_list.h",
-  ],
-
-  # TODO(crbug.com/620480): tab_model_unittest.mm exceptions.
-  "^tab_model_unittest\.mm$": [
-    "+ios/web/navigation/navigation_manager_impl.h",
-    "+ios/web/web_state/web_state_impl.h",
-  ],
-}
diff --git a/ios/chrome/browser/ui/DEPS b/ios/chrome/browser/ui/DEPS
index 26909e7..2084452 100644
--- a/ios/chrome/browser/ui/DEPS
+++ b/ios/chrome/browser/ui/DEPS
@@ -1,21 +1,4 @@
 specific_include_rules = {
-  # TODO(crbug.com/620489): Remove these exceptions.
-  "^browser_view_controller\.mm$": [
-    "+ios/web/web_state/ui/crw_web_controller.h",
-  ],
-  "^browser_view_controller_unittest\.mm$": [
-    "+ios/web/web_state/ui/crw_web_controller.h",
-    "+ios/web/web_state/web_state_impl.h",
-  ],
-
-  # TODO(crbug.com/620147): Remove these exceptions.
-  "^open_in_controller\.mm$": [
-    "+ios/web/web_state/ui/crw_web_controller.h",
-  ],
-  "^open_in_controller_unittest\.mm$": [
-    "+ios/web/web_state/ui/crw_web_controller.h",
-  ],
-
   # web::HttpServer is deprecated in favor of net::EmbeddedTestServer.
   # TODO:(crbug.com/891834) Remove this exception.
   "browser_view_controller_egtest\.mm": [
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h
index 45429fb..fc4dd671 100644
--- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h
+++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h
@@ -115,7 +115,7 @@
       const std::vector<base::string16>& values,
       const std::vector<base::string16>& labels) override;
   base::span<const autofill::Suggestion> GetPopupSuggestions() const override;
-  void PinPopupViewUntilUpdate() override;
+  void PinPopupView() override;
   void UpdatePopup(const std::vector<autofill::Suggestion>& suggestions,
                    PopupType popup_type) override;
   void HideAutofillPopup(PopupHidingReason reason) override;
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
index b80d438b..61b65ef 100644
--- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
+++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
@@ -209,10 +209,11 @@
     const CreditCard& card,
     UnmaskCardReason reason,
     base::WeakPtr<CardUnmaskDelegate> delegate) {
-  unmask_controller_.ShowPrompt(base::Bind(&CreateCardUnmaskPromptViewBridge,
-                                    base::Unretained(&unmask_controller_),
-                                    base::Unretained(base_view_controller_)),
-                                card, reason, delegate);
+  unmask_controller_.ShowPrompt(
+      base::Bind(&CreateCardUnmaskPromptViewBridge,
+                 base::Unretained(&unmask_controller_),
+                 base::Unretained(base_view_controller_)),
+      card, reason, delegate);
 }
 
 void ChromeAutofillClientIOS::OnUnmaskVerificationResult(
@@ -356,7 +357,7 @@
   return base::span<const autofill::Suggestion>();
 }
 
-void ChromeAutofillClientIOS::PinPopupViewUntilUpdate() {
+void ChromeAutofillClientIOS::PinPopupView() {
   NOTIMPLEMENTED();
 }
 
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index fffd326..3610ec0 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -190,7 +190,6 @@
 #include "ios/web/public/web_client.h"
 #import "ios/web/public/web_state_delegate_bridge.h"
 #import "ios/web/public/web_state_observer_bridge.h"
-#import "ios/web/web_state/ui/crw_web_controller.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -2195,8 +2194,7 @@
 
   _sadTabCoordinator = [[SadTabCoordinator alloc]
       initWithBaseViewController:self.browserContainerViewController
-                    browserState:self.browserState];
-  _sadTabCoordinator.dispatcher = self.dispatcher;
+                         browser:self.browser];
   _sadTabCoordinator.overscrollDelegate = self;
 
   // If there are any existing SadTabHelpers in
@@ -2625,7 +2623,6 @@
     OverscrollActionsTabHelper::FromWebState(webState)->SetDelegate(self);
   }
 
-  // Install the proper CRWWebController delegates.
   web_deprecated::SetSwipeRecognizerProvider(webState,
                                              self.sideSwipeController);
   webState->SetDelegate(_webStateDelegate.get());
@@ -3524,20 +3521,23 @@
   }
 }
 
-- (BOOL)shouldAllowOverscrollActions {
+- (BOOL)shouldAllowOverscrollActionsForOverscrollActionsController:
+    (OverscrollActionsController*)controller {
   return !self.toolbarAccessoryPresenter.presenting;
 }
 
-- (UIView*)headerView {
+- (UIView*)headerViewForOverscrollActionsController:
+    (OverscrollActionsController*)controller {
   return self.primaryToolbarCoordinator.viewController.view;
 }
 
-- (UIView*)toolbarSnapshotView {
+- (UIView*)toolbarSnapshotViewForOverscrollActionsController:
+    (OverscrollActionsController*)controller {
   return [self.primaryToolbarCoordinator.viewController.view
       snapshotViewAfterScreenUpdates:NO];
 }
 
-- (CGFloat)overscrollActionsControllerHeaderInset:
+- (CGFloat)headerInsetForOverscrollActionsController:
     (OverscrollActionsController*)controller {
   // The current WebState can be nil if the Browser's WebStateList is empty
   // (e.g. after closing the last tab, etc).
@@ -3553,10 +3553,16 @@
     return 0;
 }
 
-- (CGFloat)overscrollHeaderHeight {
+- (CGFloat)headerHeightForOverscrollActionsController:
+    (OverscrollActionsController*)controller {
   return self.headerHeight;
 }
 
+- (FullscreenController*)fullscreenControllerForOverscrollActionsController:
+    (OverscrollActionsController*)controller {
+  return self.fullscreenController;
+}
+
 #pragma mark - DialogPresenterDelegate methods
 
 - (void)dialogPresenter:(DialogPresenter*)presenter
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
index 3fd4a6b9..9a6dd26 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -259,30 +259,40 @@
   }
 }
 
-- (BOOL)shouldAllowOverscrollActions {
+- (BOOL)shouldAllowOverscrollActionsForOverscrollActionsController:
+    (OverscrollActionsController*)controller {
   return YES;
 }
 
-- (UIView*)toolbarSnapshotView {
+- (UIView*)toolbarSnapshotViewForOverscrollActionsController:
+    (OverscrollActionsController*)controller {
   return
       [[self.headerController toolBarView] snapshotViewAfterScreenUpdates:NO];
 }
 
-- (UIView*)headerView {
+- (UIView*)headerViewForOverscrollActionsController:
+    (OverscrollActionsController*)controller {
   return self.suggestionsViewController.view;
 }
 
-- (CGFloat)overscrollActionsControllerHeaderInset:
+- (CGFloat)headerInsetForOverscrollActionsController:
     (OverscrollActionsController*)controller {
   return 0;
 }
 
-- (CGFloat)overscrollHeaderHeight {
+- (CGFloat)headerHeightForOverscrollActionsController:
+    (OverscrollActionsController*)controller {
   CGFloat height = [self.headerController toolBarView].bounds.size.height;
   CGFloat topInset = self.suggestionsViewController.view.safeAreaInsets.top;
   return height + topInset;
 }
 
+- (FullscreenController*)fullscreenControllerForOverscrollActionsController:
+    (OverscrollActionsController*)controller {
+  // Fullscreen isn't supported here.
+  return nullptr;
+}
+
 #pragma mark - Public methods
 
 - (UIView*)view {
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index 1a070376..528a3aa3 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -1485,6 +1485,7 @@
 
 - (void)presentSignedInAccountsViewControllerForBrowserState:
     (ChromeBrowserState*)browserState {
+  UMA_HISTOGRAM_BOOLEAN("Signin.SignedInAccountsViewImpression", true);
   UIViewController* accountsViewController =
       [[SignedInAccountsViewController alloc]
           initWithBrowserState:browserState
diff --git a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h
index e9aafe29..567cd0e 100644
--- a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h
+++ b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h
@@ -10,8 +10,8 @@
 #import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.h"
 #import "ios/web/public/ui/crw_web_view_scroll_view_proxy.h"
 
-class ChromeBrowserState;
 @protocol CRWWebViewProxy;
+class FullscreenController;
 @class OverscrollActionsController;
 
 // Describe the current state of the overscroll action controller.
@@ -43,18 +43,29 @@
 - (void)overscrollActionsController:(OverscrollActionsController*)controller
                    didTriggerAction:(OverscrollAction)action;
 // Should return true when the delegate wants to enable the overscroll actions.
-- (BOOL)shouldAllowOverscrollActions;
+- (BOOL)shouldAllowOverscrollActionsForOverscrollActionsController:
+    (OverscrollActionsController*)controller;
 // The toolbar snapshot view that will be used to fade in/out the toolbar.
 // This snapshot will be animated when performing the pull down animation
 // revealing the actions.
-- (UIView*)toolbarSnapshotView;
+- (UIView*)toolbarSnapshotViewForOverscrollActionsController:
+    (OverscrollActionsController*)controller;
 // The header view over which the overscroll action view will be added.
-- (UIView*)headerView;
+- (UIView*)headerViewForOverscrollActionsController:
+    (OverscrollActionsController*)controller;
 // Called to retrieve the top inset added to the scrollview for the header.
-- (CGFloat)overscrollActionsControllerHeaderInset:
+- (CGFloat)headerInsetForOverscrollActionsController:
     (OverscrollActionsController*)controller;
 // Called to retrieve the current height of the header.
-- (CGFloat)overscrollHeaderHeight;
+- (CGFloat)headerHeightForOverscrollActionsController:
+    (OverscrollActionsController*)controller;
+// The fullscreen controller, if any, the delegate makes available for
+// overscroll. The deleagate may return nullptr if fullscreen isn't supported.
+// This method will be called each time the overscroll state might need to
+// interact with fullscreen, so it's fine for a delegate to return nullptr as
+// soon as it enters a state where fullscreen is unsupported.
+- (FullscreenController*)fullscreenControllerForOverscrollActionsController:
+    (OverscrollActionsController*)controller;
 @end
 
 // The OverscrollActionsController controls the display of an
@@ -94,8 +105,6 @@
 // The delegate must be set for the OverscrollActionsController to work
 // properly.
 @property(nonatomic, weak) id<OverscrollActionsControllerDelegate> delegate;
-// The BrowserState.
-@property(nonatomic, assign) ChromeBrowserState* browserState;
 
 // Used to clear state maintained by the controller and de-register from
 // notifications. After this call the controller ceases to function and will
diff --git a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
index 0a3c6f4..1765f6e 100644
--- a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
+++ b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
@@ -267,7 +267,6 @@
 @synthesize initialHeaderHeight = _initialHeaderHeight;
 @synthesize overscrollState = _overscrollState;
 @synthesize delegate = _delegate;
-@synthesize browserState = _browserState;
 @synthesize panPointScreenOrigin = _panPointScreenOrigin;
 @synthesize panGestureRecognizer = _panGestureRecognizer;
 @synthesize scrollViewDragged = _scrollViewDragged;
@@ -397,7 +396,8 @@
       // Set the contentInset to remove the bounce that would fight with drag.
       [self setScrollViewContentInset:insets];
       [self scrollView].scrollIndicatorInsets = insets;
-      _initialHeaderHeight = [[self delegate] overscrollHeaderHeight];
+      _initialHeaderHeight =
+          [[self delegate] headerHeightForOverscrollActionsController:self];
       self.overscrollState = OverscrollState::STARTED_PULLING;
     }
     [self updateWithVerticalOffset:-contentOffsetFromExpandedHeader
@@ -427,8 +427,8 @@
       (CACurrentMediaTime() - _lastScrollBeginTime) >=
       kMinimumDurationBetweenScrollingInSeconds;
   // Finally check that the delegate allow overscroll actions.
-  const BOOL delegateAllowOverscrollActions =
-      [self.delegate shouldAllowOverscrollActions];
+  const BOOL delegateAllowOverscrollActions = [self.delegate
+      shouldAllowOverscrollActionsForOverscrollActionsController:self];
   const BOOL isCurrentlyProcessingOverscroll =
       self.overscrollState != OverscrollState::NO_PULL_STARTED;
   return isCurrentlyProcessingOverscroll ||
@@ -771,10 +771,12 @@
     } break;
     case OverscrollState::STARTED_PULLING: {
       if (!self.overscrollActionView.superview && self.scrollViewDragged) {
-        UIView* headerView = [self.delegate headerView];
+        UIView* headerView =
+            [self.delegate headerViewForOverscrollActionsController:self];
         DCHECK(headerView);
         if (previousOverscrollState == OverscrollState::NO_PULL_STARTED) {
-          UIView* view = [self.delegate toolbarSnapshotView];
+          UIView* view = [self.delegate
+              toolbarSnapshotViewForOverscrollActionsController:self];
           [self.overscrollActionView addSnapshotView:view];
           [[NSNotificationCenter defaultCenter]
               postNotificationName:kOverscrollActionsWillStart
@@ -845,7 +847,7 @@
 }
 
 - (CGFloat)initialHeaderInset {
-  return [self.delegate overscrollActionsControllerHeaderInset:self];
+  return [self.delegate headerInsetForOverscrollActionsController:self];
 }
 
 - (BOOL)isDisablingFullscreen {
@@ -855,15 +857,18 @@
 - (void)setDisablingFullscreen:(BOOL)disablingFullscreen {
   if (self.disablingFullscreen == disablingFullscreen)
     return;
+
   _fullscreenDisabler = nullptr;
-  // Don't try to disable fullscreen if the BrowserState has been detached, such
-  // as when an overscroll that begins just as the last incognito tab is closed.
-  if (!disablingFullscreen || !self.browserState)
+  if (!disablingFullscreen)
     return;
 
-  // TODO: (crbug.com/1063521): Retrieve FullscreenController using Browser.
+  // Ask the delegate for a fullscreen controller. It may return nothing if
+  // (for example) the UI is in the middle of teardown.
   FullscreenController* fullscreenController =
-      FullscreenController::FromBrowserState(self.browserState);
+      [self.delegate fullscreenControllerForOverscrollActionsController:self];
+  if (!fullscreenController)
+    return;
+
   // Disabling fullscreen will show the toolbars, which may potentially produce
   // a |-scrollViewDidScroll| event if the browser viewport insets need to be
   // updated.  |_ignoreScrollForDisabledFullscreen| is set to YES while the
diff --git a/ios/chrome/browser/ui/sad_tab/BUILD.gn b/ios/chrome/browser/ui/sad_tab/BUILD.gn
index 73661c2..d52ad24 100644
--- a/ios/chrome/browser/ui/sad_tab/BUILD.gn
+++ b/ios/chrome/browser/ui/sad_tab/BUILD.gn
@@ -41,6 +41,7 @@
     ":sad_tab",
     "//components/ui_metrics",
     "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/main:public",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/fullscreen:coordinators",
@@ -63,6 +64,7 @@
   deps = [
     "//components/strings:components_strings_grit",
     "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/main:test_support",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/sad_tab",
     "//ios/chrome/browser/ui/sad_tab:coordinator",
diff --git a/ios/chrome/browser/ui/sad_tab/sad_tab_coordinator.h b/ios/chrome/browser/ui/sad_tab/sad_tab_coordinator.h
index 45e4d009..02ecddf 100644
--- a/ios/chrome/browser/ui/sad_tab/sad_tab_coordinator.h
+++ b/ios/chrome/browser/ui/sad_tab/sad_tab_coordinator.h
@@ -8,14 +8,18 @@
 #import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
 #import "ios/chrome/browser/web/sad_tab_tab_helper_delegate.h"
 
-@protocol ApplicationCommands;
-@protocol BrowserCommands;
 @protocol OverscrollActionsControllerDelegate;
 
 // Coordinator that displays a SadTab view.
 @interface SadTabCoordinator : ChromeCoordinator<SadTabTabHelperDelegate>
 
-@property(nonatomic, weak) id<ApplicationCommands, BrowserCommands> dispatcher;
+// Unavailable, use -initWithBaseViewController:browser:.
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+    NS_UNAVAILABLE;
+// Unavailable, use -initWithBaseViewController:browser:.
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                              browserState:(ChromeBrowserState*)browserState
+    NS_UNAVAILABLE;
 
 // Required to support Overscroll Actions UI, which is displayed when Sad Tab is
 // pulled down.
diff --git a/ios/chrome/browser/ui/sad_tab/sad_tab_coordinator.mm b/ios/chrome/browser/ui/sad_tab/sad_tab_coordinator.mm
index 31e64ab..dab857c 100644
--- a/ios/chrome/browser/ui/sad_tab/sad_tab_coordinator.mm
+++ b/ios/chrome/browser/ui/sad_tab/sad_tab_coordinator.mm
@@ -7,8 +7,10 @@
 #include "base/metrics/histogram_macros.h"
 #include "components/ui_metrics/sadtab_metrics_types.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
+#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
 #import "ios/chrome/browser/ui/fullscreen/chrome_coordinator+fullscreen_disabling.h"
 #import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h"
@@ -50,7 +52,8 @@
   _viewController = [[SadTabViewController alloc] init];
   _viewController.delegate = self;
   _viewController.overscrollDelegate = self.overscrollDelegate;
-  _viewController.offTheRecord = self.browserState->IsOffTheRecord();
+  _viewController.offTheRecord =
+      self.browser->GetBrowserState()->IsOffTheRecord();
   _viewController.repeatedFailure = self.repeatedFailure;
 
   [self.baseViewController addChildViewController:_viewController];
@@ -85,17 +88,26 @@
 
 - (void)sadTabViewControllerShowReportAnIssue:
     (SadTabViewController*)sadTabViewController {
-  [self.dispatcher showReportAnIssueFromViewController:self.baseViewController];
+  // TODO(crbug.com/1045047): Use HandlerForProtocol after commands protocol
+  // clean up.
+  [static_cast<id<ApplicationCommands>>(self.browser->GetCommandDispatcher())
+      showReportAnIssueFromViewController:self.baseViewController];
 }
 
 - (void)sadTabViewController:(SadTabViewController*)sadTabViewController
     showSuggestionsPageWithURL:(const GURL&)URL {
   OpenNewTabCommand* command = [OpenNewTabCommand commandWithURLFromChrome:URL];
-  [self.dispatcher openURLInNewTab:command];
+  // TODO(crbug.com/1045047): Use HandlerForProtocol after commands protocol
+  // clean up.
+  [static_cast<id<ApplicationCommands>>(self.browser->GetCommandDispatcher())
+      openURLInNewTab:command];
 }
 
 - (void)sadTabViewControllerReload:(SadTabViewController*)sadTabViewController {
-  [self.dispatcher reload];
+  // TODO(crbug.com/1045047): Use HandlerForProtocol after commands protocol
+  // clean up.
+  [static_cast<id<BrowserCommands>>(self.browser->GetCommandDispatcher())
+      reload];
 }
 
 #pragma mark - SadTabTabHelperDelegate
diff --git a/ios/chrome/browser/ui/sad_tab/sad_tab_coordinator_unittest.mm b/ios/chrome/browser/ui/sad_tab/sad_tab_coordinator_unittest.mm
index bf41422..dba2311 100644
--- a/ios/chrome/browser/ui/sad_tab/sad_tab_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/sad_tab/sad_tab_coordinator_unittest.mm
@@ -5,8 +5,10 @@
 #import "ios/chrome/browser/ui/sad_tab/sad_tab_coordinator.h"
 
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#import "ios/chrome/browser/main/test_browser.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
+#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/sad_tab/sad_tab_view_controller.h"
 #import "ios/chrome/browser/ui/util/named_guide.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
@@ -26,21 +28,21 @@
  protected:
   SadTabCoordinatorTest()
       : base_view_controller_([[UIViewController alloc] init]),
-        browser_state_(TestChromeBrowserState::Builder().Build()) {
+        browser_(std::make_unique<TestBrowser>()) {
     UILayoutGuide* guide = [[NamedGuide alloc] initWithName:kContentAreaGuide];
     [base_view_controller_.view addLayoutGuide:guide];
     AddSameConstraints(guide, base_view_controller_.view);
   }
   web::WebTaskEnvironment task_environment_;
   UIViewController* base_view_controller_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  std::unique_ptr<Browser> browser_;
 };
 
 // Tests starting coordinator.
 TEST_F(SadTabCoordinatorTest, Start) {
   SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
       initWithBaseViewController:base_view_controller_
-                    browserState:browser_state_.get()];
+                         browser:browser_.get()];
 
   [coordinator start];
 
@@ -60,7 +62,7 @@
 TEST_F(SadTabCoordinatorTest, Stop) {
   SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
       initWithBaseViewController:base_view_controller_
-                    browserState:browser_state_.get()];
+                         browser:browser_.get()];
 
   [coordinator start];
   ASSERT_EQ(1U, base_view_controller_.childViewControllers.count);
@@ -73,7 +75,7 @@
 TEST_F(SadTabCoordinatorTest, Dismiss) {
   SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
       initWithBaseViewController:base_view_controller_
-                    browserState:browser_state_.get()];
+                         browser:browser_.get()];
 
   [coordinator start];
   ASSERT_EQ(1U, base_view_controller_.childViewControllers.count);
@@ -87,7 +89,7 @@
 TEST_F(SadTabCoordinatorTest, Hide) {
   SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
       initWithBaseViewController:base_view_controller_
-                    browserState:browser_state_.get()];
+                         browser:browser_.get()];
 
   [coordinator start];
   ASSERT_EQ(1U, base_view_controller_.childViewControllers.count);
@@ -103,7 +105,7 @@
   web_state.WasShown();
   SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
       initWithBaseViewController:base_view_controller_
-                    browserState:browser_state_.get()];
+                         browser:browser_.get()];
 
   [coordinator sadTabTabHelper:nullptr
       presentSadTabForWebState:&web_state
@@ -125,11 +127,11 @@
 TEST_F(SadTabCoordinatorTest, FirstFailureInIncognito) {
   web::TestWebState web_state;
   web_state.WasShown();
-  ChromeBrowserState* otr_browser_state =
-      browser_state_->GetOffTheRecordChromeBrowserState();
+  std::unique_ptr<Browser> otr_browser = std::make_unique<TestBrowser>(
+      browser_->GetBrowserState()->GetOffTheRecordChromeBrowserState());
   SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
       initWithBaseViewController:base_view_controller_
-                    browserState:otr_browser_state];
+                         browser:otr_browser.get()];
 
   [coordinator sadTabTabHelper:nullptr
       presentSadTabForWebState:&web_state
@@ -149,11 +151,11 @@
 
 // Tests SadTabViewController state for the repeated failure in incognito mode.
 TEST_F(SadTabCoordinatorTest, ShowFirstFailureInIncognito) {
-  ChromeBrowserState* otr_browser_state =
-      browser_state_->GetOffTheRecordChromeBrowserState();
+  std::unique_ptr<Browser> otr_browser = std::make_unique<TestBrowser>(
+      browser_->GetBrowserState()->GetOffTheRecordChromeBrowserState());
   SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
       initWithBaseViewController:base_view_controller_
-                    browserState:otr_browser_state];
+                         browser:otr_browser.get()];
 
   [coordinator sadTabTabHelper:nullptr didShowForRepeatedFailure:YES];
 
@@ -175,9 +177,14 @@
   web_state.WasShown();
   SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
       initWithBaseViewController:base_view_controller_
-                    browserState:browser_state_.get()];
-  coordinator.dispatcher = OCMStrictProtocolMock(@protocol(BrowserCommands));
-  OCMExpect([coordinator.dispatcher reload]);
+                         browser:browser_.get()];
+
+  id mock_browser_commands_handler_ =
+      OCMStrictProtocolMock(@protocol(BrowserCommands));
+  [browser_->GetCommandDispatcher()
+      startDispatchingToTarget:mock_browser_commands_handler_
+                   forProtocol:@protocol(BrowserCommands)];
+  OCMExpect([mock_browser_commands_handler_ reload]);
 
   [coordinator sadTabTabHelper:nullptr
       presentSadTabForWebState:&web_state
@@ -192,7 +199,7 @@
   // Verify dispatcher's message.
   [view_controller.actionButton
       sendActionsForControlEvents:UIControlEventTouchUpInside];
-  EXPECT_OCMOCK_VERIFY(coordinator.dispatcher);
+  EXPECT_OCMOCK_VERIFY(mock_browser_commands_handler_);
   [coordinator stop];
 }
 
@@ -202,10 +209,14 @@
   web_state.WasShown();
   SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
       initWithBaseViewController:base_view_controller_
-                    browserState:browser_state_.get()];
-  coordinator.dispatcher =
+                         browser:browser_.get()];
+
+  id mock_application_commands_handler_ =
       OCMStrictProtocolMock(@protocol(ApplicationCommands));
-  OCMExpect([coordinator.dispatcher
+  [browser_->GetCommandDispatcher()
+      startDispatchingToTarget:mock_application_commands_handler_
+                   forProtocol:@protocol(ApplicationCommands)];
+  OCMExpect([mock_application_commands_handler_
       showReportAnIssueFromViewController:base_view_controller_]);
 
   [coordinator sadTabTabHelper:nullptr
@@ -221,7 +232,7 @@
   // Verify dispatcher's message.
   [view_controller.actionButton
       sendActionsForControlEvents:UIControlEventTouchUpInside];
-  EXPECT_OCMOCK_VERIFY(coordinator.dispatcher);
+  EXPECT_OCMOCK_VERIFY(mock_application_commands_handler_);
   [coordinator stop];
 }
 
@@ -230,7 +241,7 @@
   web::TestWebState web_state;
   SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
       initWithBaseViewController:base_view_controller_
-                    browserState:browser_state_.get()];
+                         browser:browser_.get()];
 
   [coordinator sadTabTabHelper:nullptr
       presentSadTabForWebState:&web_state
diff --git a/ios/chrome/browser/ui/ui_feature_flags.cc b/ios/chrome/browser/ui/ui_feature_flags.cc
index a9c0a065..47a744a16 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.cc
+++ b/ios/chrome/browser/ui/ui_feature_flags.cc
@@ -36,3 +36,6 @@
 
 const base::Feature kFirstResponderSendAction{
     "FirstResponderSendAction", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kNewSigninArchitecture{"NewSigninArchitecture",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ios/chrome/browser/ui/ui_feature_flags.h b/ios/chrome/browser/ui/ui_feature_flags.h
index d5f742c..e70ef16 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.h
+++ b/ios/chrome/browser/ui/ui_feature_flags.h
@@ -41,4 +41,7 @@
 // Verify if the crash in https://crbug.com/816427 is fixed on iOS 12.
 extern const base::Feature kFirstResponderSendAction;
 
+// Feature flag to enable the new sign-in architecture.
+extern const base::Feature kNewSigninArchitecture;
+
 #endif  // IOS_CHROME_BROWSER_UI_UI_FEATURE_FLAGS_H_
diff --git a/ios/chrome/test/fakes/fake_overscroll_actions_controller_delegate.mm b/ios/chrome/test/fakes/fake_overscroll_actions_controller_delegate.mm
index d8e299c..9f457359 100644
--- a/ios/chrome/test/fakes/fake_overscroll_actions_controller_delegate.mm
+++ b/ios/chrome/test/fakes/fake_overscroll_actions_controller_delegate.mm
@@ -23,25 +23,34 @@
   _selectedAction = action;
 }
 
-- (BOOL)shouldAllowOverscrollActions {
+- (BOOL)shouldAllowOverscrollActionsForOverscrollActionsController:
+    (OverscrollActionsController*)controller {
   return YES;
 }
 
-- (UIView*)toolbarSnapshotView {
+- (UIView*)toolbarSnapshotViewForOverscrollActionsController:
+    (OverscrollActionsController*)controller {
   return nil;
 }
 
-- (UIView*)headerView {
+- (UIView*)headerViewForOverscrollActionsController:
+    (OverscrollActionsController*)controller {
   return _headerView;
 }
 
-- (CGFloat)overscrollActionsControllerHeaderInset:
+- (CGFloat)headerInsetForOverscrollActionsController:
     (OverscrollActionsController*)controller {
   return 0;
 }
 
-- (CGFloat)overscrollHeaderHeight {
+- (CGFloat)headerHeightForOverscrollActionsController:
+    (OverscrollActionsController*)controller {
   return 0;
 }
 
+- (FullscreenController*)fullscreenControllerForOverscrollActionsController:
+    (OverscrollActionsController*)controller {
+  return nullptr;
+}
+
 @end
diff --git a/ios/public/provider/chrome/browser/build_config.gni b/ios/public/provider/chrome/browser/build_config.gni
index 20c09d1..ea1e971 100644
--- a/ios/public/provider/chrome/browser/build_config.gni
+++ b/ios/public/provider/chrome/browser/build_config.gni
@@ -27,11 +27,6 @@
   # Overridden when using the Google-internal repository to build Chrome on iOS.
   ios_provider_target = "//ios/chrome/browser/providers:provider_factory"
 
-  # This defines the build target to include a valid GoogleService-Info.plist
-  # file which is copied to the application bundle. This is used by Firebase
-  # SDK (which comes from the internal framework).
-  ios_firebase_resources_target = ""
-
   # This defines targets to include resources needed by the providers which are
   # copied to the application bundle.
   ios_providers_resources_targets = []
diff --git a/ios/web_view/internal/autofill/web_view_autofill_client_ios.h b/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
index b49c3b19..772215b 100644
--- a/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
+++ b/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
@@ -106,7 +106,7 @@
       const std::vector<base::string16>& values,
       const std::vector<base::string16>& labels) override;
   base::span<const autofill::Suggestion> GetPopupSuggestions() const override;
-  void PinPopupViewUntilUpdate() override;
+  void PinPopupView() override;
   void UpdatePopup(const std::vector<autofill::Suggestion>& suggestions,
                    PopupType popup_type) override;
   void HideAutofillPopup(PopupHidingReason reason) override;
diff --git a/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm b/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
index 6b89f2b..3f26a66 100644
--- a/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
+++ b/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
@@ -241,7 +241,7 @@
   return base::span<const autofill::Suggestion>();
 }
 
-void WebViewAutofillClientIOS::PinPopupViewUntilUpdate() {
+void WebViewAutofillClientIOS::PinPopupView() {
   NOTIMPLEMENTED();
 }
 
diff --git a/media/renderers/win/media_foundation_protection_manager.cc b/media/renderers/win/media_foundation_protection_manager.cc
index 93e72f65..711e59b 100644
--- a/media/renderers/win/media_foundation_protection_manager.cc
+++ b/media/renderers/win/media_foundation_protection_manager.cc
@@ -24,9 +24,11 @@
 HRESULT MediaFoundationProtectionManager::RuntimeClassInitialize() {
   DVLOG(1) << __func__ << ": this=" << this;
 
+  if (!base::win::ScopedHString::ResolveCoreWinRTStringDelayload())
+    return E_FAIL;
+
   // Init an empty |property_set_| as MFMediaEngine could access it via
   // |get_Properties| before we populate it within SetPMPServer.
-  base::win::ScopedHString::ResolveCoreWinRTStringDelayload();
   base::win::ScopedHString property_set_id = base::win::ScopedHString::Create(
       RuntimeClass_Windows_Foundation_Collections_PropertySet);
   RETURN_IF_FAILED(
@@ -53,11 +55,11 @@
   ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable*>>
       property_map;
   RETURN_IF_FAILED(property_set_.As(&property_map));
-  boolean replaced = false;
-  base::win::ScopedHString::ResolveCoreWinRTStringDelayload();
+
   // MFMediaEngine uses |pmp_server_key| to get the Protected Media Path (PMP)
   // server used for playing protected content. This is not currently documented
   // in MSDN.
+  boolean replaced = false;
   base::win::ScopedHString pmp_server_key = base::win::ScopedHString::Create(
       L"Windows.Media.Protection.MediaProtectionPMPServer");
   RETURN_IF_FAILED(
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 8ca0ca3..d64896d 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -913,6 +913,8 @@
       "quic/quic_chromium_packet_reader.h",
       "quic/quic_chromium_packet_writer.cc",
       "quic/quic_chromium_packet_writer.h",
+      "quic/quic_client_session_cache.cc",
+      "quic/quic_client_session_cache.h",
       "quic/quic_clock_skew_detector.cc",
       "quic/quic_clock_skew_detector.h",
       "quic/quic_connection_logger.cc",
@@ -4331,6 +4333,7 @@
     "quic/quic_chromium_client_session_test.cc",
     "quic/quic_chromium_client_stream_test.cc",
     "quic/quic_chromium_connection_helper_test.cc",
+    "quic/quic_client_session_cache_unittests.cc",
     "quic/quic_clock_skew_detector_test.cc",
     "quic/quic_connectivity_probing_manager_test.cc",
     "quic/quic_end_to_end_unittest.cc",
diff --git a/net/quic/quic_client_session_cache.cc b/net/quic/quic_client_session_cache.cc
new file mode 100644
index 0000000..fc47595
--- /dev/null
+++ b/net/quic/quic_client_session_cache.cc
@@ -0,0 +1,94 @@
+// Copyright (c) 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 "net/quic/quic_client_session_cache.h"
+
+#include "base/time/clock.h"
+#include "base/time/default_clock.h"
+
+namespace net {
+
+namespace {
+
+const size_t kDefaultMaxEntries = 1024;
+// Check whether the SSL session inside |state| is expired at |now|.
+bool IsExpired(quic::QuicResumptionState* state, time_t now) {
+  if (now < 0)
+    return true;
+
+  SSL_SESSION* session = state->tls_session.get();
+  uint64_t now_u64 = static_cast<uint64_t>(now);
+
+  // now_u64 may be slightly behind because of differences in how
+  // time is calculated at this layer versus BoringSSL.
+  // Add a second of wiggle room to account for this.
+  return now_u64 < SSL_SESSION_get_time(session) - 1 ||
+         now_u64 >=
+             SSL_SESSION_get_time(session) + SSL_SESSION_get_timeout(session);
+}
+
+}  // namespace
+
+QuicClientSessionCache::QuicClientSessionCache()
+    : QuicClientSessionCache(kDefaultMaxEntries) {}
+
+QuicClientSessionCache::QuicClientSessionCache(size_t max_entries)
+    : clock_(base::DefaultClock::GetInstance()), cache_(max_entries) {
+  memory_pressure_listener_.reset(
+      new base::MemoryPressureListener(base::BindRepeating(
+          &QuicClientSessionCache::OnMemoryPressure, base::Unretained(this))));
+}
+
+QuicClientSessionCache::~QuicClientSessionCache() {
+  Flush();
+}
+
+void QuicClientSessionCache::Insert(
+    const quic::QuicServerId& server_id,
+    std::unique_ptr<quic::QuicResumptionState> state) {
+  cache_.Put(server_id, std::move(state));
+}
+
+std::unique_ptr<quic::QuicResumptionState> QuicClientSessionCache::Lookup(
+    const quic::QuicServerId& server_id,
+    const SSL_CTX* /*ctx*/) {
+  auto iter = cache_.Get(server_id);
+  if (iter == cache_.end())
+    return nullptr;
+
+  time_t now = clock_->Now().ToTimeT();
+  std::unique_ptr<quic::QuicResumptionState> state = std::move(iter->second);
+  cache_.Erase(iter);
+  if (IsExpired(state.get(), now))
+    state = nullptr;
+  return state;
+}
+
+void QuicClientSessionCache::FlushExpiredStates() {
+  time_t now = clock_->Now().ToTimeT();
+  auto iter = cache_.begin();
+  while (iter != cache_.end()) {
+    if (IsExpired(iter->second.get(), now)) {
+      iter = cache_.Erase(iter);
+    } else {
+      ++iter;
+    }
+  }
+}
+
+void QuicClientSessionCache::OnMemoryPressure(
+    base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
+  switch (memory_pressure_level) {
+    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
+      break;
+    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
+      FlushExpiredStates();
+      break;
+    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
+      Flush();
+      break;
+  }
+}
+
+}  // namespace net
diff --git a/net/quic/quic_client_session_cache.h b/net/quic/quic_client_session_cache.h
new file mode 100644
index 0000000..c102eff
--- /dev/null
+++ b/net/quic/quic_client_session_cache.h
@@ -0,0 +1,60 @@
+// Copyright (c) 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 NET_QUIC_QUIC_CLIENT_SESSION_CACHE_H_
+#define NET_QUIC_QUIC_CLIENT_SESSION_CACHE_H_
+
+#include <stddef.h>
+#include <time.h>
+
+#include <memory>
+#include <string>
+
+#include "base/bind.h"
+#include "base/containers/mru_cache.h"
+#include "base/memory/memory_pressure_monitor.h"
+#include "base/time/time.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h"
+#include "third_party/boringssl/src/include/openssl/base.h"
+
+namespace base {
+class Clock;
+}
+
+namespace net {
+
+class NET_EXPORT_PRIVATE QuicClientSessionCache : public quic::SessionCache {
+ public:
+  QuicClientSessionCache();
+  explicit QuicClientSessionCache(size_t max_entries);
+  ~QuicClientSessionCache() override;
+
+  void Insert(const quic::QuicServerId& server_id,
+              std::unique_ptr<quic::QuicResumptionState> state) override;
+
+  std::unique_ptr<quic::QuicResumptionState> Lookup(
+      const quic::QuicServerId& server_id,
+      const SSL_CTX* ctx) override;
+
+  void SetClockForTesting(base::Clock* clock) { clock_ = clock; }
+
+  size_t size() const { return cache_.size(); }
+
+  void Flush() { cache_.Clear(); }
+
+  void OnMemoryPressure(
+      base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
+
+ private:
+  void FlushExpiredStates();
+
+  base::Clock* clock_;
+  base::MRUCache<quic::QuicServerId, std::unique_ptr<quic::QuicResumptionState>>
+      cache_;
+  std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
+};
+
+}  // namespace net
+
+#endif  // NET_QUIC_QUIC_CLIENT_SESSION_CACHE_H_
diff --git a/net/quic/quic_client_session_cache_unittests.cc b/net/quic/quic_client_session_cache_unittests.cc
new file mode 100644
index 0000000..d9b7312
--- /dev/null
+++ b/net/quic/quic_client_session_cache_unittests.cc
@@ -0,0 +1,205 @@
+// 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 "net/quic/quic_client_session_cache.h"
+
+#include "base/run_loop.h"
+#include "base/test/simple_test_clock.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/boringssl/src/include/openssl/ssl.h"
+
+namespace net {
+
+namespace {
+
+std::unique_ptr<base::SimpleTestClock> MakeTestClock() {
+  std::unique_ptr<base::SimpleTestClock> clock =
+      std::make_unique<base::SimpleTestClock>();
+  // SimpleTestClock starts at the null base::Time which converts to and from
+  // time_t confusingly.
+  clock->SetNow(base::Time::FromTimeT(1000000000));
+  return clock;
+}
+
+class QuicClientSessionCacheTest : public testing::Test {
+ public:
+  QuicClientSessionCacheTest() : ssl_ctx_(SSL_CTX_new(TLS_method())) {}
+
+ protected:
+  bssl::UniquePtr<SSL_SESSION> NewSSLSession() {
+    SSL_SESSION* session = SSL_SESSION_new(ssl_ctx_.get());
+    if (!SSL_SESSION_set_protocol_version(session, TLS1_3_VERSION))
+      return nullptr;
+    return bssl::UniquePtr<SSL_SESSION>(session);
+  }
+
+  bssl::UniquePtr<SSL_SESSION> MakeTestSession(base::Time now,
+                                               base::TimeDelta timeout) {
+    bssl::UniquePtr<SSL_SESSION> session = NewSSLSession();
+    SSL_SESSION_set_time(session.get(), now.ToTimeT());
+    SSL_SESSION_set_timeout(session.get(), timeout.InSeconds());
+    return session;
+  }
+
+  bssl::UniquePtr<SSL_CTX> ssl_ctx_;
+};
+
+}  // namespace
+
+// Tests that simple insertion and lookup work correctly.
+TEST_F(QuicClientSessionCacheTest, Basic) {
+  QuicClientSessionCache cache;
+
+  std::unique_ptr<quic::QuicResumptionState> state1 =
+      std::make_unique<quic::QuicResumptionState>();
+  state1->application_state.push_back('a');
+  state1->tls_session = NewSSLSession();
+  quic::QuicServerId id1("a.com", 443);
+  std::unique_ptr<quic::QuicResumptionState> state2 =
+      std::make_unique<quic::QuicResumptionState>();
+  state2->application_state.push_back('b');
+  state2->tls_session = NewSSLSession();
+  quic::QuicServerId id2("b.com", 443);
+
+  EXPECT_EQ(nullptr, cache.Lookup(id1, ssl_ctx_.get()));
+  EXPECT_EQ(nullptr, cache.Lookup(id2, ssl_ctx_.get()));
+  EXPECT_EQ(0u, cache.size());
+
+  cache.Insert(id1, std::move(state1));
+  EXPECT_EQ(1u, cache.size());
+  EXPECT_EQ('a', cache.Lookup(id1, ssl_ctx_.get())->application_state.front());
+  EXPECT_EQ(nullptr, cache.Lookup(id2, ssl_ctx_.get()));
+
+  std::unique_ptr<quic::QuicResumptionState> state3 =
+      std::make_unique<quic::QuicResumptionState>();
+  state3->application_state.push_back('c');
+  state3->tls_session = NewSSLSession();
+  quic::QuicServerId id3("c.com", 443);
+  cache.Insert(id3, std::move(state3));
+  cache.Insert(id2, std::move(state2));
+  EXPECT_EQ(2u, cache.size());
+  EXPECT_EQ('b', cache.Lookup(id2, ssl_ctx_.get())->application_state.front());
+  EXPECT_EQ('c', cache.Lookup(id3, ssl_ctx_.get())->application_state.front());
+
+  // Verify that the cache is cleared after Lookups.
+  EXPECT_EQ(nullptr, cache.Lookup(id1, ssl_ctx_.get()));
+  EXPECT_EQ(nullptr, cache.Lookup(id2, ssl_ctx_.get()));
+  EXPECT_EQ(nullptr, cache.Lookup(id3, ssl_ctx_.get()));
+  EXPECT_EQ(0u, cache.size());
+}
+
+// When the size limit is exceeded, the oldest entry should be erased.
+TEST_F(QuicClientSessionCacheTest, SizeLimit) {
+  QuicClientSessionCache cache(2);
+
+  std::unique_ptr<quic::QuicResumptionState> state1 =
+      std::make_unique<quic::QuicResumptionState>();
+  state1->application_state.push_back('a');
+  state1->tls_session = NewSSLSession();
+  quic::QuicServerId id1("a.com", 443);
+
+  std::unique_ptr<quic::QuicResumptionState> state2 =
+      std::make_unique<quic::QuicResumptionState>();
+  state2->application_state.push_back('b');
+  state2->tls_session = NewSSLSession();
+  quic::QuicServerId id2("b.com", 443);
+
+  std::unique_ptr<quic::QuicResumptionState> state3 =
+      std::make_unique<quic::QuicResumptionState>();
+  state3->application_state.push_back('c');
+  state3->tls_session = NewSSLSession();
+  quic::QuicServerId id3("c.com", 443);
+
+  cache.Insert(id1, std::move(state1));
+  cache.Insert(id2, std::move(state2));
+  cache.Insert(id3, std::move(state3));
+
+  EXPECT_EQ(2u, cache.size());
+  EXPECT_EQ('b', cache.Lookup(id2, ssl_ctx_.get())->application_state.front());
+  EXPECT_EQ('c', cache.Lookup(id3, ssl_ctx_.get())->application_state.front());
+  EXPECT_EQ(nullptr, cache.Lookup(id1, ssl_ctx_.get()));
+}
+
+// Expired session isn't considered valid and nullptr will be returned upon
+// Lookup.
+TEST_F(QuicClientSessionCacheTest, Expiration) {
+  const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(1000);
+  QuicClientSessionCache cache;
+  std::unique_ptr<base::SimpleTestClock> clock = MakeTestClock();
+  cache.SetClockForTesting(clock.get());
+
+  std::unique_ptr<quic::QuicResumptionState> state1 =
+      std::make_unique<quic::QuicResumptionState>();
+  state1->tls_session = MakeTestSession(clock->Now(), kTimeout);
+  quic::QuicServerId id1("a.com", 443);
+
+  std::unique_ptr<quic::QuicResumptionState> state2 =
+      std::make_unique<quic::QuicResumptionState>();
+  state2->tls_session = MakeTestSession(clock->Now(), 3 * kTimeout);
+  ;
+  state2->application_state.push_back('b');
+  quic::QuicServerId id2("b.com", 443);
+
+  cache.Insert(id1, std::move(state1));
+  cache.Insert(id2, std::move(state2));
+
+  EXPECT_EQ(2u, cache.size());
+  // Expire the session.
+  clock->Advance(kTimeout * 2);
+  // The entry has not been removed yet.
+  EXPECT_EQ(2u, cache.size());
+
+  EXPECT_EQ(nullptr, cache.Lookup(id1, ssl_ctx_.get()));
+  EXPECT_EQ(1u, cache.size());
+  EXPECT_EQ('b', cache.Lookup(id2, ssl_ctx_.get())->application_state.front());
+  EXPECT_EQ(0u, cache.size());
+}
+
+TEST_F(QuicClientSessionCacheTest, FlushOnMemoryNotifications) {
+  base::test::TaskEnvironment task_environment;
+  const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(1000);
+  QuicClientSessionCache cache;
+  std::unique_ptr<base::SimpleTestClock> clock = MakeTestClock();
+  cache.SetClockForTesting(clock.get());
+
+  std::unique_ptr<quic::QuicResumptionState> state1 =
+      std::make_unique<quic::QuicResumptionState>();
+  state1->tls_session = MakeTestSession(clock->Now(), kTimeout);
+  quic::QuicServerId id1("a.com", 443);
+
+  std::unique_ptr<quic::QuicResumptionState> state2 =
+      std::make_unique<quic::QuicResumptionState>();
+  state2->tls_session = MakeTestSession(clock->Now(), 3 * kTimeout);
+  ;
+  state2->application_state.push_back('b');
+  quic::QuicServerId id2("b.com", 443);
+
+  cache.Insert(id1, std::move(state1));
+  cache.Insert(id2, std::move(state2));
+
+  EXPECT_EQ(2u, cache.size());
+  // Expire the session.
+  clock->Advance(kTimeout * 2);
+  // The entry has not been removed yet.
+  EXPECT_EQ(2u, cache.size());
+
+  // Fire a notification that will flush expired sessions.
+  base::MemoryPressureListener::NotifyMemoryPressure(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
+  base::RunLoop().RunUntilIdle();
+
+  // session1 is expired and should be flushed.
+  EXPECT_EQ(nullptr, cache.Lookup(id1, ssl_ctx_.get()));
+  EXPECT_EQ(1u, cache.size());
+
+  // Fire notification that will flush everything.
+  base::MemoryPressureListener::NotifyMemoryPressure(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0u, cache.size());
+}
+
+}  // namespace net
\ No newline at end of file
diff --git a/remoting/base/breakpad_win.cc b/remoting/base/breakpad_win.cc
index e68f332..805a3bae 100644
--- a/remoting/base/breakpad_win.cc
+++ b/remoting/base/breakpad_win.cc
@@ -4,7 +4,7 @@
 
 // This module contains the necessary code to register the Breakpad exception
 // handler. This implementation is based on Chrome crash reporting code. See:
-//   - src/components/crash/content/app/breakpad_win.cc
+//   - src/components/crash/core/app/breakpad_win.cc
 //   - src/chrome/installer/setup/setup_main.cc
 
 #include "remoting/base/breakpad.h"
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 9634bac..95d845e 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2300,6 +2300,21 @@
             ]
         }
     ],
+    "FeedSendFeedback": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "InterestFeedFeedback"
+                    ]
+                }
+            ]
+        }
+    ],
     "FilterAdsOnAbusiveSites": [
         {
             "platforms": [
@@ -2933,21 +2948,6 @@
             ]
         }
     ],
-    "InterestFeedFeedback": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "InterestFeedFeedback"
-                    ]
-                }
-            ]
-        }
-    ],
     "IsolatePasswordSites": [
         {
             "platforms": [
@@ -3648,7 +3648,6 @@
                         "NtpRealboxMatchOmniboxTheme",
                         "OmniboxDisplayTitleForCurrentUrl",
                         "OmniboxDocumentProvider",
-                        "OmniboxLocalEntitySuggestions",
                         "OmniboxMaxURLMatches",
                         "OmniboxOnFocusSuggestions",
                         "OmniboxRemoveSuggestionsFromClipboard",
@@ -3692,6 +3691,25 @@
             ]
         }
     ],
+    "OmniboxLocalEntities": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows",
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "DesktopAndAndroidExperiments",
+                    "enable_features": [
+                        "OmniboxLocalEntitySuggestions"
+                    ]
+                }
+            ]
+        }
+    ],
     "OmniboxOnDeviceHeadSuggest": [
         {
             "platforms": [
diff --git a/testing/xvfb.py b/testing/xvfb.py
index 346d0212..d8b9df5 100755
--- a/testing/xvfb.py
+++ b/testing/xvfb.py
@@ -275,11 +275,6 @@
     parent = psutil.Process(weston_proc_pid)
     if parent is None:
       break # The process is not found. Give up.
-    weston_proc_display = parent.environ().get('WAYLAND_DISPLAY')
-    # If display is set, Weston could start successfully and we can use
-    # that display for Wayland connection in Chromium.
-    if weston_proc_display is not None:
-      return weston_proc_display
 
     # Traverse through all the children processes and find the
     # "weston-desktop-shell" process that sets local to process env variables
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 052a485..a8dcfd28 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -7259,7 +7259,7 @@
       optional string method
       # If set, overrides the post data in the request.
       optional string postData
-      # If set, overrides the request headrts.
+      # If set, overrides the request headers.
       optional array of HeaderEntry headers
 
   # Continues a request supplying authChallengeResponse following authRequired event.
diff --git a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
index 6d5b3d38..207a176 100644
--- a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
+++ b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
@@ -248,6 +248,15 @@
                                : nullptr;
 }
 
+v8::Local<v8::Array> EnumerateIndexedProperties(v8::Isolate* isolate,
+                                                uint32_t length) {
+  Vector<v8::Local<v8::Value>> elements;
+  elements.ReserveCapacity(length);
+  for (uint32_t i = 0; i < length; ++i)
+    elements.UncheckedAppend(v8::Integer::New(isolate, i));
+  return v8::Array::New(isolate, elements.data(), elements.size());
+}
+
 }  // namespace bindings
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.h b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.h
index dcfb310..32d3db5 100644
--- a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.h
+++ b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.h
@@ -156,6 +156,10 @@
 CORE_EXPORT ExecutionContext* ExecutionContextFromV8Wrappable(
     const DOMParser* parser);
 
+CORE_EXPORT v8::Local<v8::Array> EnumerateIndexedProperties(
+    v8::Isolate* isolate,
+    uint32_t length);
+
 }  // namespace bindings
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/code_node_cxx.py b/third_party/blink/renderer/bindings/scripts/bind_gen/code_node_cxx.py
index d92aeb1..b52c867 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/code_node_cxx.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/code_node_cxx.py
@@ -20,7 +20,7 @@
     def __init__(self, body):
         template_format = (
             "{{\n"  #
-            "  {body}\n"  #
+            "  {body}\n"
             "}}")
 
         CompositeNode.__init__(
@@ -33,7 +33,7 @@
     def __init__(self, cond, body, likeliness):
         template_format = (
             "if ({cond}) {{\n"  #
-            "  {body}\n"  #
+            "  {body}\n"
             "}}")
 
         CompositeNode.__init__(
@@ -47,9 +47,9 @@
     def __init__(self, cond, then, then_likeliness, else_, else_likeliness):
         template_format = (
             "if ({cond}) {{\n"  #
-            "  {then}\n"  #
-            "}} else {{\n"  #
-            "  {else_}\n"  #
+            "  {then}\n"
+            "}} else {{\n"
+            "  {else_}\n"
             "}}")
 
         CompositeNode.__init__(
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_context.py b/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_context.py
index c428af96..6ffc5e5 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_context.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_context.py
@@ -58,6 +58,11 @@
             "operation_group": None,
 
             # Special member-ish definition
+            "indexed_property_getter": None,
+            "indexed_property_setter": None,
+            "named_property_getter": None,
+            "named_property_setter": None,
+            "named_property_deleter": None,
             "stringifier": None,
 
             # The names of the class being generated and its base class.
@@ -162,8 +167,18 @@
                 or self.namespace)
 
     @property
+    def does_override_idl_return_type(self):
+        # Blink implementation returns in a type different from the IDL type.
+        # Namely, IndexedPropertySetterResult, NamedPropertySetterResult, and
+        # NamedPropertyDeleterResult are returned ignoring the operation's
+        # return type.
+        return (self.indexed_property_setter or self.named_property_setter
+                or self.named_property_deleter)
+
+    @property
     def function_like(self):
-        return (self.callback_function or self.constructor or self.operation)
+        return (self.callback_function or self.constructor or self.operation
+                or self._indexed_or_named_property)
 
     @property
     def idl_definition(self):
@@ -198,14 +213,16 @@
 
     @property
     def is_return_by_argument(self):
+        if self.does_override_idl_return_type:
+            return False
         if self.return_type is None:
-            return None
+            return False
         return self.return_type.unwrap().is_union
 
     @property
     def may_throw_exception(self):
         if not self.member_like:
-            return None
+            return False
         ext_attr = self.member_like.extended_attributes.get("RaisesException")
         if not ext_attr:
             return False
@@ -216,7 +233,8 @@
     @property
     def member_like(self):
         return (self.attribute or self.constant or self.constructor
-                or self.dict_member or self.operation)
+                or self.dict_member or self.operation
+                or self._indexed_or_named_property)
 
     @property
     def property_(self):
@@ -225,20 +243,23 @@
 
         return (self.attribute or self.constant or self.constructor_group
                 or self.dict_member or self.exposed_construct
-                or self.operation_group)
+                or self.operation_group or self._indexed_or_named_property)
 
     @property
     def return_type(self):
         if self.attribute_get:
             return self.attribute.idl_type
-        if self.callback_function:
-            return self.callback_function.return_type
-        if self.constructor:
-            return self.constructor.return_type
-        if self.operation:
-            return self.operation.return_type
+        function_like = self.function_like
+        if function_like:
+            return function_like.return_type
         return None
 
+    @property
+    def _indexed_or_named_property(self):
+        return (self.indexed_property_getter or self.indexed_property_setter
+                or self.named_property_getter or self.named_property_setter
+                or self.named_property_deleter)
+
 
 CodeGenContext.init()
 
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
index 523d465..3424b6b 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
@@ -76,6 +76,17 @@
         tokens.insert(0, "set")
         name = "".join(tokens)
 
+    if cg_context.indexed_property_getter and not name:
+        name = "AnonymousIndexedGetter"
+    if cg_context.indexed_property_setter and not name:
+        name = "AnonymousIndexedSetter"
+    if cg_context.named_property_getter and not name:
+        name = "AnonymousNamedGetter"
+    if cg_context.named_property_setter and not name:
+        name = "AnonymousNamedSetter"
+    if cg_context.named_property_deleter and not name:
+        name = "AnonymousNamedDeleter"
+
     return name
 
 
@@ -151,6 +162,13 @@
 def custom_function_name(cg_context):
     assert isinstance(cg_context, CodeGenContext)
 
+    if cg_context.named_property_getter:
+        return "NamedPropertyGetterCustom"
+    if cg_context.named_property_setter:
+        return "NamedPropertySetterCustom"
+    if cg_context.named_property_deleter:
+        return "NamedPropertyDeleterCustom"
+
     if cg_context.attribute_get:
         suffix = "AttributeGetterCustom"
     elif cg_context.attribute_set:
@@ -211,6 +229,9 @@
     template_vars = {}
 
     local_vars.extend([
+        S("blink_property_name",
+          ("const AtomicString& ${blink_property_name} = "
+           "ToCoreAtomicString(${v8_property_name}.As<v8::String>());")),
         S("class_like_name", ("const char* const ${class_like_name} = "
                               "\"${class_like.identifier}\";")),
         S("current_context", ("v8::Local<v8::Context> ${current_context} = "
@@ -271,6 +292,16 @@
         _1 = "ExceptionState::kSetterContext"
     elif cg_context.constructor:
         _1 = "ExceptionState::kConstructionContext"
+    elif cg_context.indexed_property_getter:
+        _1 = "ExceptionState::kIndexedGetterContext"
+    elif cg_context.indexed_property_setter:
+        _1 = "ExceptionState::kIndexedSetterContext"
+    elif cg_context.named_property_getter:
+        _1 = "ExceptionState::kGetterContext"
+    elif cg_context.named_property_setter:
+        _1 = "ExceptionState::kSetterContext"
+    elif cg_context.named_property_deleter:
+        _1 = "ExceptionState::kDeletionContext"
     else:
         _1 = "ExceptionState::kExecutionContext"
     local_vars.append(
@@ -280,8 +311,9 @@
     pattern = "ExceptionState ${exception_state}({_1});{_2}"
     _1 = [
         "${isolate}", "${exception_state_context_type}", "${class_like_name}",
-        "${property_name}"
     ]
+    if cg_context.property_ and cg_context.property_.identifier:
+        _1.append("${property_name}")
     _2 = ""
     if cg_context.return_type and cg_context.return_type.unwrap().is_promise:
         _2 = ("\n"
@@ -445,10 +477,16 @@
     return SequenceNode(nodes)
 
 
-def _make_blink_api_call(code_node, cg_context, num_of_args=None):
+def _make_blink_api_call(code_node,
+                         cg_context,
+                         num_of_args=None,
+                         overriding_args=None):
     assert isinstance(code_node, SymbolScopeNode)
     assert isinstance(cg_context, CodeGenContext)
     assert num_of_args is None or isinstance(num_of_args, (int, long))
+    assert (overriding_args is None
+            or (isinstance(overriding_args, (list, tuple))
+                and all(isinstance(arg, str) for arg in overriding_args)))
 
     arguments = []
     ext_attrs = cg_context.member_like.extended_attributes
@@ -477,7 +515,9 @@
         if key:
             arguments.append(key)
 
-    if cg_context.attribute_get:
+    if overriding_args is not None:
+        arguments.extend(overriding_args)
+    elif cg_context.attribute_get:
         pass
     elif cg_context.attribute_set:
         arguments.append("${arg1_value}")
@@ -512,28 +552,41 @@
     return _format("{_1}({_2})", _1=func_designator, _2=", ".join(arguments))
 
 
-def bind_return_value(code_node, cg_context):
+def bind_return_value(code_node, cg_context, overriding_args=None):
     assert isinstance(code_node, SymbolScopeNode)
     assert isinstance(cg_context, CodeGenContext)
+    assert (overriding_args is None
+            or (isinstance(overriding_args, (list, tuple))
+                and all(isinstance(arg, str) for arg in overriding_args)))
 
     T = TextNode
     F = lambda *args, **kwargs: T(_format(*args, **kwargs))
 
     def create_definition(symbol_node):
         api_calls = []  # Pairs of (num_of_args, api_call_text)
-        arguments = (cg_context.function_like.arguments
-                     if cg_context.function_like else [])
-        for index, arg in enumerate(arguments):
-            if arg.is_optional and not arg.default_value:
-                api_calls.append((index,
-                                  _make_blink_api_call(code_node, cg_context,
-                                                       index)))
-        api_calls.append((None, _make_blink_api_call(code_node, cg_context)))
+        if overriding_args is None:
+            arguments = (cg_context.function_like.arguments
+                         if cg_context.function_like else [])
+            for index, arg in enumerate(arguments):
+                if arg.is_optional and not arg.default_value:
+                    api_calls.append((index,
+                                      _make_blink_api_call(
+                                          code_node, cg_context, index)))
+            api_calls.append((None, _make_blink_api_call(
+                code_node, cg_context)))
+        else:
+            api_calls.append((None,
+                              _make_blink_api_call(
+                                  code_node,
+                                  cg_context,
+                                  overriding_args=overriding_args)))
 
         nodes = []
-        is_return_type_void = (cg_context.attribute_set
-                               or cg_context.return_type.unwrap().is_void)
-        if not is_return_type_void:
+        is_return_type_void = ((not cg_context.return_type
+                                or cg_context.return_type.unwrap().is_void) and
+                               not cg_context.does_override_idl_return_type)
+        if not (is_return_type_void
+                or cg_context.does_override_idl_return_type):
             return_type = blink_type_info(cg_context.return_type).value_t
         if len(api_calls) == 1:
             _, api_call = api_calls[0]
@@ -1177,8 +1230,19 @@
         suffix = "_Setter"
     elif cg_context.exposed_construct:
         suffix = "_ConstructorGetterCallback"
+    elif cg_context.indexed_property_getter:
+        suffix = "_IndexedPropertyGetter"
+    elif cg_context.indexed_property_setter:
+        suffix = "_IndexedPropertySetter"
+    elif cg_context.named_property_getter:
+        suffix = "_NamedPropertyGetter"
+    elif cg_context.named_property_setter:
+        suffix = "_NamedPropertySetter"
+    elif cg_context.named_property_deleter:
+        suffix = "_NamedPropertyDeleter"
 
-    counter = target.extended_attributes.value_of("RuntimeCallStatsCounter")
+    counter = (target and
+               target.extended_attributes.value_of("RuntimeCallStatsCounter"))
     if counter:
         macro_name = "RUNTIME_CALL_TIMER_SCOPE"
         counter_name = "RuntimeCallStats::CounterId::k{}{}".format(
@@ -1187,7 +1251,7 @@
         macro_name = "RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT"
         counter_name = "\"Blink_{}_{}{}\"".format(
             blink_class_name(cg_context.class_like),
-            cg_context.property_.identifier, suffix)
+            target.identifier if target else "", suffix)
 
     return TextNode(
         _format(
@@ -1198,7 +1262,10 @@
 
 def make_steps_of_ce_reactions(cg_context):
     assert isinstance(cg_context, CodeGenContext)
-    assert cg_context.attribute_set or cg_context.operation
+    assert (cg_context.attribute_set or cg_context.operation
+            or cg_context.indexed_property_setter
+            or cg_context.named_property_setter
+            or cg_context.named_property_deleter)
 
     T = TextNode
 
@@ -1281,11 +1348,22 @@
 
     T = TextNode
 
-    if cg_context.attribute_set or cg_context.return_type.unwrap().is_void:
-        # Request a SymbolNode |return_value| to define itself without rendering
-        # any text.
+    if cg_context.does_override_idl_return_type:
+        return T("bindings::V8SetReturnValue(${info}, ${return_value});")
+
+    if not cg_context.return_type or cg_context.return_type.unwrap().is_void:
+        # Request a SymbolNode |return_value| to define itself without
+        # rendering any text.
         return T("<% return_value.request_symbol_definition() %>")
 
+    operation = cg_context.operation
+    if operation and (operation.is_setter or operation.is_deleter):
+        # Blink implementation returns in a type different from the IDL type.
+        # Namely, IndexedPropertySetterResult, NamedPropertySetterResult, and
+        # NamedPropertyDeleterResult are returned ignoring the operation's
+        # return type.
+        return T("bindings::V8SetReturnValue(${info}, ${return_value});")
+
     return_type = cg_context.return_type
     if return_type.is_typedef:
         if return_type.identifier in ("EventHandler",
@@ -1693,6 +1771,597 @@
     return node
 
 
+def make_indexed_property_getter_callback(cg_context, function_name):
+    assert isinstance(cg_context, CodeGenContext)
+    assert isinstance(function_name, str)
+
+    arg_decls = [
+        "uint32_t index",
+        "const v8::PropertyCallbackInfo<v8::Value>& info",
+    ]
+    return_type = "void"
+
+    func_decl = CxxFuncDeclNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        static=True)
+
+    func_def = CxxFuncDefNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        class_name=cg_context.class_name)
+    func_def.set_base_template_vars(cg_context.template_bindings())
+    body = func_def.body
+    body.add_template_var("index", "index")
+    body.add_template_var("info", "info")
+    bind_callback_local_vars(body, cg_context)
+    bind_return_value(body, cg_context, overriding_args=["${index}"])
+
+    body.extend([
+        make_runtime_call_timer_scope(cg_context),
+        EmptyNode(),
+        make_v8_set_return_value(cg_context),
+    ])
+
+    return func_decl, func_def
+
+
+def make_indexed_property_setter_callback(cg_context, function_name):
+    assert isinstance(cg_context, CodeGenContext)
+    assert isinstance(function_name, str)
+
+    if cg_context.indexed_property_setter is None:
+        return None, None
+
+    arg_decls = [
+        "uint32_t index",
+        "v8::Local<v8::Value> v8_value",
+        "const v8::PropertyCallbackInfo<v8::Value>& info",
+    ]
+    return_type = "void"
+
+    func_decl = CxxFuncDeclNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        static=True)
+
+    func_def = CxxFuncDefNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        class_name=cg_context.class_name)
+    func_def.set_base_template_vars(cg_context.template_bindings())
+    body = func_def.body
+    body.add_template_var("index", "index")
+    body.add_template_var("v8_value", "v8_value")
+    body.add_template_var("info", "info")
+    bind_callback_local_vars(body, cg_context)
+    bind_return_value(
+        body, cg_context, overriding_args=["${index}", "${blink_value}"])
+    body.register_code_symbol(
+        make_v8_to_blink_value(
+            "blink_value",
+            "${v8_value}",
+            cg_context.indexed_property_setter.arguments[1].idl_type,
+            argument_index=2))
+
+    body.extend([
+        make_runtime_call_timer_scope(cg_context),
+        EmptyNode(),
+        make_steps_of_ce_reactions(cg_context),
+        EmptyNode(),
+        make_v8_set_return_value(cg_context),
+    ])
+
+    return func_decl, func_def
+
+
+def make_indexed_property_definer_callback(cg_context, function_name):
+    assert isinstance(cg_context, CodeGenContext)
+    assert isinstance(function_name, str)
+
+    arg_decls = [
+        "uint32_t index",
+        "const v8::PropertyDescriptor& desc",
+        "const v8::PropertyCallbackInfo<v8::Value>& info",
+    ]
+    return_type = "void"
+
+    func_decl = CxxFuncDeclNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        static=True)
+
+    func_def = CxxFuncDefNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        class_name=cg_context.class_name)
+    func_def.set_base_template_vars(cg_context.template_bindings())
+    body = func_def.body
+    body.add_template_var("index", "index")
+    body.add_template_var("desc", "desc")
+    body.add_template_var("info", "info")
+    bind_callback_local_vars(body, cg_context)
+
+    body.append(
+        TextNode("""\
+// https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
+// 3.8.3. [[DefineOwnProperty]]
+// step 1.1. If the result of calling IsDataDescriptor(Desc) is false, then
+//   return false.
+if (desc.has_get() || desc.has_set()) {
+  bindings::V8SetReturnValue(${info}, nullptr);
+  if (${info}.ShouldThrowOnError()) {
+    ExceptionState exception_state(${info}.GetIsolate(),
+                                   ExceptionState::kIndexedSetterContext,
+                                   "${interface.identifier}");
+    exception_state.ThrowTypeError("Accessor properties are not allowed.");
+  }
+  return;
+}
+"""))
+
+    writable = bool(
+        cg_context.interface.indexed_and_named_properties.indexed_setter)
+    if writable:
+        body.append(
+            TextNode("""\
+// step 1.3. Invoke the indexed property setter with P and Desc.[[Value]].
+//
+// Return nothing and fall back to
+// ${class_name}::IndexedPropertySetterCallback."""))
+    else:
+        body.append(
+            TextNode("""\
+// step 1.2. If O does not implement an interface with an indexed property
+//   setter, then return false.
+bindings::V8SetReturnValue(${info}, nullptr);
+if (${info}.ShouldThrowOnError()) {
+  ExceptionState exception_state(${info}.GetIsolate(),
+                                 ExceptionState::kIndexedSetterContext,
+                                 "${interface.identifier}");
+  exception_state.ThrowTypeError("Index property setter is not supported.");
+  return;
+}"""))
+
+    return func_decl, func_def
+
+
+def make_indexed_property_descriptor_callback(cg_context, function_name):
+    assert isinstance(cg_context, CodeGenContext)
+    assert isinstance(function_name, str)
+
+    arg_decls = [
+        "uint32_t index",
+        "const v8::PropertyCallbackInfo<v8::Value>& info",
+    ]
+    return_type = "void"
+
+    func_decl = CxxFuncDeclNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        static=True)
+
+    func_def = CxxFuncDefNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        class_name=cg_context.class_name)
+    func_def.set_base_template_vars(cg_context.template_bindings())
+    body = func_def.body
+    body.add_template_var("index", "index")
+    body.add_template_var("info", "info")
+    bind_callback_local_vars(body, cg_context)
+
+    pattern = """\
+// https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
+// Steps 1.1. to 1.2. are covered here: we rely on
+// IndexedPropertyGetterCallback() to call the getter function and check
+// that |index| is a valid property index, in which case it will have set
+// info.GetReturnValue() to something other than undefined.
+${class_name}::IndexedPropertyGetterCallback(${index}, ${info});
+v8::Local<v8::Value> v8_value = ${info}.GetReturnValue().Get();
+if (v8_value->IsUndefined())
+  return;
+
+// 1.2.5. Let |desc| be a newly created Property Descriptor with no fields.
+// 1.2.6. Set desc.[[Value]] to the result of converting value to an
+//   ECMAScript value.
+// 1.2.7. If O implements an interface with an indexed property setter,
+//   then set desc.[[Writable]] to true, otherwise set it to false.
+v8::PropertyDescriptor desc(v8_value, /*writable=*/{cxx_writable});
+// 1.2.8. Set desc.[[Enumerable]] and desc.[[Configurable]] to true.
+desc.set_enumerable(true);
+desc.set_configurable(true);
+// 1.2.9. Return |desc|.
+bindings::V8SetReturnValue(${info}, desc);"""
+    writable = bool(
+        cg_context.interface.indexed_and_named_properties.indexed_setter)
+    cxx_writable = "true" if writable else "false"
+    body.append(TextNode(_format(pattern, cxx_writable=cxx_writable)))
+
+    return func_decl, func_def
+
+
+def make_indexed_property_enumerator_callback(cg_context, function_name):
+    assert isinstance(cg_context, CodeGenContext)
+    assert isinstance(function_name, str)
+
+    arg_decls = ["const v8::PropertyCallbackInfo<v8::Array>& info"]
+    return_type = "void"
+
+    func_decl = CxxFuncDeclNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        static=True)
+
+    func_def = CxxFuncDefNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        class_name=cg_context.class_name)
+    func_def.set_base_template_vars(cg_context.template_bindings())
+    body = func_def.body
+    body.add_template_var("info", "info")
+    bind_callback_local_vars(body, cg_context)
+
+    body.append(
+        TextNode("""\
+uint32_t length = ${blink_receiver}->length();
+v8::Local<v8::Array> array =
+    bindings::EnumerateIndexedProperties(${isolate}, length);
+bindings::V8SetReturnValue(${info}, array);"""))
+
+    return func_decl, func_def
+
+
+def make_named_property_getter_callback(cg_context, function_name):
+    assert isinstance(cg_context, CodeGenContext)
+    assert isinstance(function_name, str)
+
+    arg_decls = [
+        "v8::Local<v8::Name> v8_property_name",
+        "const v8::PropertyCallbackInfo<v8::Value>& info",
+    ]
+    return_type = "void"
+
+    func_decl = CxxFuncDeclNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        static=True)
+
+    func_def = CxxFuncDefNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        class_name=cg_context.class_name)
+    func_def.set_base_template_vars(cg_context.template_bindings())
+    body = func_def.body
+    body.add_template_var("v8_property_name", "v8_property_name")
+    body.add_template_var("info", "info")
+    bind_callback_local_vars(body, cg_context)
+    bind_return_value(
+        body, cg_context, overriding_args=["${blink_property_name}"])
+
+    body.extend([
+        make_runtime_call_timer_scope(cg_context),
+        EmptyNode(),
+    ])
+
+    if "Custom" in cg_context.named_property_getter.extended_attributes:
+        text = _format("${class_name}::{}(${blink_property_name}, ${info});",
+                       custom_function_name(cg_context))
+        body.append(TextNode(text))
+    else:
+        body.append(make_v8_set_return_value(cg_context))
+
+    return func_decl, func_def
+
+
+def make_named_property_setter_callback(cg_context, function_name):
+    assert isinstance(cg_context, CodeGenContext)
+    assert isinstance(function_name, str)
+
+    if cg_context.named_property_setter is None:
+        return None, None
+
+    arg_decls = [
+        "v8::Local<v8::Name> v8_property_name",
+        "v8::Local<v8::Value> v8_value",
+        "const v8::PropertyCallbackInfo<v8::Value>& info",
+    ]
+    return_type = "void"
+
+    func_decl = CxxFuncDeclNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        static=True)
+
+    func_def = CxxFuncDefNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        class_name=cg_context.class_name)
+    func_def.set_base_template_vars(cg_context.template_bindings())
+    body = func_def.body
+    body.add_template_var("v8_property_name", "v8_property_name")
+    body.add_template_var("v8_value", "v8_value")
+    body.add_template_var("info", "info")
+    bind_callback_local_vars(body, cg_context)
+    bind_return_value(
+        body,
+        cg_context,
+        overriding_args=["${blink_property_name}", "${blink_value}"])
+    body.register_code_symbol(
+        make_v8_to_blink_value(
+            "blink_value",
+            "${v8_value}",
+            cg_context.named_property_setter.arguments[1].idl_type,
+            argument_index=2))
+
+    body.extend([
+        make_runtime_call_timer_scope(cg_context),
+        EmptyNode(),
+    ])
+
+    if "Custom" in cg_context.named_property_setter.extended_attributes:
+        text = _format(
+            "${class_name}::{}"
+            "(${blink_property_name}, ${v8_value}, ${info});",
+            custom_function_name(cg_context))
+        body.append(TextNode(text))
+    else:
+        body.extend([
+            make_steps_of_ce_reactions(cg_context),
+            EmptyNode(),
+            make_v8_set_return_value(cg_context),
+        ])
+
+    return func_decl, func_def
+
+
+def make_named_property_deleter_callback(cg_context, function_name):
+    assert isinstance(cg_context, CodeGenContext)
+    assert isinstance(function_name, str)
+
+    if cg_context.named_property_deleter is None:
+        return None, None
+
+    arg_decls = [
+        "v8::Local<v8::Name> v8_property_name",
+        "const v8::PropertyCallbackInfo<v8::Boolean>& info",
+    ]
+    return_type = "void"
+
+    func_decl = CxxFuncDeclNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        static=True)
+
+    func_def = CxxFuncDefNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        class_name=cg_context.class_name)
+    func_def.set_base_template_vars(cg_context.template_bindings())
+    body = func_def.body
+    body.add_template_var("v8_property_name", "v8_property_name")
+    body.add_template_var("info", "info")
+    bind_callback_local_vars(body, cg_context)
+    bind_return_value(
+        body, cg_context, overriding_args=["${blink_property_name}"])
+
+    body.extend([
+        make_runtime_call_timer_scope(cg_context),
+        EmptyNode(),
+    ])
+
+    if "Custom" in cg_context.named_property_deleter.extended_attributes:
+        text = _format("${class_name}::{}(${blink_property_name}, ${info});",
+                       custom_function_name(cg_context))
+        body.append(TextNode(text))
+    else:
+        body.extend([
+            make_steps_of_ce_reactions(cg_context),
+            EmptyNode(),
+            make_v8_set_return_value(cg_context),
+        ])
+
+    return func_decl, func_def
+
+
+def make_named_property_definer_callback(cg_context, function_name):
+    assert isinstance(cg_context, CodeGenContext)
+    assert isinstance(function_name, str)
+
+    arg_decls = [
+        "v8::Local<v8::Name> v8_property_name",
+        "const v8::PropertyDescriptor& desc",
+        "const v8::PropertyCallbackInfo<v8::Value>& info",
+    ]
+    return_type = "void"
+
+    func_decl = CxxFuncDeclNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        static=True)
+
+    func_def = CxxFuncDefNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        class_name=cg_context.class_name)
+    func_def.set_base_template_vars(cg_context.template_bindings())
+    body = func_def.body
+    body.add_template_var("v8_property_name", "v8_property_name")
+    body.add_template_var("desc", "desc")
+    body.add_template_var("info", "info")
+    bind_callback_local_vars(body, cg_context)
+
+    body.append(
+        TextNode("""\
+// https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
+// 3.8.3. [[DefineOwnProperty]]
+// step 2.2.2.1. If the result of calling IsDataDescriptor(Desc) is false,
+//   then return false.
+if (desc.has_get() || desc.has_set()) {
+  bindings::V8SetReturnValue(${info}, nullptr);
+  if (${info}.ShouldThrowOnError()) {
+    ExceptionState exception_state(${info}.GetIsolate(),
+                                   ExceptionState::kSetterContext,
+                                   "${interface.identifier}");
+    exception_state.ThrowTypeError("Accessor properties are not allowed.");
+  }
+  return;
+}
+"""))
+
+    writable = bool(
+        cg_context.interface.indexed_and_named_properties.named_setter)
+    if writable:
+        body.append(
+            TextNode("""\
+// step 2.2.2. Invoke the named property setter with P and Desc.[[Value]].
+//
+// Return nothing and fall back to
+// ${class_name}::NamedPropertySetterCallback."""))
+    else:
+        body.append(
+            TextNode("""\
+// step 2.2.1. If creating is false and O does not implement an interface
+// with a named property setter, then return false.
+bindings::V8SetReturnValue(${info}, nullptr);
+if (${info}.ShouldThrowOnError()) {
+  ExceptionState exception_state(${info}.GetIsolate(),
+                                 ExceptionState::kSetterContext,
+                                 "${interface.identifier}");
+  exception_state.ThrowTypeError("Named property setter is not supported.");
+  return;
+}"""))
+
+    return func_decl, func_def
+
+
+def make_named_property_descriptor_callback(cg_context, function_name):
+    assert isinstance(cg_context, CodeGenContext)
+    assert isinstance(function_name, str)
+
+    arg_decls = [
+        "v8::Local<v8::Name> v8_property_name",
+        "const v8::PropertyCallbackInfo<v8::Value>& info",
+    ]
+    return_type = "void"
+
+    func_decl = CxxFuncDeclNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        static=True)
+
+    func_def = CxxFuncDefNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        class_name=cg_context.class_name)
+    func_def.set_base_template_vars(cg_context.template_bindings())
+    body = func_def.body
+    body.add_template_var("v8_property_name", "v8_property_name")
+    body.add_template_var("info", "info")
+    bind_callback_local_vars(body, cg_context)
+
+    pattern = """\
+// https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
+// Steps 2.1. is covered here: we rely on
+// NamedPropertyGetterCallback() to call the getter function and check
+// that |v8_property_name| is a valid property name, in which case it will
+// have set info.GetReturnValue() to something other than undefined.
+${class_name}::NamedPropertyGetterCallback(${v8_property_name}, ${info});
+v8::Local<v8::Value> v8_value = ${info}.GetReturnValue().Get();
+if (v8_value->IsUndefined())
+  return;
+
+// 2.1.5. Let |desc| be a newly created Property Descriptor with no fields.
+// 2.1.6. Set desc.[[Value]] to the result of converting value to an
+//   ECMAScript value.
+// 2.1.7. If O implements an interface with a named property setter, then
+//   set desc.[[Writable]] to true, otherwise set it to false.
+v8::PropertyDescriptor desc(v8_value, /*writable=*/{cxx_writable});
+// 2.1.8. If O implements an interface with the
+//   [LegacyUnenumerableNamedProperties] extended attribute, then set
+//   desc.[[Enumerable]] to false, otherwise set it to true.
+desc.set_enumerable({cxx_enumerable});
+// 2.1.9. Set desc.[[Configurable]] to true.
+desc.set_configurable(true);
+// 1.2.9. Return |desc|.
+bindings::V8SetReturnValue(${info}, desc);"""
+    props = cg_context.interface.indexed_and_named_properties
+    writable = bool(props.named_setter)
+    cxx_writable = "true" if writable else "false"
+    enumerable = props.is_named_property_enumerable
+    cxx_enumerable = "true" if enumerable else "false"
+    body.append(
+        TextNode(
+            _format(
+                pattern,
+                cxx_writable=cxx_writable,
+                cxx_enumerable=cxx_enumerable)))
+
+    return func_decl, func_def
+
+
+def make_named_property_enumerator_callback(cg_context, function_name):
+    assert isinstance(cg_context, CodeGenContext)
+    assert isinstance(function_name, str)
+
+    if not (cg_context.interface.indexed_and_named_properties.
+            is_named_property_enumerable):
+        return None, None
+
+    arg_decls = ["const v8::PropertyCallbackInfo<v8::Array>& info"]
+    return_type = "void"
+
+    func_decl = CxxFuncDeclNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        static=True)
+
+    func_def = CxxFuncDefNode(
+        name=function_name,
+        arg_decls=arg_decls,
+        return_type=return_type,
+        class_name=cg_context.class_name)
+    func_def.set_base_template_vars(cg_context.template_bindings())
+    body = func_def.body
+    body.add_template_var("info", "info")
+    bind_callback_local_vars(body, cg_context)
+
+    body.append(
+        TextNode("""\
+Vector<String> blink_property_names;
+${blink_receiver}->NamedPropertyEnumerator(
+  blink_property_names, ${exception_state});
+if (${exception_state}.HadException())
+  return;
+bindings::V8SetReturnValue(
+  ${info},
+  ToV8(blink_property_names, ${creation_context_object}, ${isolate}));
+"""))
+
+    return func_decl, func_def
+
+
 def make_stringifier_callback_def(cg_context, function_name):
     assert isinstance(cg_context, CodeGenContext)
     assert isinstance(function_name, str)
@@ -2321,6 +2990,7 @@
 
 def make_install_interface_template(cg_context, function_name, class_name,
                                     trampoline_var_name, constructor_entries,
+                                    indexed_and_named_property_install_nodes,
                                     install_unconditional_func_name,
                                     install_context_independent_func_name):
     """
@@ -2414,6 +3084,12 @@
             assert False
     body.append(EmptyNode())
 
+    if indexed_and_named_property_install_nodes:
+        body.extend([
+            indexed_and_named_property_install_nodes,
+            EmptyNode(),
+        ])
+
     if ("Global" in cg_context.class_like.extended_attributes
             or cg_context.class_like.identifier == "Location"):
         if "Global" in cg_context.class_like.extended_attributes:
@@ -2681,6 +3357,155 @@
     return func_decl, func_def, trampoline_def
 
 
+def make_indexed_and_named_property_callbacks_and_install_nodes(cg_context):
+    assert isinstance(cg_context, CodeGenContext)
+
+    func_decls = ListNode()
+    func_defs = SequenceNode()
+    install_nodes = SequenceNode()
+
+    interface = cg_context.interface
+    if not (interface and interface.indexed_and_named_properties):
+        return func_decls, func_defs, install_nodes
+    props = interface.indexed_and_named_properties
+
+    def add_callback(func_decl, func_def):
+        func_decls.append(func_decl)
+        if func_def:
+            func_defs.append(func_def)
+            func_defs.append(EmptyNode())
+
+    def most_derived_interface(*interfaces):
+        key = lambda interface: len(interface.inclusive_inherited_interfaces)
+        return sorted(filter(None, interfaces), key=key)[-1]
+
+    if props.own_indexed_getter or props.own_indexed_setter:
+        add_callback(*make_indexed_property_getter_callback(
+            cg_context.make_copy(indexed_property_getter=props.indexed_getter),
+            "IndexedPropertyGetterCallback"))
+
+        add_callback(*make_indexed_property_setter_callback(
+            cg_context.make_copy(indexed_property_setter=props.indexed_setter),
+            "IndexedPropertySetterCallback"))
+
+        add_callback(*make_indexed_property_definer_callback(
+            cg_context, "IndexedPropertyDefinerCallback"))
+
+        add_callback(*make_indexed_property_descriptor_callback(
+            cg_context, "IndexedPropertyDescriptorCallback"))
+
+        add_callback(*make_indexed_property_enumerator_callback(
+            cg_context, "IndexedPropertyEnumeratorCallback"))
+
+    if props.has_indexed_properties:
+        impl_bridge = v8_bridge_class_name(
+            most_derived_interface(
+                props.indexed_getter.owner,
+                props.indexed_setter and props.indexed_setter.owner,
+            ))
+        flags = []
+        if (props.indexed_getter.extended_attributes.value_of("Affects") ==
+                "Nothing"):
+            flags.append("v8::PropertyHandlerFlags::kHasNoSideEffect")
+        else:
+            flags.append("v8::PropertyHandlerFlags::kNone")
+        property_handler_flags = flags[0]
+        pattern = """\
+// Indexed properties
+${instance_template}->SetHandler(
+  v8::IndexedPropertyHandlerConfiguration(
+    {impl_bridge}::IndexedPropertyGetterCallback,
+% if interface.indexed_and_named_properties.indexed_setter:
+    {impl_bridge}::IndexedPropertySetterCallback,
+% else:
+    nullptr,  // setter
+% endif
+    {impl_bridge}::IndexedPropertyDescriptorCallback,
+    nullptr,  // deleter
+    {impl_bridge}::IndexedPropertyEnumeratorCallback,
+    {impl_bridge}::IndexedPropertyDefinerCallback,
+    v8::Local<v8::Value>(),
+    {property_handler_flags}));"""
+        install_nodes.append(
+            TextNode(
+                _format(
+                    pattern,
+                    impl_bridge=impl_bridge,
+                    property_handler_flags=property_handler_flags)))
+
+    if (props.own_named_getter or props.own_named_setter
+            or props.own_named_deleter):
+        add_callback(*make_named_property_getter_callback(
+            cg_context.make_copy(named_property_getter=props.named_getter),
+            "NamedPropertyGetterCallback"))
+
+        add_callback(*make_named_property_setter_callback(
+            cg_context.make_copy(named_property_setter=props.named_setter),
+            "NamedPropertySetterCallback"))
+
+        add_callback(*make_named_property_deleter_callback(
+            cg_context.make_copy(named_property_deleter=props.named_deleter),
+            "NamedPropertyDeleterCallback"))
+
+        add_callback(*make_named_property_definer_callback(
+            cg_context, "NamedPropertyDefinerCallback"))
+
+        add_callback(*make_named_property_descriptor_callback(
+            cg_context, "NamedPropertyDescriptorCallback"))
+
+        add_callback(*make_named_property_enumerator_callback(
+            cg_context, "NamedPropertyEnumeratorCallback"))
+
+    if props.has_named_properties:
+        impl_bridge = v8_bridge_class_name(
+            most_derived_interface(
+                props.named_getter.owner,
+                props.named_setter and props.named_setter.owner,
+                props.named_deleter and props.named_deleter.owner,
+            ))
+        flags = ["v8::PropertyHandlerFlags::kOnlyInterceptStrings"]
+        if "OverrideBuiltins" not in interface.extended_attributes:
+            flags.append("v8::PropertyHandlerFlags::kNonMasking")
+        if (props.named_getter.extended_attributes.value_of("Affects") ==
+                "Nothing"):
+            flags.append("v8::PropertyHandlerFlags::kHasNoSideEffect")
+        property_handler_flags = (
+            "static_cast<v8::PropertyHandlerFlags>({})".format(" | ".join(
+                map(lambda flag: "int({})".format(flag), flags))))
+        pattern = """\
+// Named properties
+${instance_template}->SetHandler(
+  v8::NamedPropertyHandlerConfiguration(
+    {impl_bridge}::NamedPropertyGetterCallback,
+% if interface.indexed_and_named_properties.named_setter:
+    {impl_bridge}::NamedPropertySetterCallback,
+% else:
+    nullptr,  // setter
+% endif
+    {impl_bridge}::NamedPropertyDescriptorCallback,
+% if interface.indexed_and_named_properties.named_deleter:
+    {impl_bridge}::NamedPropertyDeleterCallback,
+% else:
+    nullptr,  // deleter
+% endif
+% if interface.indexed_and_named_properties.is_named_property_enumerable:
+    {impl_bridge}::NamedPropertyEnumeratorCallback,
+% else:
+    nullptr,  // enumerator
+% endif
+    {impl_bridge}::NamedPropertyDefinerCallback,
+    v8::Local<v8::Value>(),
+    {property_handler_flags}));"""
+        install_nodes.append(
+            TextNode(
+                _format(
+                    pattern,
+                    impl_bridge=impl_bridge,
+                    property_handler_flags=property_handler_flags)))
+
+    return func_decls, func_defs, install_nodes
+
+
 def make_cross_component_init(
         cg_context, function_name, class_name, has_unconditional_props,
         has_context_independent_props, has_context_dependent_props):
@@ -3025,46 +3850,70 @@
 
     # Custom callback implementations
     custom_callback_impl_decls = ListNode()
-    if interface.identifier == "HTMLAllCollection":
+
+    def add_custom_callback_impl_decl(**params):
+        arg_decls = params.pop("arg_decls")
+        name = params.pop("name", None)
+        if name is None:
+            name = custom_function_name(cg_context.make_copy(**params))
         custom_callback_impl_decls.append(
             CxxFuncDeclNode(
-                name=name_style.func("LegacyCallCustom"),
-                arg_decls=["const v8::FunctionCallbackInfo<v8::Value>&"],
+                name=name,
+                arg_decls=arg_decls,
                 return_type="void",
                 static=True))
+
+    if interface.identifier == "HTMLAllCollection":
+        add_custom_callback_impl_decl(
+            name=name_style.func("LegacyCallCustom"),
+            arg_decls=["const v8::FunctionCallbackInfo<v8::Value>&"])
     for attribute in interface.attributes:
         custom_values = attribute.extended_attributes.values_of("Custom")
         if "Getter" in custom_values:
-            func_name = custom_function_name(
-                cg_context.make_copy(attribute=attribute, attribute_get=True))
-            custom_callback_impl_decls.append(
-                CxxFuncDeclNode(
-                    name=func_name,
-                    arg_decls=["const v8::FunctionCallbackInfo<v8::Value>&"],
-                    return_type="void",
-                    static=True))
+            add_custom_callback_impl_decl(
+                attribute=attribute,
+                attribute_get=True,
+                arg_decls=["const v8::FunctionCallbackInfo<v8::Value>&"])
         if "Setter" in custom_values:
-            func_name = custom_function_name(
-                cg_context.make_copy(attribute=attribute, attribute_set=True))
-            custom_callback_impl_decls.append(
-                CxxFuncDeclNode(
-                    name=func_name,
-                    arg_decls=[
-                        "v8::Local<v8::Value>",
-                        "const v8::FunctionCallbackInfo<v8::Value>&"
-                    ],
-                    return_type="void",
-                    static=True))
+            add_custom_callback_impl_decl(
+                attribute=attribute,
+                attribute_set=True,
+                arg_decls=[
+                    "v8::Local<v8::Value>",
+                    "const v8::FunctionCallbackInfo<v8::Value>&",
+                ])
     for operation_group in interface.operation_groups:
         if "Custom" in operation_group.extended_attributes:
-            func_name = custom_function_name(
-                cg_context.make_copy(operation_group=operation_group))
-            custom_callback_impl_decls.append(
-                CxxFuncDeclNode(
-                    name=func_name,
-                    arg_decls=["const v8::FunctionCallbackInfo<v8::Value>&"],
-                    return_type="void",
-                    static=True))
+            add_custom_callback_impl_decl(
+                operation_group=operation_group,
+                arg_decls=["const v8::FunctionCallbackInfo<v8::Value>&"])
+    if interface.indexed_and_named_properties:
+        props = interface.indexed_and_named_properties
+        operation = props.own_named_getter
+        if operation and "Custom" in operation.extended_attributes:
+            add_custom_callback_impl_decl(
+                named_property_getter=operation,
+                arg_decls=[
+                    "const AtomicString& property_name",
+                    "const v8::PropertyCallbackInfo<v8::Value>&",
+                ])
+        operation = props.own_named_setter
+        if operation and "Custom" in operation.extended_attributes:
+            add_custom_callback_impl_decl(
+                named_property_setter=operation,
+                arg_decls=[
+                    "const AtomicString& property_name",
+                    "v8::Local<v8::Value> v8_value",
+                    "const v8::PropertyCallbackInfo<v8::Value>&",
+                ])
+        operation = props.own_named_deleter
+        if operation and "Custom" in operation.extended_attributes:
+            add_custom_callback_impl_decl(
+                named_property_deleter=operation,
+                arg_decls=[
+                    "const AtomicString& property_name",
+                    "const v8::PropertyCallbackInfo<v8::Value>&",
+                ])
 
     # Cross-component trampolines
     if is_cross_components:
@@ -3094,6 +3943,12 @@
         exposed_construct_entries=exposed_construct_entries,
         operation_entries=operation_entries)
 
+    # Indexed and named properties
+    (indexed_and_named_property_decls, indexed_and_named_property_defs,
+     indexed_and_named_property_install_nodes) = (
+         make_indexed_and_named_property_callbacks_and_install_nodes(
+             cg_context))
+
     # Installer functions
     is_unconditional = lambda entry: entry.exposure_conditional.is_always_true
     is_context_dependent = lambda entry: entry.is_context_dependent
@@ -3143,6 +3998,8 @@
          class_name=impl_class_name,
          trampoline_var_name=tp_install_interface_template,
          constructor_entries=constructor_entries,
+         indexed_and_named_property_install_nodes=(
+             indexed_and_named_property_install_nodes),
          install_unconditional_func_name=(install_unconditional_props_def
                                           and FN_INSTALL_UNCONDITIONAL_PROPS),
          install_context_independent_func_name=(
@@ -3340,10 +4197,20 @@
             EmptyNode(),
         ])
 
+    if indexed_and_named_property_decls:
+        api_class_def.public_section.extend([
+            TextNode("// Indexed properties and named properties"),
+            indexed_and_named_property_decls,
+            EmptyNode(),
+        ])
+
     impl_source_blink_ns.body.extend([
         CxxNamespaceNode(name="", body=callback_defs),
         EmptyNode(),
         installer_function_defs,
+        EmptyNode(),
+        indexed_and_named_property_defs,
+        EmptyNode(),
     ])
 
     # Write down to the files.
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/interface.py b/third_party/blink/renderer/bindings/scripts/web_idl/interface.py
index 27505b8..feed2c0 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/interface.py
@@ -415,6 +415,14 @@
         return self.named_getter or self.named_setter or self.named_deleter
 
     @property
+    def is_named_property_enumerable(self):
+        named_getter = self.named_getter
+        return bool(named_getter
+                    and 'NotEnumerable' not in named_getter.extended_attributes
+                    and 'LegacyUnenumerableNamedProperties' not in self.owner.
+                    extended_attributes)
+
+    @property
     def indexed_getter(self):
         return self._find_accessor('own_indexed_getter')
 
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
index b64678a..f567216 100644
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
@@ -47,6 +47,15 @@
 
   StyleValueType GetType() const override { return kUnparsedType; }
 
+  void AnonymousIndexedGetter(uint32_t index,
+                              CSSUnparsedSegment& return_value,
+                              ExceptionState& exception_state) const {
+    return_value = AnonymousIndexedGetter(index, exception_state);
+  }
+  // TODO(crbug.com/1050474): Remove the following 2-arguments version once the
+  // migration to the new bindings generator is done.  The current policy is
+  // that return value of IDL union type is returned by argument.  This policy
+  // may change when we implement IDL union types with GarbageCollected classes.
   CSSUnparsedSegment AnonymousIndexedGetter(unsigned, ExceptionState&) const;
   IndexedPropertySetterResult AnonymousIndexedSetter(unsigned,
                                                      const CSSUnparsedSegment&,
diff --git a/third_party/blink/renderer/core/css/font_face.cc b/third_party/blink/renderer/core/css/font_face.cc
index 64164b6..be17462 100644
--- a/third_party/blink/renderer/core/css/font_face.cc
+++ b/third_party/blink/renderer/core/css/font_face.cc
@@ -559,8 +559,17 @@
         return normal_capabilities;
       if (!stretch_from->IsPercentage() || !stretch_to->IsPercentage())
         return normal_capabilities;
-      capabilities.width = {FontSelectionValue(stretch_from->GetFloatValue()),
-                            FontSelectionValue(stretch_to->GetFloatValue())};
+      // https://drafts.csswg.org/css-fonts/#font-prop-desc
+      // "User agents must swap the computed value of the startpoint and
+      // endpoint of the range in order to forbid decreasing ranges."
+      if (stretch_from->GetFloatValue() < stretch_to->GetFloatValue()) {
+        capabilities.width = {FontSelectionValue(stretch_from->GetFloatValue()),
+                              FontSelectionValue(stretch_to->GetFloatValue())};
+      } else {
+        capabilities.width = {
+            FontSelectionValue(stretch_to->GetFloatValue()),
+            FontSelectionValue(stretch_from->GetFloatValue())};
+      }
     } else if (auto* stretch_primitive_value =
                    DynamicTo<CSSPrimitiveValue>(stretch_.Get())) {
       float stretch_value = stretch_primitive_value->GetFloatValue();
@@ -613,9 +622,18 @@
                 To<CSSPrimitiveValue>(range_value->GetObliqueValues()->Item(0));
             const auto& range_end =
                 To<CSSPrimitiveValue>(range_value->GetObliqueValues()->Item(1));
-            capabilities.slope = {
-                FontSelectionValue(range_start.GetFloatValue()),
-                FontSelectionValue(range_end.GetFloatValue())};
+            // https://drafts.csswg.org/css-fonts/#font-prop-desc
+            // "User agents must swap the computed value of the startpoint and
+            // endpoint of the range in order to forbid decreasing ranges."
+            if (range_start.GetFloatValue() < range_end.GetFloatValue()) {
+              capabilities.slope = {
+                  FontSelectionValue(range_start.GetFloatValue()),
+                  FontSelectionValue(range_end.GetFloatValue())};
+            } else {
+              capabilities.slope = {
+                  FontSelectionValue(range_end.GetFloatValue()),
+                  FontSelectionValue(range_start.GetFloatValue())};
+            }
           }
         }
       }
@@ -658,8 +676,17 @@
       if (!weight_from->IsNumber() || !weight_to->IsNumber() ||
           weight_from->GetFloatValue() < 1 || weight_to->GetFloatValue() > 1000)
         return normal_capabilities;
-      capabilities.weight = {FontSelectionValue(weight_from->GetFloatValue()),
-                             FontSelectionValue(weight_to->GetFloatValue())};
+      // https://drafts.csswg.org/css-fonts/#font-prop-desc
+      // "User agents must swap the computed value of the startpoint and
+      // endpoint of the range in order to forbid decreasing ranges."
+      if (weight_from->GetFloatValue() < weight_to->GetFloatValue()) {
+        capabilities.weight = {FontSelectionValue(weight_from->GetFloatValue()),
+                               FontSelectionValue(weight_to->GetFloatValue())};
+      } else {
+        capabilities.weight = {
+            FontSelectionValue(weight_to->GetFloatValue()),
+            FontSelectionValue(weight_from->GetFloatValue())};
+      }
     } else if (auto* weight_primitive_value =
                    DynamicTo<CSSPrimitiveValue>(weight_.Get())) {
       float weight_value = weight_primitive_value->GetFloatValue();
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index 91d1b11..4ede24d 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -1413,12 +1413,13 @@
   return builder.ToString();
 }
 
-CSSValueList* CombineToRangeListOrNull(const CSSPrimitiveValue* range_start,
-                                       const CSSPrimitiveValue* range_end) {
+CSSValueList* CombineToRangeList(const CSSPrimitiveValue* range_start,
+                                 const CSSPrimitiveValue* range_end) {
   DCHECK(range_start);
   DCHECK(range_end);
-  if (range_end->GetFloatValue() < range_start->GetFloatValue())
-    return nullptr;
+  // Reversed ranges are valid, let them pass through here and swap them in
+  // FontFace to keep serialisation of the value as specified.
+  // https://drafts.csswg.org/css-fonts/#font-prop-desc
   CSSValueList* value_list = CSSValueList::CreateSpaceSeparated();
   value_list->Append(*range_start);
   value_list->Append(*range_end);
@@ -1462,7 +1463,7 @@
   if (!end_angle || !IsAngleWithinLimits(end_angle))
     return nullptr;
 
-  CSSValueList* range_list = CombineToRangeListOrNull(start_angle, end_angle);
+  CSSValueList* range_list = CombineToRangeList(start_angle, end_angle);
   if (!range_list)
     return nullptr;
   return MakeGarbageCollected<cssvalue::CSSFontStyleRangeValue>(
@@ -1499,7 +1500,7 @@
   if (!end_percent)
     return nullptr;
 
-  return CombineToRangeListOrNull(start_percent, end_percent);
+  return CombineToRangeList(start_percent, end_percent);
 }
 
 CSSValue* ConsumeFontWeight(CSSParserTokenRange& range,
@@ -1537,7 +1538,7 @@
       end_weight->GetFloatValue() > 1000)
     return nullptr;
 
-  return CombineToRangeListOrNull(start_weight, end_weight);
+  return CombineToRangeList(start_weight, end_weight);
 }
 
 CSSValue* ConsumeFontFeatureSettings(CSSParserTokenRange& range,
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.cc b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
index d5ad0a7..f861e72 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
@@ -90,6 +90,7 @@
 DisplayLockContext::DisplayLockContext(Element* element)
     : element_(element), document_(&element_->GetDocument()) {
   document_->AddDisplayLockContext(this);
+  DetermineIfSubtreeHasFocus();
 }
 
 void DisplayLockContext::SetRequestedState(ESubtreeVisibility state) {
@@ -124,6 +125,10 @@
          !needs_deferred_not_intersecting_signal_);
   needs_deferred_not_intersecting_signal_ = false;
   UpdateLifecycleNotificationRegistration();
+
+  // Note that we call this here since the |state_| change is a render affecting
+  // state, but is tracked independently.
+  NotifyRenderAffectingStateChanged();
 }
 
 void DisplayLockContext::AdjustElementStyle(ComputedStyle* style) const {
@@ -138,23 +143,13 @@
   style->SetContain(contain);
 }
 
-bool DisplayLockContext::RequestLock(uint16_t activation_mask) {
+void DisplayLockContext::RequestLock(uint16_t activation_mask) {
   UpdateActivationMask(activation_mask);
-
-  if (IsLocked())
-    return true;
-
-  if (IsActivated())
-    return false;
-
-  Lock();
-  return true;
+  SetRenderAffectingState(RenderAffectingState::kLockRequested, true);
 }
 
 void DisplayLockContext::RequestUnlock() {
-  ResetActivation();
-  if (IsLocked())
-    Unlock();
+  SetRenderAffectingState(RenderAffectingState::kLockRequested, false);
 }
 
 void DisplayLockContext::UpdateActivationMask(uint16_t activatable_mask) {
@@ -167,8 +162,6 @@
                             all_activation_is_blocked);
 
   activatable_mask_ = activatable_mask;
-  // Since activation mask has changed, reset all activation.
-  ResetActivation();
 }
 
 void DisplayLockContext::UpdateDocumentBookkeeping(
@@ -212,10 +205,18 @@
     return;
   is_observed_ = should_observe;
 
-  if (should_observe)
+  if (should_observe) {
     document_->RegisterDisplayLockActivationObservation(element_);
-  else
+  } else {
     document_->UnregisterDisplayLockActivationObservation(element_);
+    // If we're not listening to viewport intersections, then we can assume
+    // we're not intersecting:
+    // 1. We might not be connected, in which case we're not intersecting.
+    // 2. We might not be in 'auto' mode. which means that this doesn't affect
+    //    anything consequential but acts as a reset should we switch back to
+    //    the 'auto' mode.
+    SetRenderAffectingState(RenderAffectingState::kIntersectsViewport, false);
+  }
 }
 
 bool DisplayLockContext::NeedsLifecycleNotifications() const {
@@ -379,28 +380,14 @@
 
   RecordActivationReason(document_, reason);
 
-  // TODO(vmpstr): This should eventually only unlock on viewport intersection,
-  // but each reason needs to be tested and considered, so this is being done in
-  // parts.
-  if (reason == DisplayLockActivationReason::kScrollIntoView)
+  // TODO(vmpstr): This should eventually never unlock, but each reason needs to
+  // be tested and considered, so this is being done in parts.
+  if (reason == DisplayLockActivationReason::kScrollIntoView ||
+      reason == DisplayLockActivationReason::kScriptFocus ||
+      reason == DisplayLockActivationReason::kUserFocus)
     return;
 
   Unlock();
-  is_activated_ = true;
-}
-
-bool DisplayLockContext::IsActivated() const {
-  return is_activated_;
-}
-
-void DisplayLockContext::ResetActivation() {
-  is_activated_ = false;
-
-  if (RuntimeEnabledFeatures::CSSSubtreeVisibilityActivationEventEnabled()) {
-    // We're no longer activated, so if the signal didn't run yet, we should
-    // cancel it.
-    weak_factory_.InvalidateWeakPtrs();
-  }
 }
 
 void DisplayLockContext::NotifyIsIntersectingViewport() {
@@ -408,13 +395,7 @@
   // subtree and we don't need to lock as a result.
   needs_deferred_not_intersecting_signal_ = false;
   UpdateLifecycleNotificationRegistration();
-
-  if (!IsLocked())
-    return;
-
-  DCHECK(IsActivatable(DisplayLockActivationReason::kViewportIntersection));
-  CommitForActivationWithSignal(
-      element_, DisplayLockActivationReason::kViewportIntersection);
+  SetRenderAffectingState(RenderAffectingState::kIntersectsViewport, true);
 }
 
 void DisplayLockContext::NotifyIsNotIntersectingViewport() {
@@ -441,9 +422,7 @@
     needs_deferred_not_intersecting_signal_ = true;
   } else {
     needs_deferred_not_intersecting_signal_ = false;
-    DCHECK(IsActivated());
-    ResetActivation();
-    Lock();
+    SetRenderAffectingState(RenderAffectingState::kIntersectsViewport, false);
   }
   UpdateLifecycleNotificationRegistration();
 }
@@ -763,6 +742,8 @@
       document_->IncrementDisplayLockBlockingAllActivation();
     }
   }
+
+  DetermineIfSubtreeHasFocus();
 }
 
 void DisplayLockContext::WillStartLifecycleUpdate(const LocalFrameView& view) {
@@ -799,6 +780,7 @@
 
 void DisplayLockContext::ElementConnected() {
   UpdateActivationObservationIfNeeded();
+  DetermineIfSubtreeHasFocus();
 }
 
 void DisplayLockContext::ScheduleAnimation() {
@@ -883,6 +865,67 @@
   return element_ && document_ && element_->isConnected() && document_->View();
 }
 
+void DisplayLockContext::NotifySubtreeLostFocus() {
+  SetRenderAffectingState(RenderAffectingState::kSubtreeHasFocus, false);
+}
+
+void DisplayLockContext::NotifySubtreeGainedFocus() {
+  SetRenderAffectingState(RenderAffectingState::kSubtreeHasFocus, true);
+}
+
+void DisplayLockContext::DetermineIfSubtreeHasFocus() {
+  if (!ConnectedToView()) {
+    SetRenderAffectingState(RenderAffectingState::kSubtreeHasFocus, false);
+    return;
+  }
+
+  bool subtree_has_focus = false;
+  // Iterate up the ancestor chain from the currently focused element. If at any
+  // time we find our element, then our subtree is focused.
+  for (auto* focused = document_->FocusedElement(); focused;
+       focused = FlatTreeTraversal::ParentElement(*focused)) {
+    if (focused == element_.Get()) {
+      subtree_has_focus = true;
+      break;
+    }
+  }
+  SetRenderAffectingState(RenderAffectingState::kSubtreeHasFocus,
+                          subtree_has_focus);
+}
+
+void DisplayLockContext::SetRenderAffectingState(RenderAffectingState state,
+                                                 bool new_flag) {
+  render_affecting_state_[static_cast<int>(state)] = new_flag;
+  NotifyRenderAffectingStateChanged();
+}
+
+void DisplayLockContext::NotifyRenderAffectingStateChanged() {
+  auto state = [this](RenderAffectingState state) {
+    return render_affecting_state_[static_cast<int>(state)];
+  };
+
+  // Check that we're visible if and only if lock has not been requested.
+  DCHECK(state_ == ESubtreeVisibility::kVisible ||
+         state(RenderAffectingState::kLockRequested));
+  DCHECK(state_ != ESubtreeVisibility::kVisible ||
+         !state(RenderAffectingState::kLockRequested));
+
+  // We should be locked if the lock has been requested (the above DCHECKs
+  // verify that this means that we are not 'visible'), and any of the
+  // following is true:
+  // - We are not in 'auto' mode (meaning 'hidden') or
+  // - We are in 'auto' mode and nothing blocks locking: viewport is
+  //   not intersecting, and subtree doesn't have focus.
+  bool should_be_locked = state(RenderAffectingState::kLockRequested) &&
+                          (state_ != ESubtreeVisibility::kAuto ||
+                           (!state(RenderAffectingState::kIntersectsViewport) &&
+                            !state(RenderAffectingState::kSubtreeHasFocus)));
+  if (should_be_locked && !IsLocked())
+    Lock();
+  else if (!should_be_locked && IsLocked())
+    Unlock();
+}
+
 void DisplayLockContext::Trace(Visitor* visitor) {
   visitor->Trace(element_);
   visitor->Trace(document_);
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.h b/third_party/blink/renderer/core/display_lock/display_lock_context.h
index 51ecdb3..667567c 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.h
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.h
@@ -187,6 +187,9 @@
   void ElementDisconnected();
   void ElementConnected();
 
+  void NotifySubtreeLostFocus();
+  void NotifySubtreeGainedFocus();
+
   void SetNeedsPrePaintSubtreeWalk(
       bool needs_effective_allowed_touch_action_update) {
     needs_effective_allowed_touch_action_update_ =
@@ -218,15 +221,11 @@
   // Request that this context be locked. Called when style determines that the
   // subtree rooted at this element should be skipped, unless things like
   // viewport intersection prevent it from doing so.
-  bool RequestLock(uint16_t activation_mask);
+  void RequestLock(uint16_t activation_mask);
   // Request that this context be unlocked. Called when style determines that
   // the subtree rooted at this element should be rendered.
   void RequestUnlock();
 
-  // Returns true if this lock has been activated and the activation has not yet
-  // been cleared.
-  bool IsActivated() const;
-
   // Records the locked context counts on the document as well as context that
   // block all activation.
   void UpdateDocumentBookkeeping(bool was_locked,
@@ -304,6 +303,10 @@
   // Unlocks the context.
   void Unlock();
 
+  // Determines if the subtree has focus. This is a linear walk from the focused
+  // element to its root element.
+  void DetermineIfSubtreeHasFocus();
+
   WeakMember<Element> element_;
   WeakMember<Document> document_;
   ESubtreeVisibility state_ = ESubtreeVisibility::kVisible;
@@ -343,9 +346,6 @@
   uint16_t activatable_mask_ =
       static_cast<uint16_t>(DisplayLockActivationReason::kAny);
 
-  // State that tracks whether we've been activated.
-  bool is_activated_ = false;
-
   // Is set to true if we are registered for lifecycle notifications.
   bool is_registered_for_lifecycle_notifications_ = false;
 
@@ -358,6 +358,18 @@
   // Lock has been requested.
   bool is_locked_ = false;
 
+  enum class RenderAffectingState : int {
+    kLockRequested,
+    kIntersectsViewport,
+    kSubtreeHasFocus,
+    kNumRenderAffectingStates
+  };
+  void SetRenderAffectingState(RenderAffectingState state, bool flag);
+  void NotifyRenderAffectingStateChanged();
+
+  bool render_affecting_state_[static_cast<int>(
+      RenderAffectingState::kNumRenderAffectingStates)] = {false};
+
   // TODO(vmpstr): This is only needed while we're still sending activation
   // events.
   base::WeakPtrFactory<DisplayLockContext> weak_factory_{this};
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context_test.cc b/third_party/blink/renderer/core/display_lock/display_lock_context_test.cc
index 94217622..9456f3e 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context_test.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context_test.cc
@@ -142,7 +142,7 @@
   }
 
   void UnlockImmediate(DisplayLockContext* context) {
-    context->RequestUnlock();
+    context->SetRequestedState(ESubtreeVisibility::kVisible);
   }
 
   bool GraphicsLayerNeedsCollection(DisplayLockContext* context) const {
@@ -1641,10 +1641,9 @@
   bool IsObservingLifecycle(DisplayLockContext* context) const {
     return context->is_registered_for_lifecycle_notifications_;
   }
-  bool IsActivated(DisplayLockContext* context) const {
-    return context->IsActivated();
+  void LockImmediate(DisplayLockContext* context) {
+    context->SetRequestedState(ESubtreeVisibility::kHidden);
   }
-  void LockImmediate(DisplayLockContext* context) { context->RequestLock(0u); }
 };
 
 TEST_F(DisplayLockContextRenderingTest, FrameDocumentRemovedWhileAcquire) {
@@ -1812,7 +1811,6 @@
   // Verify lock state.
   auto* inner_context = inner_element->GetDisplayLockContext();
   ASSERT_TRUE(inner_context);
-  EXPECT_TRUE(IsActivated(inner_context));
   EXPECT_FALSE(inner_context->IsLocked());
 
   // Lock outer.
@@ -1887,7 +1885,6 @@
   EXPECT_FALSE(IsObservingLifecycle(inner_context));
 
   // Also we should still be activated and unlocked.
-  EXPECT_TRUE(IsActivated(inner_context));
   EXPECT_FALSE(inner_context->IsLocked());
 
   // Everything should be layout clean.
@@ -1946,7 +1943,6 @@
   // Verify lock state.
   auto* inner_context = inner_element->GetDisplayLockContext();
   ASSERT_TRUE(inner_context);
-  EXPECT_TRUE(IsActivated(inner_context));
   EXPECT_FALSE(inner_context->IsLocked());
 
   // Lock outer.
@@ -2023,7 +2019,6 @@
   EXPECT_TRUE(IsObservingLifecycle(inner_context));
 
   // We're unlocked for now.
-  EXPECT_TRUE(IsActivated(inner_context));
   EXPECT_FALSE(inner_context->IsLocked());
 
   // Everything should be layout clean.
@@ -2041,7 +2036,6 @@
   EXPECT_FALSE(IsObservingLifecycle(inner_context));
 
   // We're locked.
-  EXPECT_FALSE(IsActivated(inner_context));
   EXPECT_TRUE(inner_context->IsLocked());
 }
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc b/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc
index ea715fb7..7289620e 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc
@@ -345,4 +345,23 @@
   return false;
 }
 
+void DisplayLockUtilities::ElementLostFocus(Element* element) {
+  if (!RuntimeEnabledFeatures::CSSSubtreeVisibilityEnabled())
+    return;
+  for (; element; element = FlatTreeTraversal::ParentElement(*element)) {
+    auto* context = element->GetDisplayLockContext();
+    if (context)
+      context->NotifySubtreeLostFocus();
+  }
+}
+void DisplayLockUtilities::ElementGainedFocus(Element* element) {
+  if (!RuntimeEnabledFeatures::CSSSubtreeVisibilityEnabled())
+    return;
+  for (; element; element = FlatTreeTraversal::ParentElement(*element)) {
+    auto* context = element->GetDisplayLockContext();
+    if (context)
+      context->NotifySubtreeGainedFocus();
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_utilities.h b/third_party/blink/renderer/core/display_lock/display_lock_utilities.h
index b9b4bf33..89300d4 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_utilities.h
+++ b/third_party/blink/renderer/core/display_lock/display_lock_utilities.h
@@ -76,6 +76,11 @@
   // Returns true if the element is in a locked subtree (or is self-locked with
   // no self-updates). This crosses frames while navigating the ancestor chain.
   static bool IsInLockedSubtreeCrossingFrames(const Node& node);
+
+  // Called when the focused element changes. These functions update locks to
+  // ensure that focused element ancestors remain unlocked for 'auto' state.
+  static void ElementLostFocus(Element*);
+  static void ElementGainedFocus(Element*);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index f8c94cb..1dc26e9 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -5210,6 +5210,8 @@
     old_focused_element->SetFocused(false, params.type);
     old_focused_element->SetHasFocusWithinUpToAncestor(false, ancestor);
 
+    DisplayLockUtilities::ElementLostFocus(old_focused_element);
+
     // Dispatch the blur event and let the node do any other blur related
     // activities (important for text fields)
     // If page lost focus, blur event will have already been dispatched
@@ -5263,6 +5265,7 @@
 
     focused_element_->SetFocused(true, params.type);
     focused_element_->SetHasFocusWithinUpToAncestor(true, ancestor);
+    DisplayLockUtilities::ElementGainedFocus(focused_element_.Get());
 
     // Element::setFocused for frames can dispatch events.
     if (focused_element_ != new_focused_element) {
diff --git a/third_party/blink/renderer/core/events/composition_event.idl b/third_party/blink/renderer/core/events/composition_event.idl
index a22ed3f..3665dc1 100644
--- a/third_party/blink/renderer/core/events/composition_event.idl
+++ b/third_party/blink/renderer/core/events/composition_event.idl
@@ -32,9 +32,7 @@
     readonly attribute DOMString data;
 
     // https://w3c.github.io/uievents/#idl-interface-CompositionEvent-initializers
-    // TODO(foolip): None of the initCompositionEvent() arguments should be
-    // optional, and the spec has a locale argument after data.
-    [Measure] void initCompositionEvent(optional DOMString type = "undefined",
+    [Measure] void initCompositionEvent(DOMString type,
                                         optional boolean bubbles = false,
                                         optional boolean cancelable = false,
                                         optional Window? view = null,
diff --git a/third_party/blink/renderer/core/events/mouse_event.idl b/third_party/blink/renderer/core/events/mouse_event.idl
index 6569960..8ba6df5 100644
--- a/third_party/blink/renderer/core/events/mouse_event.idl
+++ b/third_party/blink/renderer/core/events/mouse_event.idl
@@ -37,8 +37,7 @@
     boolean getModifierState(DOMString keyArg);
 
     // https://w3c.github.io/uievents/#idl-interface-MouseEvent-initializers
-    // TODO(foolip): None of the initMouseEvent() arguments should be optional.
-    [CallWith=ScriptState, Measure] void initMouseEvent(optional DOMString type = "undefined",
+    [CallWith=ScriptState, Measure] void initMouseEvent(DOMString type,
                                                         optional boolean bubbles = false,
                                                         optional boolean cancelable = false,
                                                         optional Window? view = null,
diff --git a/third_party/blink/renderer/core/events/ui_event.idl b/third_party/blink/renderer/core/events/ui_event.idl
index fe020ca..b7f2540a 100644
--- a/third_party/blink/renderer/core/events/ui_event.idl
+++ b/third_party/blink/renderer/core/events/ui_event.idl
@@ -28,8 +28,7 @@
     readonly attribute InputDeviceCapabilities? sourceCapabilities;
 
     // https://w3c.github.io/uievents/#idl-interface-UIEvent-initializers
-    // TODO(foolip): None of the initUIEvent() arguments should be optional.
-    [Measure] void initUIEvent(optional DOMString type = "undefined",
+    [Measure] void initUIEvent(DOMString type,
                                optional boolean bubbles = false,
                                optional boolean cancelable = false,
                                optional Window? view = null,
diff --git a/third_party/blink/renderer/core/frame/frame.h b/third_party/blink/renderer/core/frame/frame.h
index 5af6339..958650d 100644
--- a/third_party/blink/renderer/core/frame/frame.h
+++ b/third_party/blink/renderer/core/frame/frame.h
@@ -274,9 +274,7 @@
   // Called when the focus controller changes the focus to this frame.
   virtual void DidFocus() = 0;
 
-  virtual void SetMainFrameViewportSize(const IntSize&) {}
   virtual IntSize GetMainFrameViewportSize() const = 0;
-  virtual void SetMainFrameScrollOffset(const IntPoint&) {}
   virtual IntPoint GetMainFrameScrollOffset() const = 0;
 
  protected:
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 6525947..2fdd3d5 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -1516,22 +1516,25 @@
       frame_view->ScheduleAnimation();
     }
   }
-  Tree().Top().SetMainFrameViewportSize(
-      intersection_state.main_frame_viewport_size);
-  Tree().Top().SetMainFrameScrollOffset(
-      IntPoint(intersection_state.main_frame_scroll_offset));
 }
 
 IntSize LocalFrame::GetMainFrameViewportSize() const {
-  if (!IsMainFrame())
-    return Tree().Top().GetMainFrameViewportSize();
-  return View()->GetScrollableArea()->VisibleContentRect().Size();
+  LocalFrame& local_root = LocalFrameRoot();
+  return local_root.IsMainFrame()
+             ? local_root.View()
+                   ->GetScrollableArea()
+                   ->VisibleContentRect()
+                   .Size()
+             : IntSize(local_root.intersection_state_.main_frame_viewport_size);
 }
 
 IntPoint LocalFrame::GetMainFrameScrollOffset() const {
-  if (!IsMainFrame())
-    return Tree().Top().GetMainFrameScrollOffset();
-  return FlooredIntPoint(View()->GetScrollableArea()->GetScrollOffset());
+  LocalFrame& local_root = LocalFrameRoot();
+  return local_root.IsMainFrame()
+             ? FlooredIntPoint(
+                   local_root.View()->GetScrollableArea()->GetScrollOffset())
+             : IntPoint(
+                   local_root.intersection_state_.main_frame_scroll_offset);
 }
 
 FrameOcclusionState LocalFrame::GetOcclusionState() const {
diff --git a/third_party/blink/renderer/core/frame/remote_frame.cc b/third_party/blink/renderer/core/frame/remote_frame.cc
index 12ffa76..2c07fd5 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame.cc
@@ -602,28 +602,18 @@
   To<RemoteFrameOwner>(Owner())->SetFramePolicy(frame_policy);
 }
 
-void RemoteFrame::SetMainFrameViewportSize(
-    const IntSize& main_frame_viewport_size) {
-  DCHECK(IsMainFrame());
-  main_frame_viewport_size_ = main_frame_viewport_size;
-}
-
 IntSize RemoteFrame::GetMainFrameViewportSize() const {
-  if (!IsMainFrame())
-    return Tree().Top().GetMainFrameViewportSize();
-  return main_frame_viewport_size_;
-}
-
-void RemoteFrame::SetMainFrameScrollOffset(
-    const IntPoint& main_frame_scroll_offset) {
-  DCHECK(IsMainFrame());
-  main_frame_scroll_offset_ = main_frame_scroll_offset;
+  HTMLFrameOwnerElement* owner = DeprecatedLocalOwner();
+  DCHECK(owner);
+  DCHECK(owner->GetDocument().GetFrame());
+  return owner->GetDocument().GetFrame()->GetMainFrameViewportSize();
 }
 
 IntPoint RemoteFrame::GetMainFrameScrollOffset() const {
-  if (!IsMainFrame())
-    return Tree().Top().GetMainFrameScrollOffset();
-  return main_frame_scroll_offset_;
+  HTMLFrameOwnerElement* owner = DeprecatedLocalOwner();
+  DCHECK(owner);
+  DCHECK(owner->GetDocument().GetFrame());
+  return owner->GetDocument().GetFrame()->GetMainFrameScrollOffset();
 }
 
 bool RemoteFrame::IsIgnoredForHitTest() const {
diff --git a/third_party/blink/renderer/core/frame/remote_frame.h b/third_party/blink/renderer/core/frame/remote_frame.h
index baddf4d..4f783fa 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.h
+++ b/third_party/blink/renderer/core/frame/remote_frame.h
@@ -140,9 +140,8 @@
   // the next navigation.
   void DidUpdateFramePolicy(const FramePolicy& frame_policy) override;
 
-  void SetMainFrameViewportSize(const IntSize&) override;
+  // Called only when this frame has a local frame owner.
   IntSize GetMainFrameViewportSize() const override;
-  void SetMainFrameScrollOffset(const IntPoint&) override;
   IntPoint GetMainFrameScrollOffset() const override;
 
  private:
@@ -167,8 +166,6 @@
   bool prevent_contents_opaque_changes_ = false;
   bool is_surface_layer_ = false;
   ParsedFeaturePolicy feature_policy_header_;
-  IntSize main_frame_viewport_size_;
-  IntPoint main_frame_scroll_offset_;
 
   mojo::AssociatedRemote<mojom::blink::RemoteFrameHost>
       remote_frame_host_remote_;
diff --git a/third_party/blink/renderer/core/html/parser/html_parser_metrics.cc b/third_party/blink/renderer/core/html/parser/html_parser_metrics.cc
index ba858bb..997ff68 100644
--- a/third_party/blink/renderer/core/html/parser/html_parser_metrics.cc
+++ b/third_party/blink/renderer/core/html/parser/html_parser_metrics.cc
@@ -45,19 +45,19 @@
 }
 
 void HTMLParserMetrics::ReportMetricsAtParseEnd() {
-  // Report the histograms
+  // The various histogram limits were chosen based on initial UKM data.
   UMA_HISTOGRAM_COUNTS_1000("Blink.HTMLParsing.ChunkCount", chunk_count_);
   UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
       "Blink.HTMLParsing.ParsingTimeMax", max_parsing_time_,
-      base::TimeDelta::FromMicroseconds(1),
-      base::TimeDelta::FromMilliseconds(100), 100);
-  UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
-      "Blink.HTMLParsing.ParsingTimeMin", min_parsing_time_,
-      base::TimeDelta::FromMicroseconds(1),
-      base::TimeDelta::FromMilliseconds(100), 100);
+      base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(100),
+      1000);
+  UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES("Blink.HTMLParsing.ParsingTimeMin",
+                                          min_parsing_time_,
+                                          base::TimeDelta::FromMicroseconds(1),
+                                          base::TimeDelta::FromSeconds(1), 100);
   UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
       "Blink.HTMLParsing.ParsingTimeTotal", accumulated_parsing_time_,
-      base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(10),
+      base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(100),
       1000);
   UMA_HISTOGRAM_COUNTS_1M("Blink.HTMLParsing.TokensParsedMax",
                           max_tokens_parsed_);
@@ -70,16 +70,16 @@
   if (max_yield_interval_ != base::TimeDelta()) {
     UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
         "Blink.HTMLParsing.YieldedTimeMax", max_yield_interval_,
-        base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(1),
-        100);
+        base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(100),
+        1000);
     UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
         "Blink.HTMLParsing.YieldedTimeMin", min_yield_interval_,
-        base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(1),
+        base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(10),
         100);
     UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
         "Blink.HTMLParsing.YieldedTimeAverage",
         accumulated_yield_intervals_ / yield_count_,
-        base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(1),
+        base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(10),
         100);
   }
 
diff --git a/third_party/blink/renderer/core/html/parser/html_parser_metrics_test.cc b/third_party/blink/renderer/core/html/parser/html_parser_metrics_test.cc
index 0e234d4..bbc84ce 100644
--- a/third_party/blink/renderer/core/html/parser/html_parser_metrics_test.cc
+++ b/third_party/blink/renderer/core/html/parser/html_parser_metrics_test.cc
@@ -82,7 +82,10 @@
   EXPECT_EQ(parsing_time_min_buckets.size(), 1u);
   EXPECT_EQ(parsing_time_total_buckets.size(), 1u);
   EXPECT_GT(parsing_time_max_buckets[0].min, 0);
-  EXPECT_EQ(parsing_time_max_buckets[0], parsing_time_min_buckets[0]);
+  // Parsing time max has more buckets, with more buckets closely spaced at the
+  // low end of time. So the bucket number should be higher than that for
+  // the min value.
+  EXPECT_GT(parsing_time_max_buckets[0].min, parsing_time_min_buckets[0].min);
   // Can't compare total buckets with max/min because different histogram
   // max values mean different bucket widths.
 }
@@ -161,8 +164,8 @@
   EXPECT_EQ(yield_time_min_buckets.size(), 1u);
   EXPECT_EQ(yield_time_average_buckets.size(), 1u);
   EXPECT_GT(yield_time_max_buckets[0].min, 0);
-  EXPECT_EQ(yield_time_max_buckets[0], yield_time_min_buckets[0]);
-  EXPECT_EQ(yield_time_max_buckets[0], yield_time_average_buckets[0]);
+  EXPECT_GT(yield_time_max_buckets[0].min, yield_time_min_buckets[0].min);
+  EXPECT_GT(yield_time_max_buckets[0].min, yield_time_average_buckets[0].min);
 }
 
 TEST_F(HTMLParserMetricsTest, UkmStoresValuesCorrectly) {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
index 5396fc5..20c673a 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
@@ -199,9 +199,17 @@
   // all live inside an anonymous child box of the fieldset container.
   auto fieldset_content = Node().GetFieldsetContent();
   if (fieldset_content && (content_break_token || !has_seen_all_children)) {
-    NGBreakStatus break_status =
-        LayoutFieldsetContent(fieldset_content, content_break_token,
-                              adjusted_padding_box_size, !!legend);
+    LayoutUnit fragmentainer_block_offset;
+    if (ConstraintSpace().HasBlockFragmentation()) {
+      fragmentainer_block_offset =
+          ConstraintSpace().FragmentainerOffsetAtBfc() + intrinsic_block_size_;
+      if (legend_broke_ &&
+          IsFragmentainerOutOfSpace(fragmentainer_block_offset))
+        return NGBreakStatus::kContinue;
+    }
+    NGBreakStatus break_status = LayoutFieldsetContent(
+        fieldset_content, content_break_token, adjusted_padding_box_size,
+        fragmentainer_block_offset, !!legend);
     if (break_status == NGBreakStatus::kNeedsEarlierBreak)
       return break_status;
   }
@@ -257,6 +265,7 @@
     }
 
     const auto& physical_fragment = result->PhysicalFragment();
+    legend_broke_ = physical_fragment.BreakToken();
 
     // We have already adjusted the legend block offset, no need to adjust
     // again.
@@ -305,6 +314,7 @@
     NGBlockNode& fieldset_content,
     scoped_refptr<const NGBlockBreakToken> content_break_token,
     LogicalSize adjusted_padding_box_size,
+    LayoutUnit fragmentainer_block_offset,
     bool has_legend) {
   auto child_space = CreateConstraintSpaceForFieldsetContent(
       fieldset_content, adjusted_padding_box_size,
@@ -316,11 +326,10 @@
 
   NGBreakStatus break_status = NGBreakStatus::kContinue;
   if (ConstraintSpace().HasBlockFragmentation()) {
-    LayoutUnit block_offset =
-        ConstraintSpace().FragmentainerOffsetAtBfc() + intrinsic_block_size_;
     // TODO(almaher): The legend should be treated as out-of-flow.
     break_status = BreakBeforeChildIfNeeded(
-        ConstraintSpace(), fieldset_content, *result.get(), block_offset,
+        ConstraintSpace(), fieldset_content, *result.get(),
+        fragmentainer_block_offset,
         /*has_container_separation*/ has_legend, &container_builder_);
     EBreakBetween break_after = JoinFragmentainerBreakValues(
         result->FinalBreakAfter(), fieldset_content.Style().BreakAfter());
@@ -337,6 +346,13 @@
   return break_status;
 }
 
+bool NGFieldsetLayoutAlgorithm::IsFragmentainerOutOfSpace(
+    LayoutUnit block_offset) const {
+  if (!ConstraintSpace().HasKnownFragmentainerBlockSize())
+    return false;
+  return block_offset >= FragmentainerSpaceAtBfcStart(ConstraintSpace());
+}
+
 base::Optional<MinMaxSizes> NGFieldsetLayoutAlgorithm::ComputeMinMaxSizes(
     const MinMaxSizesInput& input) const {
   MinMaxSizes sizes;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h
index 6d02edc..c375081 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h
@@ -37,6 +37,7 @@
       NGBlockNode& fieldset_content,
       scoped_refptr<const NGBlockBreakToken> content_break_token,
       LogicalSize adjusted_padding_box_size,
+      LayoutUnit fragmentainer_block_offset,
       bool has_legend);
 
   const NGConstraintSpace CreateConstraintSpaceForLegend(
@@ -49,6 +50,8 @@
       LogicalSize padding_box_size,
       LayoutUnit block_offset);
 
+  bool IsFragmentainerOutOfSpace(LayoutUnit block_offset) const;
+
   const WritingMode writing_mode_;
 
   const NGBoxStrut border_padding_;
@@ -75,6 +78,10 @@
   // If true, this indicates the block_start_padding_edge_ had changed from its
   // initial value during the current layout pass.
   bool block_start_padding_edge_adjusted_ = false;
+
+  // If true, this indicates that the legend broke during the current layout
+  // pass.
+  bool legend_broke_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc
index 93a1aee..cf0e7cad 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc
@@ -1302,7 +1302,7 @@
      <fieldset id="fieldset">
       <legend id="legend" style="width:10px; height:50px;"></legend>
       <div style="break-inside:avoid; width:20px; height:70px;"></div>
-    </fieldsest>
+    </fieldset>
   )HTML");
 
   LayoutUnit kFragmentainerSpaceAvailable(100);
@@ -1354,7 +1354,7 @@
      <fieldset id="fieldset">
       <legend id="legend" style="width:10px; height:50px;"></legend>
       <div style="break-inside:avoid; width:20px; height:170px;"></div>
-    </fieldsest>
+    </fieldset>
   )HTML");
 
   LayoutUnit kFragmentainerSpaceAvailable(100);
@@ -1416,7 +1416,7 @@
       <fieldset id="fieldset">
         <legend id="legend" style="break-inside:avoid; width:10px; height:60px;">
         </legend>
-      </fieldsest>
+      </fieldset>
     </div>
   )HTML");
 
@@ -1432,15 +1432,10 @@
       NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(node, space);
   ASSERT_FALSE(fragment->BreakToken()->IsFinished());
 
-  // TODO(almaher): Breaking before the fieldset has higher appeal than
-  // breaking inside the legend
   String dump = DumpFragmentTree(fragment.get());
   String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
   offset:unplaced size:1000x100
     offset:0,0 size:20x50
-    offset:0,50 size:100x50
-      offset:0,0 size:10x50
-      offset:0,50 size:100x0
 )DUMP";
   EXPECT_EQ(expectation, dump);
 
@@ -1450,9 +1445,10 @@
 
   dump = DumpFragmentTree(fragment.get());
   expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
-  offset:unplaced size:1000x10
-    offset:0,0 size:100x10
-      offset:0,0 size:10x10
+  offset:unplaced size:1000x60
+    offset:0,0 size:100x60
+      offset:0,0 size:10x60
+      offset:0,60 size:100x0
 )DUMP";
   EXPECT_EQ(expectation, dump);
 }
@@ -1473,7 +1469,7 @@
         <legend id="legend" style="width:10px; height:25px;"></legend>
         <div style="width:30px; height:25px;"></div>
         <div style="break-before:avoid; width:15px; height:25px;"></div>
-      </fieldsest>
+      </fieldset>
     </div>
   )HTML");
 
@@ -1527,7 +1523,7 @@
       <div style="width:20px; height:90px;"></div>
       <fieldset id="fieldset">
         <legend id="legend" style="break-before:avoid;"></legend>
-      </fieldsest>
+      </fieldset>
     </div>
   )HTML");
 
@@ -1580,7 +1576,7 @@
         <legend id="legend" style="width:10px; height:25px;"></legend>
         <div style="break-after:avoid; width:30px; height:25px;"></div>
         <div style="width:15px; height:25px;"></div>
-      </fieldsest>
+      </fieldset>
     </div>
   )HTML");
 
@@ -1635,7 +1631,7 @@
       <fieldset id="fieldset">
         <legend id="legend" style="break-after:avoid;"></legend>
         <div style="width:15px; height:25px;"></div>
-      </fieldsest>
+      </fieldset>
     </div>
   )HTML");
 
@@ -1690,7 +1686,7 @@
      <fieldset id="fieldset">
       <legend id="legend" style="margin-top:60px; width:10px; height:20px;"></legend>
       <div style="width:20px; height:20px;"></div>
-    </fieldsest>
+    </fieldset>
   )HTML");
 
   LayoutUnit kFragmentainerSpaceAvailable(50);
@@ -1742,7 +1738,7 @@
      <fieldset id="fieldset">
       <legend id="legend" style="margin-bottom:20px; height:90px;"></legend>
       <div style="width:20px; height:20px;"></div>
-    </fieldsest>
+    </fieldset>
   )HTML");
 
   LayoutUnit kFragmentainerSpaceAvailable(100);
diff --git a/third_party/blink/renderer/core/script/classic_pending_script.cc b/third_party/blink/renderer/core/script/classic_pending_script.cc
index 846e15e..99d8e6a 100644
--- a/third_party/blink/renderer/core/script/classic_pending_script.cc
+++ b/third_party/blink/renderer/core/script/classic_pending_script.cc
@@ -259,7 +259,7 @@
   ScriptElementBase* element = GetElement();
   if (element) {
     SubresourceIntegrityHelper::DoReport(
-        *element->GetDocument().ToExecutionContext(),
+        *element->GetDocument().GetExecutionContext(),
         GetResource()->IntegrityReportInfo());
 
     // It is possible to get back a script resource with integrity metadata
diff --git a/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc b/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
index d1a9de4..b1f9ed8 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
@@ -214,7 +214,15 @@
     const v8_inspector::V8StackTraceId& stack_id) {
   DCHECK(IsContextThread());
 
-  // Step 12. "If the algorithm asynchronously completes with null, then:"
+  // Step 12. "If the algorithm asynchronously completes with null or with
+  // script whose error to rethrow is non-null, then:"
+  //
+  // The case |error to rethrow| is non-null indicates the parse error.
+  // Parsing the script should be done during fetching according to the spec
+  // but it is done in EvaluateClassicScript() for classic scripts.
+  // Therefore, we cannot catch parse error events here.
+  // TODO(https://crbug.com/1058259) Catch parse error events for classic
+  // shared workers.
   if (classic_script_loader->Failed()) {
     // Step 12.1. "Queue a task to fire an event named error at worker."
     // Step 12.2. "Run the environment discarding steps for inside settings."
diff --git a/third_party/blink/renderer/core/workers/worker_module_tree_client.cc b/third_party/blink/renderer/core/workers/worker_module_tree_client.cc
index a55bcf02..fd24be1 100644
--- a/third_party/blink/renderer/core/workers/worker_module_tree_client.cc
+++ b/third_party/blink/renderer/core/workers/worker_module_tree_client.cc
@@ -25,8 +25,9 @@
   blink::WorkerReportingProxy& worker_reporting_proxy =
       worker_global_scope->ReportingProxy();
 
-  // Step 12. "If the algorithm asynchronously completes with null, then:"
-  if (!module_script) {
+  // Step 12. "If the algorithm asynchronously completes with null or with
+  // script whose error to rethrow is non-null, then:"
+  if (!module_script || module_script->HasErrorToRethrow()) {
     // Step 12.1. "Queue a task to fire an event named error at worker."
     // DidFailToFetchModuleScript() will asynchronously fire the event.
     worker_reporting_proxy.DidFailToFetchModuleScript();
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 921dae0f..e5a5570 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -2441,6 +2441,9 @@
     children.push_back(owned_child);
 
   for (AXObject* child : children) {
+    constexpr size_t kMaxDescendantsForTextAlternativeComputation = 100;
+    if (visited.size() > kMaxDescendantsForTextAlternativeComputation + 1)
+      break;  // Need to add 1 because the root naming node is in the list.
     // If a child is a continuation, we should ignore attributes like
     // hidden and presentational. See LAYOUT TREE WALKING ALGORITHM in
     // ax_layout_object.cc for more information on continuations.
diff --git a/third_party/blink/renderer/modules/remote_objects/remote_object.cc b/third_party/blink/renderer/modules/remote_objects/remote_object.cc
index 2319ad9..abb3c47 100644
--- a/third_party/blink/renderer/modules/remote_objects/remote_object.cc
+++ b/third_party/blink/renderer/modules/remote_objects/remote_object.cc
@@ -124,10 +124,10 @@
 
         nested_arguments.push_back(std::move(nested_argument));
       }
-
-      return mojom::blink::RemoteInvocationArgument::NewArrayValue(
-          std::move(nested_arguments));
     }
+
+    return mojom::blink::RemoteInvocationArgument::NewArrayValue(
+        std::move(nested_arguments));
   }
 
   return nullptr;
diff --git a/third_party/blink/renderer/platform/bindings/to_v8.h b/third_party/blink/renderer/platform/bindings/to_v8.h
index 86bde05..50ae965 100644
--- a/third_party/blink/renderer/platform/bindings/to_v8.h
+++ b/third_party/blink/renderer/platform/bindings/to_v8.h
@@ -220,27 +220,27 @@
 // Declare the function here but define it later so it can call the ToV8()
 // overloads below.
 template <typename Sequence>
-inline v8::Local<v8::Value> ToV8SequenceInternal(
+inline v8::Local<v8::Array> ToV8SequenceInternal(
     const Sequence&,
     v8::Local<v8::Object> creation_context,
     v8::Isolate*);
 
 template <typename T, size_t Extent>
-inline v8::Local<v8::Value> ToV8(base::span<T, Extent> value,
+inline v8::Local<v8::Array> ToV8(base::span<T, Extent> value,
                                  v8::Local<v8::Object> creation_context,
                                  v8::Isolate* isolate) {
   return ToV8SequenceInternal(value, creation_context, isolate);
 }
 
 template <typename T, wtf_size_t inlineCapacity>
-inline v8::Local<v8::Value> ToV8(const Vector<T, inlineCapacity>& value,
+inline v8::Local<v8::Array> ToV8(const Vector<T, inlineCapacity>& value,
                                  v8::Local<v8::Object> creation_context,
                                  v8::Isolate* isolate) {
   return ToV8SequenceInternal(value, creation_context, isolate);
 }
 
 template <typename T, wtf_size_t inlineCapacity>
-inline v8::Local<v8::Value> ToV8(const HeapVector<T, inlineCapacity>& value,
+inline v8::Local<v8::Array> ToV8(const HeapVector<T, inlineCapacity>& value,
                                  v8::Local<v8::Object> creation_context,
                                  v8::Isolate* isolate) {
   return ToV8SequenceInternal(value, creation_context, isolate);
@@ -301,7 +301,7 @@
 }
 
 template <typename Sequence>
-inline v8::Local<v8::Value> ToV8SequenceInternal(
+inline v8::Local<v8::Array> ToV8SequenceInternal(
     const Sequence& sequence,
     v8::Local<v8::Object> creation_context,
     v8::Isolate* isolate) {
@@ -324,7 +324,7 @@
     if (!array->CreateDataProperty(context, index++, value)
              .To(&created_property) ||
         !created_property) {
-      return v8::Local<v8::Value>();
+      return v8::Local<v8::Array>();
     }
   }
   return array;
diff --git a/third_party/blink/renderer/platform/bindings/v8_set_return_value.cc b/third_party/blink/renderer/platform/bindings/v8_set_return_value.cc
index a08ed40..a80ede46 100644
--- a/third_party/blink/renderer/platform/bindings/v8_set_return_value.cc
+++ b/third_party/blink/renderer/platform/bindings/v8_set_return_value.cc
@@ -11,6 +11,38 @@
 
 namespace bindings {
 
+v8::Local<v8::Object> CreatePropertyDescriptorObject(
+    v8::Isolate* isolate,
+    const v8::PropertyDescriptor& desc) {
+  // https://tc39.es/ecma262/#sec-frompropertydescriptor
+  v8::Local<v8::Context> current_context = isolate->GetCurrentContext();
+  v8::Local<v8::Object> object = v8::Object::New(isolate);
+
+  auto add_property = [&](const char* name, v8::Local<v8::Value> value) {
+    return object->CreateDataProperty(current_context, V8String(isolate, name),
+                                      value);
+  };
+  auto add_property_bool = [&](const char* name, bool value) {
+    return add_property(name, value ? v8::True(isolate) : v8::False(isolate));
+  };
+
+  bool result;
+  if (desc.has_value()) {
+    if (!(add_property("value", desc.value()).To(&result) &&
+          add_property_bool("writable", desc.writable()).To(&result)))
+      return v8::Local<v8::Object>();
+  } else {
+    if (!(add_property("get", desc.get()).To(&result) &&
+          add_property("set", desc.set()).To(&result)))
+      return v8::Local<v8::Object>();
+  }
+  if (!(add_property_bool("enumerable", desc.enumerable()).To(&result) &&
+        add_property_bool("configurable", desc.configurable()).To(&result)))
+    return v8::Local<v8::Object>();
+
+  return object;
+}
+
 v8::Local<v8::Value> GetInterfaceObjectExposedOnGlobal(
     v8::Isolate* isolate,
     v8::Local<v8::Object> creation_context,
diff --git a/third_party/blink/renderer/platform/bindings/v8_set_return_value.h b/third_party/blink/renderer/platform/bindings/v8_set_return_value.h
index 07cae22..ed639ef 100644
--- a/third_party/blink/renderer/platform/bindings/v8_set_return_value.h
+++ b/third_party/blink/renderer/platform/bindings/v8_set_return_value.h
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/platform/bindings/dom_data_store.h"
 #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
 #include "third_party/blink/renderer/platform/bindings/v8_value_cache.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
@@ -49,6 +50,70 @@
   info.GetReturnValue().Set(value);
 }
 
+// Property descriptor
+PLATFORM_EXPORT v8::Local<v8::Object> CreatePropertyDescriptorObject(
+    v8::Isolate* isolate,
+    const v8::PropertyDescriptor& desc);
+
+template <typename CallbackInfo>
+void V8SetReturnValue(const CallbackInfo& info,
+                      const v8::PropertyDescriptor& value) {
+  info.GetReturnValue().Set(
+      CreatePropertyDescriptorObject(info.GetIsolate(), value));
+}
+
+// Indexed properties and named properties
+PLATFORM_EXPORT inline void V8SetReturnValue(
+    const v8::FunctionCallbackInfo<v8::Value>& info,
+    IndexedPropertySetterResult value) {
+  // If an operation implementing indexed property setter is invoked as a
+  // regular operation, and the return type is not type void (V8SetReturnValue
+  // won't be called in case of type void), then return the given value as is.
+  info.GetReturnValue().Set(info[1]);
+}
+
+PLATFORM_EXPORT inline void V8SetReturnValue(
+    const v8::PropertyCallbackInfo<v8::Value>& info,
+    IndexedPropertySetterResult value) {
+  if (value == IndexedPropertySetterResult::kDidNotIntercept) {
+    // Do not set the return value to indicate that the request was not
+    // intercepted.
+    return;
+  }
+  info.GetReturnValue().SetNull();
+}
+
+PLATFORM_EXPORT inline void V8SetReturnValue(
+    const v8::FunctionCallbackInfo<v8::Value>& info,
+    NamedPropertySetterResult value) {
+  // If an operation implementing named property setter is invoked as a
+  // regular operation, and the return type is not type void (V8SetReturnValue
+  // won't be called in case of type void), then return the given value as is.
+  info.GetReturnValue().Set(info[1]);
+}
+
+PLATFORM_EXPORT inline void V8SetReturnValue(
+    const v8::PropertyCallbackInfo<v8::Value>& info,
+    NamedPropertySetterResult value) {
+  if (value == NamedPropertySetterResult::kDidNotIntercept) {
+    // Do not set the return value to indicate that the request was not
+    // intercepted.
+    return;
+  }
+  info.GetReturnValue().SetNull();
+}
+
+template <typename CallbackInfo>
+void V8SetReturnValue(const CallbackInfo& info,
+                      NamedPropertyDeleterResult value) {
+  if (value == NamedPropertyDeleterResult::kDidNotIntercept) {
+    // Do not set the return value to indicate that the request was not
+    // intercepted.
+    return;
+  }
+  info.GetReturnValue().Set(value == NamedPropertyDeleterResult::kDeleted);
+}
+
 // nullptr
 template <typename CallbackInfo>
 void V8SetReturnValue(const CallbackInfo& info, nullptr_t) {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 30bafb3..dbecf2c 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1715,7 +1715,7 @@
     },
     {
       name: "TrustedDOMTypes",
-      status: "experimental",
+      status: "stable",
     },
     {
       name: "TrustTokens",
diff --git a/third_party/blink/tools/blinkpy/common/system/executive.py b/third_party/blink/tools/blinkpy/common/system/executive.py
index 780edb7..88b5458 100644
--- a/third_party/blink/tools/blinkpy/common/system/executive.py
+++ b/third_party/blink/tools/blinkpy/common/system/executive.py
@@ -419,19 +419,9 @@
     def map(self, thunk, arglist, processes=None):
         if sys.platform == 'win32' or len(arglist) == 1:
             return map(thunk, arglist)
-        # multiprocessing.Pool will hang if child processes terminate
-        # unexpectedly (https://bugs.python.org/issue9205). The most common
-        # scenario is when a user presses Ctrl-C in an interactive terminal,
-        # which sends SIGINT to the whole process group, which may hang the
-        # script. Workaround: ignore SIGINT in worker processes and rely on
-        # pool.terminate() to exit gracefully.
-        original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
         pool = multiprocessing.Pool(processes=(processes or multiprocessing.cpu_count()))
-        signal.signal(signal.SIGINT, original_sigint_handler)
         try:
             return pool.map(thunk, arglist)
-        except KeyboardInterrupt:
-            pool.terminate()
         finally:
             pool.close()
             pool.join()
diff --git a/third_party/blink/tools/blinkpy/web_tests/web_tests_history.py b/third_party/blink/tools/blinkpy/web_tests/web_tests_history.py
index 9c82ebd..8d09f4d0 100644
--- a/third_party/blink/tools/blinkpy/web_tests/web_tests_history.py
+++ b/third_party/blink/tools/blinkpy/web_tests/web_tests_history.py
@@ -3,8 +3,10 @@
 # found in the LICENSE file.
 
 import argparse
-import sys
 import logging
+import multiprocessing
+import signal
+import traceback
 from collections import namedtuple
 
 from blinkpy.common.system.log_utils import configure_logging
@@ -80,16 +82,15 @@
                         ('chromium.org' in commit.author):
                     before_fork_googlers += 1
         if before_fork == 0:
-            print "%s\t[OK] created after fork" % path
+            return "%s\t[OK] created after fork" % path
         elif before_fork == before_fork_googlers:
-            print "%s\t[OK] created before fork, but all pre-fork commits from Googlers" % path
+            return "%s\t[OK] created before fork, but all pre-fork commits from Googlers" % path
         else:
-            print "%s\t[NO]" % path
+            return "%s\t[NO]" % path
 
     def _process_single(self, path):
-        abs_path = self.filesystem.join(self.port.web_tests_dir(), path)
-        commits = self.run_git_log(abs_path)
-        self.analyze(path, commits)
+        _init(self)
+        print _run(path)
         return 0
 
     def _process_many(self, paths):
@@ -99,23 +100,37 @@
             return 1
         _log.info("Total test files discovered: %d", len(files))
 
-        base_cmd = [self._path]
-        if self.options.verbose:
-            base_cmd += ['-v'] * self.options.verbose
-        commands = []
-        for path in files:
-            commands.append((base_cmd + [path], None))
-        results = self.host.executive.run_in_parallel(commands)
-        if results is None:
-            return 1
+        pool = multiprocessing.Pool(processes=multiprocessing.cpu_count(), initializer=_init, initargs=(self, ))
 
-        exit_code = 0
-        for retcode, out, err in results:
-            if retcode != 0:
-                exit_code = retcode
-            sys.stdout.write(out)
-            sys.stderr.write(err)
-        return exit_code
+        # Capture SIGTERM/INT to exit gracefully without leaving workers behind.
+        def _handler(signum, _):
+            _log.error('Received signal %d, exiting...', signum)
+            raise SystemExit
+
+        signal.signal(signal.SIGINT, _handler)
+        signal.signal(signal.SIGTERM, _handler)
+
+        try:
+            for res in pool.imap_unordered(_run, files):
+                # BaseException includes Exception as well as KeyboardInterrupt.
+                if isinstance(res, BaseException):
+                    # Traceback is already printed in the worker; exit directly.
+                    raise SystemExit
+                print res
+            pool.close()
+        except Exception:
+            # A user exception was raised from the manager (main) process.
+            traceback.print_exc()
+            pool.terminate()
+            return 1
+        except SystemExit:
+            # Either a worker process has exited unexpectedly, or the manager
+            # process has received SIGTERM/INT.
+            pool.terminate()
+            return 1
+        finally:
+            pool.join()
+        return 0
 
     def _is_test(self, path):
         return self.port.is_non_wpt_test_file(self.filesystem.dirname(path), self.filesystem.basename(path))
@@ -126,6 +141,29 @@
         return self._process_many(self.options.paths)
 
 
+# The following protected variable and functions are used inside worker
+# processes. Functions have to be Picklable to work with multiprocessing.Pool,
+# so they are defined at the module level, and thus the variable is global, too.
+_checker = None
+
+
+def _init(checker):
+    global _checker
+    _checker = checker
+
+
+def _run(path):
+    try:
+        abs_path = _checker.filesystem.join(_checker.port.web_tests_dir(), path)
+        commits = _checker.run_git_log(abs_path)
+        return _checker.analyze(path, commits)
+    except Exception as e:
+        traceback.print_exc()
+        return e
+    except KeyboardInterrupt as e:
+        return e
+
+
 def main(argv):
     host = Host()
     checker = HistoryChecker(host, argv)
diff --git a/third_party/blink/web_tests/FlagExpectations/layout-ng-fragment-item b/third_party/blink/web_tests/FlagExpectations/layout-ng-fragment-item
index 053397dd..c0abca6 100644
--- a/third_party/blink/web_tests/FlagExpectations/layout-ng-fragment-item
+++ b/third_party/blink/web_tests/FlagExpectations/layout-ng-fragment-item
@@ -185,7 +185,6 @@
 crbug.com/450493 crbug.com/971262 http/tests/devtools/profiler/live-line-level-heap-profile.js [ Crash Pass Timeout ]
 crbug.com/831673 http/tests/devtools/reveal-objects.js [ Pass Timeout ]
 crbug.com/849670 http/tests/devtools/service-workers/service-worker-v8-cache.js [ Timeout ]
-crbug.com/874695 crbug.com/954319 http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-in-multiple-workers.js [ Crash Pass Timeout ]
 crbug.com/874695 http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-shifted-breakpoint.js [ Timeout ]
 crbug.com/874695 http/tests/devtools/sources/debugger-breakpoints/dom-breakpoints.js [ Crash Pass ]
 crbug.com/450493 crbug.com/912793 http/tests/devtools/sources/debugger-breakpoints/restore-locations-for-breakpoint-with-broken-source-map.js [ Crash Failure Pass Timeout ]
@@ -710,7 +709,6 @@
 crbug.com/982194 external/wpt/offscreen-canvas/the-offscreen-canvas/offscreencanvas.resize.html [ Failure Pass ]
 crbug.com/982194 external/wpt/payment-method-basic-card/apply_the_modifiers.html [ Timeout ]
 crbug.com/982194 external/wpt/payment-method-basic-card/steps_for_selecting_the_payment_handler.html [ Timeout ]
-crbug.com/982194 external/wpt/pointerevents/pointerevent_lostpointercapture_for_disconnected_node_in_shadow_dom.html [ Pass Timeout ]
 crbug.com/982194 external/wpt/quirks/text-decoration-doesnt-propagate-into-tables/quirks.html [ Failure ]
 crbug.com/982194 external/wpt/screen-orientation/onchange-event-subframe.html [ Pass ]
 crbug.com/982194 external/wpt/svg/painting/marker-005.svg [ Failure ]
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index 8cfd781..7f8e9ec 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -78,43 +78,17 @@
 webkit.org/b/90488 [ Debug ] http/tests/devtools/* [ Slow ]
 webkit.org/b/90488 [ Debug ] inspector-protocol/* [ Slow ]
 
-# DevTools console, debugger and profiler tests are slow in Release as well.
-crbug.com/451577 http/tests/devtools/console/* [ Slow ]
-crbug.com/450493 http/tests/devtools/sources/* [ Slow ]
-crbug.com/450493 http/tests/devtools/startup/sources/* [ Slow ]
-crbug.com/450493 http/tests/devtools/csp/* [ Slow ]
-crbug.com/450493 http/tests/devtools/profiler/* [ Slow ]
-crbug.com/420008 http/tests/devtools/tracing/* [ Slow ]
+# DevTools tests cause flakes on slow hardware in Release as well.
+crbug.com/1063051 [ Release ] http/tests/devtools/* [ Slow ]
 crbug.com/420008 virtual/threaded/http/tests/devtools/tracing/* [ Slow ]
-crbug.com/902685 http/tests/devtools/isolated-code-cache/* [ Slow ]
 crbug.com/902685 virtual/site-isolated-code-cache/http/tests/devtools/isolated-code-cache/* [ Slow ]
 crbug.com/902685 virtual/not-split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/* [ Slow ]
-crbug.com/902685 http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js [ Slow ]
 crbug.com/902685 virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js [ Slow ]
 crbug.com/902685 virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/* [ Slow ]
 # Misc DevTools tests that are slow
-crbug.com/246190 [ Release ] http/tests/devtools/indexeddb/* [ Slow ]
-crbug.com/451577 [ Mac ] http/tests/devtools/extensions/extensions-sidebar.js [ Slow ]
-crbug.com/941860 http/tests/devtools/extensions/extensions-events.js [ Slow ]
-crbug.com/679833 http/tests/devtools/network/network-datareceived.js [ Slow ]
-webkit.org/b/90488 [ Release ] http/tests/devtools/compiler-source-mapping-debug.js [ Slow ]
 crbug.com/243492 inspector-protocol/injected-script-discard.js [ Slow ]
-crbug.com/327078 http/tests/devtools/network/long-script-content.js [ Slow ]
-crbug.com/420008 [ Release ] http/tests/devtools/editor/text-editor-word-jumps.js [ Slow ]
-crbug.com/420008 [ Release ] http/tests/devtools/console-xhr-logging.js [ Slow ]
-crbug.com/596486 [ Linux ] http/tests/devtools/elements/insert-node.js [ Slow ]
-crbug.com/451577 http/tests/devtools/resource-tree/resource-tree-crafted-frame-add.js [ Slow ]
-crbug.com/451577 http/tests/devtools/resource-tree/resource-tree-frame-in-crafted-frame.js [ Slow ]
-crbug.com/510337 http/tests/devtools/elements/styles-1/edit-value-url-with-color.js [ Slow ]
-crbug.com/451577 [ Mac ] http/tests/devtools/extensions/extensions-reload.js [ Slow ]
-crbug.com/451577 [ Mac ] http/tests/devtools/extensions/extensions-resources.js [ Slow ]
-crbug.com/451577 [ Win10 ] http/tests/devtools/extensions/extensions-sidebar.js [ Slow ]
-crbug.com/451577 [ Mac ] http/tests/devtools/layers/layer-canvas-log.js [ Slow ]
-crbug.com/451577 [ Mac ] http/tests/devtools/network/network-domain-filter.js [ Slow ]
-crbug.com/859169 http/tests/devtools/layers/layer-compositing-reasons.js [ Slow ]
 
 crbug.com/510337 cssom/cssvalue-comparison.html [ Slow ]
-crbug.com/1034030 [ Mac ] http/tests/devtools/resource-har-conversion.js [ Slow ]
 
 # These tests are intentionally SLOW because of throttled loading.
 crbug.com/73609 http/tests/media/video-play-stall.html [ Slow ]
@@ -154,22 +128,16 @@
 # This test takes 5+ seconds as intended because it tests connection throttling.
 crbug.com/459377 http/tests/websocket/multiple-connections-throttled.html [ Slow ]
 
-crbug.com/336481 http/tests/devtools/jump-to-previous-editing-location.js [ Slow ]
 crbug.com/346259 http/tests/websocket/no-crash-on-cookie-flood.html [ Slow ]
 
 crbug.com/522646 http/tests/media/encrypted-media/encrypted-media-encrypted-event-different-origin.html [ Slow ]
 crbug.com/522646 virtual/audio-service/http/tests/media/encrypted-media/encrypted-media-encrypted-event-different-origin.html [ Slow ]
 crbug.com/411164 [ Win ] http/tests/security/powerfulFeatureRestrictions/serviceworker-on-insecure-origin.html [ Slow ]
-crbug.com/510337 http/tests/devtools/console/console-format.js [ Slow ]
 crbug.com/357427 http/tests/workers/terminate-during-sync-operation-file.html [ Slow ]
 crbug.com/357427 http/tests/workers/terminate-during-sync-operation-filesystem.html [ Slow ]
 crbug.com/402379 [ Debug ] storage/indexeddb/cursor-continue-validity.html [ Slow ]
 crbug.com/402379 [ Debug ] storage/indexeddb/mozilla/indexes.html [ Slow ]
-crbug.com/480769 http/tests/devtools/service-workers/service-workers-redundant.js [ Slow ]
-crbug.com/480769 http/tests/devtools/service-workers/service-worker-agents.js [ Slow ]
 crbug.com/504703 inspector-protocol/debugger/debugger-step-into-dedicated-worker.js [ Slow ]
-crbug.com/548765 http/tests/devtools/console-fetch-logging.js [ Slow ]
-crbug.com/937378 http/tests/devtools/service-workers/service-workers-force-update-on-page-load.js [ Slow ]
 
 crbug.com/419993 [ Debug ] fast/css/giant-stylesheet-crash.html [ Slow ]
 
@@ -187,7 +155,6 @@
 crbug.com/459009 crypto/subtle/* [ Slow ]
 
 
-crbug.com/528419 http/tests/devtools/elements/styles-2/pseudo-elements.js [ Slow ]
 
 crbug.com/802029 fast/dom/shadow/focus-controller-recursion-crash.html [ Slow ]
 
@@ -233,10 +200,6 @@
 
 crbug.com/741259 editing/selection/select-bidi-run.html [ Slow ]
 
-# These tests were previously marked Slow in ASANExpectations.
-crbug.com/451577 [ Linux ] http/tests/devtools/elements/user-properties.js [ Slow ]
-crbug.com/451577 [ Linux ] http/tests/devtools/layers/layer-canvas-log.js [ Slow ]
-
 crbug.com/793771 media/controls/scrubbing.html [ Slow ]
 crbug.com/793771 virtual/audio-service/media/controls/scrubbing.html [ Slow ]
 
@@ -257,8 +220,6 @@
 # This test has many test cases and each case requires a gesture scroll with pauses in between.
 crbug.com/765326 virtual/scroll_customization/fast/scroll-behavior/scroll-customization/scroll-customization-property.html [ Slow ]
 
-crbug.com/808185 [ Win Release ] http/tests/devtools/editor/text-editor-ctrl-d-1.js [ Slow ]
-
 # This test does a lot of IPC because it tests limits on IPC allocations,
 # and is therefore slow.
 crbug.com/826957 fast/peerconnection/RTCPeerConnection-manyCandidates.html [ Slow ]
@@ -324,8 +285,6 @@
 crbug.com/869662 [ Mac10.13 ] virtual/threaded-prefer-compositing/fast/scrolling/overflow-scrollability.html [ Slow ]
 
 # Tests with slowest_run >= 10s on flakiness dashboard for site_per_process_blink_web_tests
-crbug.com/874695 http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-shifted-breakpoint.js [ Slow ]
-crbug.com/874695 http/tests/devtools/sources/sourcemap-hot-reload.js [ Slow ]
 
 # Tests where |3s <= slowest_run < 10s| on flakiness dashboard for site_per_process_blink_web_tests
 crbug.com/874695 accessibility/is-ignored-change-sends-notification.html [ Slow ]
@@ -392,20 +351,6 @@
 crbug.com/874695 http/tests/credentialmanager/credentialscontainer-create-with-virtual-authenticator.html [ Slow ]
 crbug.com/874695 http/tests/credentialmanager/credentialscontainer-get-origins.html [ Slow ]
 crbug.com/874695 http/tests/credentialmanager/register-then-sign.html [ Slow ]
-crbug.com/874695 http/tests/devtools/console/console-correct-suggestions.js [ Slow ]
-crbug.com/874695 http/tests/devtools/editor/text-editor-formatter.js [ Slow ]
-crbug.com/874695 http/tests/devtools/inspect-iframe-from-different-domain.js [ Slow ]
-crbug.com/874695 http/tests/devtools/oopif/oopif-cookies-refresh.js [ Slow ]
-crbug.com/874695 http/tests/devtools/persistence/persistence-mimetype-on-rename.js [ Slow ]
-crbug.com/874695 http/tests/devtools/service-workers/user-agent-override.js [ Slow ]
-crbug.com/874695 http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-in-multiple-workers.js [ Slow ]
-crbug.com/874695 http/tests/devtools/sources/debugger-breakpoints/dom-breakpoints.js [ Slow ]
-crbug.com/874695 http/tests/devtools/sources/debugger/debugger-proto-property.js [ Slow ]
-crbug.com/874695 http/tests/devtools/sources/debugger-frameworks/frameworks-dom-xhr-event-breakpoints.js [ Slow ]
-crbug.com/874695 http/tests/devtools/sources/debugger/live-edit-no-reveal.js [ Slow ]
-crbug.com/874695 http/tests/devtools/tracing/decode-resize.js [ Slow ]
-crbug.com/874695 http/tests/devtools/tracing/timeline-paint/paint-profiler-update.js [ Slow ]
-crbug.com/874695 http/tests/devtools/tracing/timeline-style/timeline-style-recalc-all-invalidator-types.js [ Slow ]
 crbug.com/874695 http/tests/fetch/serviceworker/body-mixin-base-https-other-https.html [ Slow ]
 crbug.com/874695 http/tests/fetch/serviceworker/body-mixin.html [ Slow ]
 crbug.com/874695 http/tests/fetch/serviceworker/stream-reader-base-https-other-https.html [ Slow ]
@@ -586,8 +531,6 @@
 crbug.com/874695 virtual/streaming-preload/http/tests/fetch/workers/stream-reader-base-https-other-https.html [ Slow ]
 crbug.com/874695 virtual/streaming-preload/http/tests/fetch/workers/stream-reader.html [ Slow ]
 crbug.com/874695 virtual/streaming-preload/http/tests/fetch/workers/thorough/* [ Slow ]
-crbug.com/874695 http/tests/devtools/sxg/sxg-cert-not-found.js [ Slow ]
-crbug.com/874695 http/tests/devtools/sxg/sxg-disable-cache.js [ Slow ]
 crbug.com/874695 virtual/scalefactor200/css3/filters/effect-all-on-background-hw.html [ Slow ]
 crbug.com/874695 virtual/scalefactor200/css3/filters/effect-blur-hw.html [ Slow ]
 crbug.com/874695 virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw.html [ Slow ]
@@ -622,7 +565,6 @@
 
 crbug.com/878650 [ Release ] inspector-protocol/heap-profiler/heap-snapshot-merged-nodes.js [ Slow ]
 crbug.com/878650 [ Release ] inspector-protocol/heap-profiler/heap-snapshot-with-no-detached-iframe.js [ Slow ]
-crbug.com/939037 http/tests/devtools/profiler/heap-snapshot-location.js [ Slow ]
 
 crbug.com/893015 [ Linux ] http/tests/fetch/chromium/response-array-buffer-gc-crash.html [ Slow ]
 crbug.com/893015 [ Linux ] http/tests/fetch/chromium/response-blob-gc-crash.html [ Slow ]
@@ -641,10 +583,6 @@
 
 crbug.com/951895 [ Mac Debug ] transforms/2d/transform-2d.html [ Slow ]
 
-crbug.com/962831 [ Release ] http/tests/devtools/network/preview-searchable.js [ Slow ]
-
-crbug.com/967526 http/tests/devtools/console/console-uncaught-promise.js [ Slow ]
-
 crbug.com/980804 fast/scrolling/scrollbars/mouse-autoscrolling-on-scrollbar.html [ Slow ]
 crbug.com/980804 virtual/compositor_threaded_scrollbar_scrolling/fast/scrolling/scrollbars/mouse-autoscrolling-on-scrollbar.html [ Slow ]
 crbug.com/980804 virtual/scroll_customization/fast/scrolling/scrollbars/mouse-autoscrolling-on-scrollbar.html [ Slow ]
@@ -662,15 +600,6 @@
 # Slow when using Vulkan.
 crbug.com/994331 compositing/lots-of-img-layers.html [ Slow ]
 
-crbug.com/1028479 [ Mac ] http/tests/devtools/bindings/jssourcemap-navigator-overlapping-sources.js [ Slow ]
-crbug.com/1027439 http/tests/devtools/storage-panel-dom-storage-update.js [ Slow ]
-
-# Mark all axe audit tests slow.
-crbug.com/1020379 http/tests/devtools/a11y-axe-core/* [ Slow ]
-
-crbug.com/1040054 http/tests/devtools/cache-storage/cache-data.js [ Slow ]
-crbug.com/1040450 http/tests/devtools/persistence/persistence-external-change-breakpoints.js [ Slow ]
-
 crbug.com/1048204 external/wpt/selection/idlharness.window.html [ Slow ]
 
 # This test contains a slow loading image that results in timeouts.
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 3039965..d9e70e9 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -946,7 +946,6 @@
 crbug.com/591099 [ Mac ] paint/invalidation/flexbox/remove-inline-block-descendant-of-flex.html [ Failure ]
 crbug.com/591099 [ Mac ] fast/replaced/input-radio-height-inside-auto-container.html [ Failure ]
 crbug.com/591099 [ Mac10.11 ] virtual/audio-service/media/track/track-cue-rendering-position-auto-rtl.html [ Failure ]
-crbug.com/591099 [ Mac10.13 ] fast/dynamic/outerHTML-doc.html [ Failure ]
 
 # A few other lines for this test are commented out above to avoid conflicting expectations.
 # If they are not also fixed, reinstate them when removing these lines.
@@ -2687,14 +2686,6 @@
 # These could likely be removed - see https://chromium-review.googlesource.com/c/chromium/src/+/1252141
 crbug.com/654477 media/controls-focus-ring.html [ Failure ]
 crbug.com/654477 virtual/audio-service/media/controls-focus-ring.html [ Failure ]
-crbug.com/654477 [ Mac10.10 ] http/tests/media/video-buffered-range-contains-currentTime.html [ Failure ]
-crbug.com/654477 [ Mac10.11 ] http/tests/media/video-buffered-range-contains-currentTime.html [ Failure ]
-crbug.com/654477 [ Retina ] http/tests/media/video-buffered-range-contains-currentTime.html [ Failure ]
-crbug.com/638621 [ Win7 ] http/tests/media/video-buffered-range-contains-currentTime.html [ Failure ]
-crbug.com/654477 [ Mac10.10 ] virtual/audio-service/http/tests/media/video-buffered-range-contains-currentTime.html [ Failure ]
-crbug.com/654477 [ Mac10.11 ] virtual/audio-service/http/tests/media/video-buffered-range-contains-currentTime.html [ Failure ]
-crbug.com/654477 [ Retina ] virtual/audio-service/http/tests/media/video-buffered-range-contains-currentTime.html [ Failure ]
-crbug.com/638621 [ Win7 ] virtual/audio-service/http/tests/media/video-buffered-range-contains-currentTime.html [ Failure ]
 
 crbug.com/637930 http/tests/media/video-buffered.html [ Pass Failure ]
 crbug.com/637930 virtual/audio-service/http/tests/media/video-buffered.html [ Pass Failure ]
@@ -3031,7 +3022,6 @@
 crbug.com/626703 [ Linux ] external/wpt/workers/abrupt-completion.html [ Timeout ]
 crbug.com/626703 [ Mac ] external/wpt/workers/abrupt-completion.html [ Timeout ]
 crbug.com/626703 [ Win ] external/wpt/workers/abrupt-completion.html [ Timeout ]
-crbug.com/626703 [ Retina ] virtual/sxg-subresource/external/wpt/signed-exchange/reporting/sxg-reporting-navigation-parse_error.tentative.html [ Timeout ]
 crbug.com/626703 [ Linux ] external/wpt/webaudio/the-audio-api/the-audioworklet-interface/process-parameters.https.html [ Timeout ]
 crbug.com/626703 [ Mac ] external/wpt/webaudio/the-audio-api/the-audioworklet-interface/process-parameters.https.html [ Timeout ]
 crbug.com/626703 [ Win ] external/wpt/webaudio/the-audio-api/the-audioworklet-interface/process-parameters.https.html [ Timeout ]
@@ -3128,15 +3118,12 @@
 crbug.com/626703 [ Linux ] virtual/omt-worker-fetch/external/wpt/fetch/corb/script-resource-with-nonsniffable-types.tentative.sub.html [ Timeout ]
 crbug.com/626703 [ Mac ] virtual/omt-worker-fetch/external/wpt/fetch/corb/script-resource-with-nonsniffable-types.tentative.sub.html [ Timeout ]
 crbug.com/626703 [ Win ] virtual/omt-worker-fetch/external/wpt/fetch/corb/script-resource-with-nonsniffable-types.tentative.sub.html [ Timeout ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-disabled.html [ Timeout ]
 crbug.com/626703 [ Win ] external/wpt/webvtt/rendering/cues-with-video/processing-model/embedded_style_media_queries.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/webvtt/rendering/cues-with-video/processing-model/embedded_style_media_queries_resized.html [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html [ Timeout ]
 crbug.com/626703 [ Mac ] external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html [ Timeout ]
 crbug.com/626703 [ Win ] external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/location-set-and-document-open.html [ Timeout ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cues-enter-exit.html [ Timeout ]
 crbug.com/626703 [ Mac ] external/wpt/preload/preload-with-type.html [ Pass Timeout ]
-crbug.com/626703 [ Retina ] virtual/threaded/external/wpt/css/css-animations/animation-opacity-pause-and-set-time.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/html/rendering/non-replaced-elements/form-controls/select-sizing-001.html [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/css/css-align/baseline-rules/grid-item-input-type-number.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/css/css-align/baseline-rules/grid-item-input-type-number.html [ Failure ]
@@ -3158,7 +3145,6 @@
 crbug.com/626703 [ Win10 ] external/wpt/payment-request/payment-request-hasenrolledinstrument-method.tentative.https.html [ Failure Timeout ]
 crbug.com/626703 [ Win10 ] virtual/cascade/external/wpt/css/css-paint-api/color-custom-property-animation.https.html [ Failure ]
 crbug.com/626703 [ Win10 ] external/wpt/css/css-paint-api/color-custom-property-animation.https.html [ Failure ]
-crbug.com/626703 [ Retina ] virtual/cascade/external/wpt/css/css-paint-api/one-custom-property-animation.https.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/web-animations/timing-model/animation-effects/phases-and-states.html [ Crash ]
 crbug.com/626703 [ Linux ] external/wpt/webvtt/rendering/cues-with-video/processing-model/snap-to-line.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/webvtt/rendering/cues-with-video/processing-model/snap-to-line.html [ Failure ]
@@ -3175,15 +3161,10 @@
 crbug.com/626703 [ Linux ] external/wpt/css/css-lists/li-value-reversed-003.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/css/css-lists/li-value-reversed-003.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/css/css-lists/li-value-reversed-003.html [ Failure ]
-crbug.com/626703 [ Mac10.10 ] virtual/cascade/external/wpt/css/css-paint-api/parse-input-arguments-018.https.html [ Failure ]
 crbug.com/626703 [ Mac10.13 ] external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/mediastreamaudiosourcenode-routing.html [ Timeout ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/mediastreamaudiosourcenode-routing.html [ Timeout ]
 crbug.com/626703 [ Retina ] external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/mediastreamaudiosourcenode-routing.html [ Timeout ]
 crbug.com/626703 [ Win7 ] external/wpt/IndexedDB/structured-clone.any.html [ Timeout ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cues-sorted-before-dispatch.html [ Timeout ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/mediacapture-fromelement/ended.html [ Timeout ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cues-pause-on-exit.html [ Timeout ]
-crbug.com/626703 [ Mac10.10 ] external/wpt/workers/data-url-shared.html [ Crash ]
 crbug.com/626703 [ Win7 ] external/wpt/pointerevents/pointerevent_touch-action-svg-none-test_touch.html [ Timeout ]
 crbug.com/626703 [ Win7 ] external/wpt/pointerevents/extension/pointerevent_touch-action-pan-right-css_touch.html [ Timeout ]
 crbug.com/626703 [ Mac10.11 ] external/wpt/shape-detection/detection-security-test.https.html [ Timeout ]
@@ -3194,20 +3175,14 @@
 crbug.com/626703 external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success.https.html [ Timeout ]
 crbug.com/626703 [ Win10 ] virtual/omt-worker-fetch/external/wpt/fetch/api/request/destination/fetch-destination-worker.https.html [ Crash ]
 crbug.com/626703 external/wpt/webauthn/idlharness-manual.https.window.js [ Skip ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cues-missed.html [ Timeout ]
-crbug.com/626703 [ Retina ] virtual/at-property/external/wpt/css/css-properties-values-api/registered-property-cssom.html [ Timeout ]
-crbug.com/626703 [ Retina ] virtual/at-property/external/wpt/css/css-properties-values-api/registered-property-computation.html [ Timeout ]
-crbug.com/626703 [ Retina ] virtual/at-property/external/wpt/css/css-properties-values-api/conditional-rules.html [ Timeout ]
 crbug.com/983503 [ Mac ] virtual/layout_ng_fieldset/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-select-elem-001.html [ Failure ]
 crbug.com/983503 [ Mac ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-select-elem-002.html [ Failure ]
 crbug.com/983503 [ Mac ] virtual/layout_ng_fieldset/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-select-elem-002.html [ Failure ]
 crbug.com/983503 [ Mac ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-select-elem-001.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/css/css-fonts/font-family-name-025.html [ Failure ]
 crbug.com/626703 [ Mac10.11 ] external/wpt/shape-detection/detection-HTMLVideoElement-invalid-state.html [ Timeout ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/webaudio/the-audio-api/the-mediaelementaudiosourcenode-interface/mediaElementAudioSourceToScriptProcessorTest.html [ Timeout ]
 crbug.com/626703 [ Mac10.12 ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-mode.html [ Timeout ]
 crbug.com/1004760 [ Mac ] external/wpt/html/semantics/embedded-content/media-elements/ready-states/autoplay-hidden.optional.html [ Timeout ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-timestamp-events.html [ Timeout ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/start_alignment.html [ Failure ]
 crbug.com/626703 [ Retina ] external/wpt/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-top-left.html [ Timeout ]
 crbug.com/626703 external/wpt/mediacapture-streams/MediaStream-MediaElement-srcObject.https.html [ Timeout ]
@@ -4289,9 +4264,6 @@
 
 crbug.com/927477 external/wpt/import-maps/acquire-import-maps-flag/worker-request/* [ Skip ]
 
-# Disabled temporarliy until eager evaluation is fixed/re-baselined in DevTools.
-crbug.com/1043151 http/tests/devtools/console/viewport-testing/console-runtime-result-below-prompt.js [ Pass Failure ]
-
 # This test requires a special browser flag and seems not suitable for a wpt test, see bug.
 crbug.com/691944 external/wpt/service-workers/service-worker/update-after-oneday.https.html [ Skip ]
 crbug.com/691944 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/update-after-oneday.https.html [ Skip ]
@@ -5545,32 +5517,38 @@
 crbug.com/915352 [ Mac10.11 ] virtual/threaded/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-and-display-none.https.html [ Pass Failure ]
 
 # Paint Timing failures
-crbug.com/1062984 external/wpt/paint-timing/fcp-only/fcp-bg-image-set.html [ Pass Failure ]
-crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-bg-image-set.html [ Pass Failure ]
-crbug.com/1062984 external/wpt/paint-timing/fcp-only/fcp-opacity-descendant.html [ Pass Failure ]
-crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-opacity-descendant.html [ Pass Failure ]
-crbug.com/1062984 external/wpt/paint-timing/fcp-only/fcp-opacity.html [ Pass Failure ]
-crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-opacity.html [ Pass Failure ]
-crbug.com/1062984 external/wpt/paint-timing/fcp-only/fcp-pseudo-element-display.html [ Pass Failure ]
-crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-pseudo-element-display.html [ Pass Failure ]
-crbug.com/1062984 external/wpt/paint-timing/fcp-only/fcp-pseudo-element-opacity.html [ Pass Failure ]
-crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-pseudo-element-opacity.html [ Pass Failure ]
 crbug.com/1062984 external/wpt/paint-timing/border-image.html [ Pass Failure ]
-crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/border-image.html [ Pass Failure ]
-crbug.com/1062984 external/wpt/paint-timing/fcp-only/fcp-video-poster.html [ Pass Failure ]
-crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-video-poster.html [ Pass Failure ]
-crbug.com/1062984 external/wpt/paint-timing/replaced-content-image.html [ Pass Failure ]
-crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/replaced-content-image.html [ Pass Failure ]
-crbug.com/1062984 external/wpt/paint-timing/mask-image.html [ Pass Failure ]
-crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/mask-image.html [ Pass Failure ]
-crbug.com/1062984 external/wpt/paint-timing/fcp-only/fcp-video-frame.html [ Timeout Failure Pass ]
-crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-video-frame.html [ Timeout Failure Pass ]
-crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-gradient.html [ Pass Failure ]
-crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-canvas-context.html [ Pass Failure ]
+crbug.com/1062984 external/wpt/paint-timing/fcp-only/fcp-bg-image-set.html [ Pass Failure ]
 crbug.com/1062984 external/wpt/paint-timing/fcp-only/fcp-invisible-3d-rotate-descendant.html [ Pass Failure ]
-crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-invisible-3d-rotate-descendant.html [ Pass Failure ]
+crbug.com/1062984 external/wpt/paint-timing/fcp-only/fcp-opacity-descendant.html [ Pass Failure ]
+crbug.com/1062984 external/wpt/paint-timing/fcp-only/fcp-opacity.html [ Pass Failure ]
+crbug.com/1062984 external/wpt/paint-timing/fcp-only/fcp-pseudo-element-display.html [ Pass Failure ]
+crbug.com/1062984 external/wpt/paint-timing/fcp-only/fcp-pseudo-element-opacity.html [ Pass Failure ]
+crbug.com/1062984 external/wpt/paint-timing/fcp-only/fcp-video-frame.html [ Timeout Failure Pass ]
+crbug.com/1062984 external/wpt/paint-timing/fcp-only/fcp-video-poster.html [ Pass Failure ]
 crbug.com/1062984 external/wpt/paint-timing/fcp-only/fcp-whitespace.html [ Pass Failure ]
+crbug.com/1062984 external/wpt/paint-timing/mask-image.html [ Pass Failure ]
+crbug.com/1062984 external/wpt/paint-timing/replaced-content-image.html [ Pass Failure ]
+crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/border-image.html [ Pass Failure ]
+crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-bg-image-set.html [ Pass Failure ]
+crbug.com/1062985 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-bg-image-two-steps.html [ Pass Failure ]
+crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-canvas-context.html [ Pass Failure ]
+crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-gradient.html [ Pass Failure ]
+crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-invisible-3d-rotate-descendant.html [ Pass Failure ]
+crbug.com/1062985 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-invisible-scale.html [ Pass Failure ]
+crbug.com/1062985 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-invisible-text.html [ Pass Failure ]
+crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-opacity-descendant.html [ Pass Failure ]
+crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-opacity.html [ Pass Failure ]
+crbug.com/1062985 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-out-of-bounds.html [ Pass Failure ]
+crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-pseudo-element-display.html [ Pass Failure ]
+crbug.com/1062985 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-pseudo-element-image.html [ Pass Failure ]
+crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-pseudo-element-opacity.html [ Pass Failure ]
+crbug.com/1062985 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-pseudo-element-visibility.html [ Pass Failure ]
+crbug.com/1062985 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-video-frame.html [ Pass Failure Timeout ]
+crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-video-poster.html [ Pass Failure ]
 crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/fcp-only/fcp-whitespace.html [ Pass Failure ]
+crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/mask-image.html [ Pass Failure ]
+crbug.com/1062984 virtual/paint-timing/external/wpt/paint-timing/replaced-content-image.html [ Pass Failure ]
 
 # Sheriff 2019-03-01
 crbug.com/937416 http/tests/devtools/resource-tree/resource-tree-htmlimports.js [ Pass Failure ]
@@ -5701,7 +5679,6 @@
 crbug.com/954297 [ Win ] http/tests/devtools/oopif/oopif-presentation-console-messages.js [ Pass Crash Timeout ]
 
 # Sheriff 2019-04-22
-crbug.com/954319 [ Linux ] http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-in-multiple-workers.js [ Pass Timeout ]
 crbug.com/954998 [ Mac ] http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-end-to-end.js [ Pass Timeout ]
 
 # Sheriff 2019-04-25
@@ -6116,14 +6093,6 @@
 # Sheriff 2019-10-31
 crbug.com/1020036 [ Debug ] external/wpt/lifecycle/freeze.html [ Failure Pass ]
 
-# Temporarily disabled for landing productregistry removal in devtools
-crbug.com/1011466 http/tests/devtools/console/console-format-classes.js [ Pass Failure ]
-crbug.com/1011466 http/tests/devtools/modules-load-initial.js [ Pass Failure ]
-crbug.com/1011466 http/tests/devtools/tracing/timeline-misc/timeline-aggregated-details.js [ Pass Failure ]
-crbug.com/1011466 http/tests/devtools/tracing/timeline-network/timeline-network-resource-details.js [ Pass Failure ]
-crbug.com/1011466 virtual/threaded/http/tests/devtools/tracing/timeline-misc/timeline-aggregated-details.js [ Pass Failure ]
-crbug.com/1011466 virtual/threaded/http/tests/devtools/tracing/timeline-network/timeline-network-resource-details.js [ Pass Failure ]
-
 # Tests that json modules load correctly within the context of service workers
 crbug.com/967018 external/wpt/html/semantics/scripting-1/the-script-element/json-module/json-module-service-worker-test.https.tentative.html [ Failure ]
 crbug.com/967018 virtual/streaming-preload/external/wpt/html/semantics/scripting-1/the-script-element/json-module/json-module-service-worker-test.https.tentative.html [ Failure ]
@@ -6287,10 +6256,6 @@
 crbug.com/1041973 external/wpt/html/semantics/forms/constraints/form-validation-reportValidity.html [ Pass Failure ]
 crbug.com/1041973 virtual/web-components-v0-disabled/external/wpt/html/semantics/forms/constraints/form-validation-reportValidity.html [ Pass Failure ]
 
-# Disabled for landing devtools changes
-crbug.com/1030746 http/tests/devtools/sources/debugger-ui/sourcemap-src-not-loaded.js [ Pass Failure ]
-crbug.com/1030746 http/tests/devtools/network/load-resource-for-frontend.js [ Pass Failure ]
-
 # Disable for landing devtools changes
 crbug.com/1006759 http/tests/devtools/console/argument-hints.js [ Pass Failure ]
 
@@ -6524,6 +6489,7 @@
 # Upcoming DevTools change
 crbug.com/1006759 http/tests/devtools/profiler/cpu-profiler-save-load.js [ Pass Failure Timeout ]
 crbug.com/1006759 http/tests/devtools/profiler/heap-snapshot-loader.js [ Pass Failure ]
+crbug.com/1064472 http/tests/devtools/elements/elements-tab-stops.js [ Pass Failure ]
 
 # Flaky tests blocking WPT import
 crbug.com/1049607 external/wpt/fetch/api/response/response-error-from-stream.html [ Pass Failure ]
@@ -6693,6 +6659,3 @@
 # Sheriff 2020-03-16
 crbug.com/1061029 [ Win ] external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting.https.html [ Pass Failure ]
 crbug.com/1061029 [ Mac ] external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting.https.html [ Pass Failure ]
-
-### external/wpt/pointerevents/
-crbug.com/1063163 external/wpt/pointerevents/pointerevent_lostpointercapture_for_disconnected_node_in_shadow_dom.html [ Failure ]
diff --git a/third_party/blink/web_tests/WPTOverrideExpectations b/third_party/blink/web_tests/WPTOverrideExpectations
index 1adb6925..969bede 100644
--- a/third_party/blink/web_tests/WPTOverrideExpectations
+++ b/third_party/blink/web_tests/WPTOverrideExpectations
@@ -1009,7 +1009,6 @@
 external/wpt/picture-in-picture/shadow-dom.html [ Pass ] # wpt_subtest_failure
 external/wpt/pointerevents/extension/pointerevent_getCoalescedEvents_when_pointerlocked.html [ Failure ]
 external/wpt/pointerevents/idlharness.window.html [ Failure Pass ]
-external/wpt/pointerevents/pointerevent_lostpointercapture_for_disconnected_node_in_shadow_dom.html [ Pass Failure ]
 external/wpt/pointerevents/pointerevent_pointercapture-in-custom-element.html [ Failure ]
 external/wpt/pointerevents/pointerevent_pointercapture-in-shadow-dom.html [ Failure ]
 external/wpt/pointerevents/pointerevent_pointercapture_in_frame.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/accessibility/name-calc-recursive.html b/third_party/blink/web_tests/accessibility/name-calc-recursive.html
index eebc6ab1..140d4c3 100644
--- a/third_party/blink/web_tests/accessibility/name-calc-recursive.html
+++ b/third_party/blink/web_tests/accessibility/name-calc-recursive.html
@@ -22,6 +22,25 @@
   <table role="table"><tr><td>cell</td></tr></table>
 </div>
 
+<!-- Chrome will use a maximum of 100 descendants in the recursive name calc -->
+<a id="a-truncate-because-too-many-nodes" href="#">
+  <span>a1</span><span>2<span><span>3</span>4</span></span><span>5</span><span>6</span><span>7</span><span>8</span><span>9</span><span>0</span>
+  <span>b1</span><span>2<span><span>3</span>4</span></span><span>5</span><span>6</span><span>7</span><span>8</span><span>9</span><span>0</span>
+  <span>c1</span><span>2<span><span>3</span>4</span></span><span>5</span><span>6</span><span>7</span><span>8</span><span>9</span><span>0</span>
+  <span>d1</span><span>2<span><span>3</span>4</span></span><span>5</span><span>6</span><span>7</span><span>8</span><span>9</span><span>0</span>
+  <span>e1</span><span>2<span><span>3</span>4</span></span><span>5</span><span>6</span><span>7</span><span>8</span><span>9</span><span>0</span>
+  <span>f1</span><span>2<span><span>3</span>4</span></span><span>5</span><span>6</span><span>7</span><span>8</span><span>9</span><span>0</span>
+  <span>g1</span><span>2<span><span>3</span>4</span></span><span>5</span><span>6</span><span>7</span><span>8</span><span>9</span><span>0</span>
+  <span>h1</span><span>2<span><span>3</span>4</span></span><span>5</span><span>6</span><span>7</span><span>8</span><span>9</span><span>0</span>
+  <span>i1</span><span>2<span><span>3</span>4</span></span><span>5</span><span>6</span><span>7</span><span>8</span><span>9</span><span>0</span>
+  <span>j1</span><span>2<span><span>3</span>4</span></span><span>5</span><span>6</span><span>7</span><span>8</span><span>9</span><span>0</span>
+  <span>Extra-A</span>
+  <span>Extra-B</span>
+  <span>Extra-C</span>
+</button>
+
+
+
 <script>
 test(function(t) {
   const snippets = [
@@ -29,6 +48,7 @@
     [ 'a-dfn', 'definition' ],
     [ 'a-p', 'paragraph' ],
     [ 'a-time', '12/04/2016' ],
+    [ 'a-truncate-because-too-many-nodes', 'a1234567890b1234567890c1234567890d1234567890e1234567890f1234567890g1234567890h1234567890i1234567890j1234567890' ],
   ];
   for (let [ id, expectedName ] of snippets) {
     let a = accessibilityController.accessibleElementById(id);
diff --git a/third_party/blink/web_tests/css3/flexbox/columns-height-set-via-top-bottom.html b/third_party/blink/web_tests/css3/flexbox/columns-height-set-via-top-bottom.html
deleted file mode 100644
index a346dde..0000000
--- a/third_party/blink/web_tests/css3/flexbox/columns-height-set-via-top-bottom.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!DOCTYPE html>
-<html>
-<link href="resources/flexbox.css" rel="stylesheet">
-<style>
-.container {
-    position: relative;
-    height: 100px;
-    width: 100px;
-    border: 2px solid orange;
-}
-.flexbox {
-    flex-direction: column;
-    position: absolute;
-    top: 0;
-    right: 0;
-    bottom: 0;
-    left: 0;
-    padding: 10px;
-}
-.flexbox > :nth-child(1) {
-    background-color: lightblue;
-}
-.flexbox > :nth-child(2) {
-    background-color: lightgreen;
-}
-</style>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/check-layout-th.js"></script>
-<body onload="checkLayout('.flexbox')">
-<div id=log></div>
-
-<div class="container">
-    <div data-expected-height=100 class="flexbox column">
-        <div data-expected-height=30 data-expected-width=80 style="height: 30px"></div>
-        <div data-expected-height=50 data-expected-width=80 style="flex: 1;"></div>
-    </div>
-</div>
-
-<div class="container">
-    <div class="flexbox column wrap">
-        <div data-expected-height=50 data-expected-width=40 style="height: 50px"></div>
-        <div data-expected-height=80 data-expected-width=40 style="flex: 1 50px;"></div>
-    </div>
-</div>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/custom-elements/embedder-custom-elements-not-exposed.html b/third_party/blink/web_tests/custom-elements/embedder-custom-elements-not-exposed.html
index 3793f10d..2c1e584 100644
--- a/third_party/blink/web_tests/custom-elements/embedder-custom-elements-not-exposed.html
+++ b/third_party/blink/web_tests/custom-elements/embedder-custom-elements-not-exposed.html
@@ -35,7 +35,7 @@
   var rejectImmediatelyOrResolve =
       Promise.race([whenDefinedPromise, Promise.resolve()]);
 
-  return promise_rejects(t, 'SyntaxError', rejectImmediatelyOrResolve);
+  return promise_rejects_dom(t, 'SyntaxError', rejectImmediatelyOrResolve);
 }, 'customElements.whenDefined embedder element name');
 
 </script>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
index 13353f6c..eb26fa4 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
@@ -81315,6 +81315,30 @@
      {}
     ]
    ],
+   "css/css-text/white-space/pre-line-051.html": [
+    [
+     "css/css-text/white-space/pre-line-051.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/pre-line-052.html": [
+    [
+     "css/css-text/white-space/pre-line-052.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/white-space/pre-wrap-001.html": [
     [
      "css/css-text/white-space/pre-wrap-001.html",
@@ -81555,6 +81579,30 @@
      {}
     ]
    ],
+   "css/css-text/white-space/pre-wrap-051.html": [
+    [
+     "css/css-text/white-space/pre-wrap-051.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/pre-wrap-052.html": [
+    [
+     "css/css-text/white-space/pre-wrap-052.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/white-space/pre-wrap-float-001.html": [
     [
      "css/css-text/white-space/pre-wrap-float-001.html",
@@ -82487,6 +82535,78 @@
      {}
     ]
    ],
+   "css/css-text/white-space/white-space-pre-031.html": [
+    [
+     "css/css-text/white-space/white-space-pre-031.html",
+     [
+      [
+       "/css/css-text/white-space/reference/white-space-pre-031-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/white-space-pre-032.html": [
+    [
+     "css/css-text/white-space/white-space-pre-032.html",
+     [
+      [
+       "/css/css-text/white-space/reference/white-space-pre-031-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/white-space-pre-034.html": [
+    [
+     "css/css-text/white-space/white-space-pre-034.html",
+     [
+      [
+       "/css/css-text/white-space/reference/white-space-pre-034-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/white-space-pre-035.html": [
+    [
+     "css/css-text/white-space/white-space-pre-035.html",
+     [
+      [
+       "/css/css-text/white-space/reference/white-space-pre-031-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/white-space-pre-051.html": [
+    [
+     "css/css-text/white-space/white-space-pre-051.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/white-space-pre-052.html": [
+    [
+     "css/css-text/white-space/white-space-pre-052.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/white-space/white-space-pre-wrap-trailing-spaces-001.html": [
     [
      "css/css-text/white-space/white-space-pre-wrap-trailing-spaces-001.html",
@@ -82583,18 +82703,6 @@
      {}
     ]
    ],
-   "css/css-text/white-space/white-space-pre-wrap-trailing-spaces-009.html": [
-    [
-     "css/css-text/white-space/white-space-pre-wrap-trailing-spaces-009.html",
-     [
-      [
-       "/css/css-text/white-space/reference/white-space-pre-wrap-trailing-spaces-004-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
    "css/css-text/white-space/white-space-pre-wrap-trailing-spaces-010.html": [
     [
      "css/css-text/white-space/white-space-pre-wrap-trailing-spaces-010.html",
@@ -82655,6 +82763,174 @@
      {}
     ]
    ],
+   "css/css-text/white-space/ws-break-spaces-applies-to-001.html": [
+    [
+     "css/css-text/white-space/ws-break-spaces-applies-to-001.html",
+     [
+      [
+       "/css/css-text/white-space/reference/ws-break-spaces-applies-to-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/ws-break-spaces-applies-to-002.html": [
+    [
+     "css/css-text/white-space/ws-break-spaces-applies-to-002.html",
+     [
+      [
+       "/css/css-text/white-space/reference/ws-break-spaces-applies-to-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/ws-break-spaces-applies-to-003.html": [
+    [
+     "css/css-text/white-space/ws-break-spaces-applies-to-003.html",
+     [
+      [
+       "/css/css-text/white-space/reference/ws-break-spaces-applies-to-003-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/ws-break-spaces-applies-to-005.html": [
+    [
+     "css/css-text/white-space/ws-break-spaces-applies-to-005.html",
+     [
+      [
+       "/css/css-text/white-space/reference/ws-break-spaces-applies-to-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/ws-break-spaces-applies-to-006.html": [
+    [
+     "css/css-text/white-space/ws-break-spaces-applies-to-006.html",
+     [
+      [
+       "/css/css-text/white-space/reference/ws-break-spaces-applies-to-006-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/ws-break-spaces-applies-to-007.html": [
+    [
+     "css/css-text/white-space/ws-break-spaces-applies-to-007.html",
+     [
+      [
+       "/css/css-text/white-space/reference/ws-break-spaces-applies-to-006-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/ws-break-spaces-applies-to-008.html": [
+    [
+     "css/css-text/white-space/ws-break-spaces-applies-to-008.html",
+     [
+      [
+       "/css/css-text/white-space/reference/ws-break-spaces-applies-to-006-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/ws-break-spaces-applies-to-009.html": [
+    [
+     "css/css-text/white-space/ws-break-spaces-applies-to-009.html",
+     [
+      [
+       "/css/css-text/white-space/reference/ws-break-spaces-applies-to-006-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/ws-break-spaces-applies-to-010.html": [
+    [
+     "css/css-text/white-space/ws-break-spaces-applies-to-010.html",
+     [
+      [
+       "/css/css-text/white-space/reference/ws-break-spaces-applies-to-006-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/ws-break-spaces-applies-to-011.html": [
+    [
+     "css/css-text/white-space/ws-break-spaces-applies-to-011.html",
+     [
+      [
+       "/css/css-text/white-space/reference/ws-break-spaces-applies-to-006-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/ws-break-spaces-applies-to-012.html": [
+    [
+     "css/css-text/white-space/ws-break-spaces-applies-to-012.html",
+     [
+      [
+       "/css/css-text/white-space/reference/ws-break-spaces-applies-to-012-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/ws-break-spaces-applies-to-013.html": [
+    [
+     "css/css-text/white-space/ws-break-spaces-applies-to-013.html",
+     [
+      [
+       "/css/css-text/white-space/reference/ws-break-spaces-applies-to-012-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/ws-break-spaces-applies-to-014.html": [
+    [
+     "css/css-text/white-space/ws-break-spaces-applies-to-014.html",
+     [
+      [
+       "/css/css-text/white-space/reference/ws-break-spaces-applies-to-006-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/ws-break-spaces-applies-to-015.html": [
+    [
+     "css/css-text/white-space/ws-break-spaces-applies-to-015.html",
+     [
+      [
+       "/css/css-text/white-space/reference/ws-break-spaces-applies-to-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/word-boundary/word-boundary-001.html": [
     [
      "css/css-text/word-boundary/word-boundary-001.html",
@@ -153202,6 +153478,12 @@
    "css/css-text/white-space/reference/white-space-pre-011-ref.html": [
     []
    ],
+   "css/css-text/white-space/reference/white-space-pre-031-ref.html": [
+    []
+   ],
+   "css/css-text/white-space/reference/white-space-pre-034-ref.html": [
+    []
+   ],
    "css/css-text/white-space/reference/white-space-pre-wrap-trailing-spaces-001-ref.html": [
     []
    ],
@@ -153223,6 +153505,18 @@
    "css/css-text/white-space/reference/white-space-zero-fontsize-002-ref.html": [
     []
    ],
+   "css/css-text/white-space/reference/ws-break-spaces-applies-to-001-ref.html": [
+    []
+   ],
+   "css/css-text/white-space/reference/ws-break-spaces-applies-to-003-ref.html": [
+    []
+   ],
+   "css/css-text/white-space/reference/ws-break-spaces-applies-to-006-ref.html": [
+    []
+   ],
+   "css/css-text/white-space/reference/ws-break-spaces-applies-to-012-ref.html": [
+    []
+   ],
    "css/css-text/white-space/seg-break-transformation-001-expected.txt": [
     []
    ],
@@ -161239,6 +161533,9 @@
    "domxpath/README.md": [
     []
    ],
+   "domxpath/callback-interface-expected.txt": [
+    []
+   ],
    "domxpath/helpers.js": [
     []
    ],
@@ -238530,6 +238827,12 @@
      {}
     ]
    ],
+   "domxpath/callback-interface.html": [
+    [
+     "domxpath/callback-interface.html",
+     {}
+    ]
+   ],
    "domxpath/document.tentative.html": [
     [
      "domxpath/document.tentative.html",
@@ -413444,7 +413747,7 @@
    "support"
   ],
   "css/css-text/line-breaking/reference/line-breaking-atomic-005-ref.html": [
-   "e22d482299b55cc1f5c833eb75b2b635c17c91ab",
+   "bd88329ad0eac9866ea4b195ebad6327ec83007c",
    "support"
   ],
   "css/css-text/line-breaking/reference/line-breaking-atomic-007-ref.html": [
@@ -416391,6 +416694,14 @@
    "8dd08d80992e6d6310a6697f84c2533d399c8e5e",
    "reftest"
   ],
+  "css/css-text/white-space/pre-line-051.html": [
+   "2a918fc28e0348d8cdcae7f18353d4d5e0233b55",
+   "reftest"
+  ],
+  "css/css-text/white-space/pre-line-052.html": [
+   "76eea859726c35eac573fcf8081f16497b9cf6e8",
+   "reftest"
+  ],
   "css/css-text/white-space/pre-line-br-with-whitespace-child-crash.html": [
    "c7e1855f9794f42f965e64fc390e1b50b83059fb",
    "crashtest"
@@ -416479,6 +416790,14 @@
    "ec3e8621159a28103527809c582e1ac0ea365bcc",
    "reftest"
   ],
+  "css/css-text/white-space/pre-wrap-051.html": [
+   "19d7f4dbbc03ccb1ccadd6b759bc4730839a3d25",
+   "reftest"
+  ],
+  "css/css-text/white-space/pre-wrap-052.html": [
+   "4794f52e16c0f9b9b45f4a42aa0664a29deac4cc",
+   "reftest"
+  ],
   "css/css-text/white-space/pre-wrap-float-001.html": [
    "af29b0505e0eefbab09b011798c0dd6136598cca",
    "reftest"
@@ -416731,6 +417050,14 @@
    "c3aecc8dfbd241d29f08266bb122fbcb5cd442e3",
    "support"
   ],
+  "css/css-text/white-space/reference/white-space-pre-031-ref.html": [
+   "93181354ba29b09f7f4c8ae0e98d21ce5d2703f3",
+   "support"
+  ],
+  "css/css-text/white-space/reference/white-space-pre-034-ref.html": [
+   "b920bdb111274bac29825e53b68005e1bf7ad6e9",
+   "support"
+  ],
   "css/css-text/white-space/reference/white-space-pre-wrap-trailing-spaces-001-ref.html": [
    "8db147766bc3fa769df5601b8b32964fb59bcbc1",
    "support"
@@ -416759,6 +417086,22 @@
    "6d2134ac4da3fc7bc3ce78fea834f2037e47cf6c",
    "support"
   ],
+  "css/css-text/white-space/reference/ws-break-spaces-applies-to-001-ref.html": [
+   "842f46913d1c705e5c648c0b01e4f184d313ef5f",
+   "support"
+  ],
+  "css/css-text/white-space/reference/ws-break-spaces-applies-to-003-ref.html": [
+   "3bb361776040c1f50b35d9400e67d967c05dde4c",
+   "support"
+  ],
+  "css/css-text/white-space/reference/ws-break-spaces-applies-to-006-ref.html": [
+   "d8a5b90638eea73d82dcb8c86d1f88a5910ea84f",
+   "support"
+  ],
+  "css/css-text/white-space/reference/ws-break-spaces-applies-to-012-ref.html": [
+   "942bd8fe92d2973dd568eabbc2b959c9b80c628c",
+   "support"
+  ],
   "css/css-text/white-space/seg-break-transformation-000.html": [
    "cb7b0f30b566320c1022f61fde44bc09695badb1",
    "testharness"
@@ -417091,6 +417434,30 @@
    "88b9894d13776605ab5d2c9b5e38e13db80168de",
    "reftest"
   ],
+  "css/css-text/white-space/white-space-pre-031.html": [
+   "c96f51eefae610feaff1a12bd604c211e50c4207",
+   "reftest"
+  ],
+  "css/css-text/white-space/white-space-pre-032.html": [
+   "7c066b767034f838247b7e1e97b41d0dc9250479",
+   "reftest"
+  ],
+  "css/css-text/white-space/white-space-pre-034.html": [
+   "29bf8a53a6389b513c63b1344fba735a18a6e5fc",
+   "reftest"
+  ],
+  "css/css-text/white-space/white-space-pre-035.html": [
+   "2723c5ce67c380a9d6104e624c130325788c29b2",
+   "reftest"
+  ],
+  "css/css-text/white-space/white-space-pre-051.html": [
+   "b6879133cc38cef498664ac911e2181477bc9664",
+   "reftest"
+  ],
+  "css/css-text/white-space/white-space-pre-052.html": [
+   "a2f03ef50c06393ee8655e7e90405217b3590cf0",
+   "reftest"
+  ],
   "css/css-text/white-space/white-space-pre-wrap-trailing-spaces-001.html": [
    "82fbfbc976cd02629d1180e4470170a533aa27e9",
    "reftest"
@@ -417123,16 +417490,12 @@
    "75d8b2000648b339416c469e22b57230db224cbb",
    "reftest"
   ],
-  "css/css-text/white-space/white-space-pre-wrap-trailing-spaces-009.html": [
-   "fad383233b31b29633bc08ec61472f179e74555f",
-   "reftest"
-  ],
   "css/css-text/white-space/white-space-pre-wrap-trailing-spaces-010.html": [
-   "ba0c5364884acf7afc87140ea36cdd68e590150a",
+   "55e2437198fc6f97c5b41c602dced36257c23a79",
    "reftest"
   ],
   "css/css-text/white-space/white-space-pre-wrap-trailing-spaces-011.html": [
-   "00f97f0360977a582e2e16057445dc47f34198e8",
+   "e54475ce44dcc12cd05a993ec92e7a18aac36bc7",
    "reftest"
   ],
   "css/css-text/white-space/white-space-wrap-after-nowrap-001.html": [
@@ -417147,6 +417510,62 @@
    "1b8d3b7eb60f07d12ea87d3464c228269262ec7d",
    "reftest"
   ],
+  "css/css-text/white-space/ws-break-spaces-applies-to-001.html": [
+   "fb0de71551a28be1491b3e5a2228873552a50f96",
+   "reftest"
+  ],
+  "css/css-text/white-space/ws-break-spaces-applies-to-002.html": [
+   "4777ccba8fb4605f9196d22cb1109d3b1ac1037a",
+   "reftest"
+  ],
+  "css/css-text/white-space/ws-break-spaces-applies-to-003.html": [
+   "14306d8a74eae53833873b2f5c8da1fe035c2dc9",
+   "reftest"
+  ],
+  "css/css-text/white-space/ws-break-spaces-applies-to-005.html": [
+   "38974e0086618c4bd1e9410323263446d9d3fc17",
+   "reftest"
+  ],
+  "css/css-text/white-space/ws-break-spaces-applies-to-006.html": [
+   "110feb52c9657db2e710cdd0efbfccf17a21fa0e",
+   "reftest"
+  ],
+  "css/css-text/white-space/ws-break-spaces-applies-to-007.html": [
+   "6c99a930d00c58825e72f441a6e54e7908decb85",
+   "reftest"
+  ],
+  "css/css-text/white-space/ws-break-spaces-applies-to-008.html": [
+   "007c95a9a0d4d9754653a2bf53441b712bb99698",
+   "reftest"
+  ],
+  "css/css-text/white-space/ws-break-spaces-applies-to-009.html": [
+   "6bd4dbb346f60518bb60d14a70149be0eb5de93b",
+   "reftest"
+  ],
+  "css/css-text/white-space/ws-break-spaces-applies-to-010.html": [
+   "caf1e89eb9fd3ebbd33534817185e7e8e53a6790",
+   "reftest"
+  ],
+  "css/css-text/white-space/ws-break-spaces-applies-to-011.html": [
+   "df43ab43a153970a8989c21db28e147848a52af8",
+   "reftest"
+  ],
+  "css/css-text/white-space/ws-break-spaces-applies-to-012.html": [
+   "540ce4a7e95c111bb99b2aa7bfa89e08aa8906a0",
+   "reftest"
+  ],
+  "css/css-text/white-space/ws-break-spaces-applies-to-013.html": [
+   "5274bd7be276cf072cac755951155f88aa9d7c5b",
+   "reftest"
+  ],
+  "css/css-text/white-space/ws-break-spaces-applies-to-014.html": [
+   "1454b3fcf53fbfca22b312d684fd403632923190",
+   "reftest"
+  ],
+  "css/css-text/white-space/ws-break-spaces-applies-to-015.html": [
+   "b8634bbaef14a0bf0a73f3d5a2c26f877ab231f5",
+   "reftest"
+  ],
   "css/css-text/word-boundary/reference/word-boundary-001-ref.html": [
    "620079f6ad48bba3d65a9bcd92edfd717228b973",
    "support"
@@ -435584,7 +436003,7 @@
    "support"
   ],
   "css/cssom-view/MediaQueryList-addListener-handleEvent.html": [
-   "30c8cf2de557db73d0222ea33e1a4702ebaec8b6",
+   "43b144a9ae2d709233f046b298fbaefec4861944",
    "testharness"
   ],
   "css/cssom-view/MediaQueryList-addListener-removeListener-expected.txt": [
@@ -448080,7 +448499,7 @@
    "testharness"
   ],
   "dom/nodes/Node-lookupNamespaceURI.html": [
-   "f5401e54723b492b942f2a127a40a3b7328e97ca",
+   "74c1ac8bd755fbe5388a666954fcf22c4b6665e1",
    "testharness"
   ],
   "dom/nodes/Node-lookupPrefix.xhtml": [
@@ -448867,6 +449286,14 @@
    "41522edf05f8bc752408eb2cdde10a05bd211a7a",
    "testharness"
   ],
+  "domxpath/callback-interface-expected.txt": [
+   "a89cd980a750fb8a20e48b4cde3f25913ed5ed41",
+   "support"
+  ],
+  "domxpath/callback-interface.html": [
+   "4596814c34dfcfa9bb615fe207f3523511207e6d",
+   "testharness"
+  ],
   "domxpath/document.tentative.html": [
    "b75c0f0d66dacb47a825ae2aa00bb63a19616433",
    "testharness"
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/columns-height-set-via-top-bottom.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/columns-height-set-via-top-bottom.html
new file mode 100644
index 0000000..80cdcc69
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/columns-height-set-via-top-bottom.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+<title>Proper size of flex item in a column flexbox with height set via top/bottom.</title>
+<link href="support/flexbox.css" rel="stylesheet">
+<link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#flex-direction-property">
+<link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#propdef-flex-flow">
+<meta name="assert" content="This test ensures that when a 'display: flex' element with 'flex-flow: column' is itself sized by fitting to another container via setting position absolute + top, bottom, right, left all to 0, its flex items do not have natural size."/>
+<style>
+.container {
+    position: relative;
+    height: 100px;
+    width: 100px;
+    border: 2px solid orange;
+}
+.flexbox {
+    flex-direction: column;
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    padding: 10px;
+}
+.flexbox > :nth-child(1) {
+    background-color: lightblue;
+}
+.flexbox > :nth-child(2) {
+    background-color: lightgreen;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<body onload="checkLayout('.flexbox')">
+<div id=log></div>
+
+<div class="container">
+    <div data-expected-height=100 class="flexbox column">
+        <div data-expected-height=30 data-expected-width=80 style="height: 30px"></div>
+        <div data-expected-height=50 data-expected-width=80 style="flex: 1;"></div>
+    </div>
+</div>
+
+<div class="container">
+    <div class="flexbox column wrap">
+        <div data-expected-height=50 data-expected-width=40 style="height: 50px"></div>
+        <div data-expected-height=80 data-expected-width=40 style="flex: 1 50px;"></div>
+    </div>
+</div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/font-face-range-order.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/font-face-range-order.html
new file mode 100644
index 0000000..ed0508a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/font-face-range-order.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Fonts Module Level 3: Order of values in @font-face range descriptors</title>
+<link rel="author" title="Dominik Röttsches" href="mailto:drott@chromium.org"/>
+<link rel="help" href="https://drafts.csswg.org/css-fonts/#font-prop-desc">
+<meta name="assert" content="Allow ranges to be specified in reverse order.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<style>
+@font-face {
+font-family: reversed-range-test;
+font-stretch: 200% 50%;
+font-style: oblique 90deg -90deg;
+font-weight: 900 100;
+src: local(Ahem);
+}
+</style>
+<script>
+  async_test(function(t) {
+    document.fonts.load("12px reversed-range-test").then((fonts) => {
+      t.step( () => {
+        assert_equals(fonts[0].stretch, "200% 50%", "Stretch value must be returned as specified.");
+        assert_equals(fonts[0].style, "oblique 90deg -90deg", "Style value must be returned as specified.");
+        assert_equals(fonts[0].weight, "900 100", "Weight value must be returned as specified.");
+      });
+      t.done()
+    })});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/matching/range-descriptor-reversed-ref.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/matching/range-descriptor-reversed-ref.html
new file mode 100644
index 0000000..e59dc50
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/matching/range-descriptor-reversed-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<style>
+    @font-face {
+    font-family: variable_axes;
+    src: url("resources/variabletest_matching.ttf");
+    }
+
+    body {
+    font-family: variable_axes, sans-serif;
+    font-size: 80px;
+    }
+</style>
+<span>0p;/</span>
+<span>8i;,</span>
+<script>
+    document.fonts.ready.then(
+        () => { document.documentElement.classList.remove("reftest-wait"); });
+</script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/matching/range-descriptor-reversed.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/matching/range-descriptor-reversed.html
new file mode 100644
index 0000000..71b424d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/matching/range-descriptor-reversed.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<meta charset="utf-8">
+<title>CSS Fonts Module Level 3: Property descriptor ranges</title>
+<link rel="author" title="Dominik Röttsches" href="mailto:drott@chromium.org"/>
+<link rel="help" href="https://drafts.csswg.org/css-fonts/#font-prop-desc">
+<link rel="match" href="range-descriptor-reversed-ref.html">
+<meta name="assert" content="User agents must swap the computed value of the startpoint and endpoint of the range in order to forbid decreasing ranges.">
+</head>
+<link rel="stylesheet" href="font-matching.css">
+<style>
+    @font-face {
+    font-family: variable_axes;
+    src: url("resources/variabletest_matching.ttf");
+    font-stretch: 200% 50%;
+    font-style: oblique 90deg -90deg;
+    font-weight: 900 100;
+    }
+</style>
+<link rel="match" href="stretch-distance-over-weight-distance-ref.html">
+<span id="stretch_style_weight_1">MNOP</span>
+<span id="stretch_style_weight_9">MNOP</span>
+<script>
+    document.fonts.ready.then(
+        () => { document.documentElement.classList.remove("reftest-wait"); });
+</script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/at-font-face-descriptors-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/at-font-face-descriptors-expected.txt
index d1372c9..0a6825e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/at-font-face-descriptors-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/at-font-face-descriptors-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 84 tests; 66 PASS, 18 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 84 tests; 69 PASS, 15 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS font-weight(valid): 'normal' keyword: normal
 PASS font-weight(valid): 'bold' keyword: bold
 FAIL font-weight(invalid): 'lighter' keyword iside @font-face: lighter assert_equals: No properties should be set. expected "" but got "lighter"
@@ -25,7 +25,7 @@
 PASS font-weight(valid): Lower bound calc(): calc(100 + 100) 400
 PASS font-weight(valid): Upper bound calc(): 200 calc(200 + 200)
 PASS font-weight(valid): Both bounds are calc(): calc(100 + 100) calc(200 + 200)
-FAIL font-weight(valid): Bounds out of order are valid: 400 200 assert_not_equals: Valid value should be accepted. got disallowed value ""
+PASS font-weight(valid): Bounds out of order are valid: 400 200
 PASS font-weight(invalid): Extra content after upper bound: 100 200 300
 PASS font-stretch(valid): 'ultra-condensed' keyword: ultra-condensed
 PASS font-stretch(valid): 'extra-condensed' keyword: extra-condensed
@@ -57,7 +57,7 @@
 FAIL font-stretch(valid): Lower bound calc(): calc(10% + 10%) 30% assert_equals: Unexpected resulting value. expected "20% 30%" but got "calc(20%) 30%"
 FAIL font-stretch(valid): Upper bound calc(): 10% calc(10% + 10%) assert_equals: Unexpected resulting value. expected "10% 20%" but got "10% calc(20%)"
 FAIL font-stretch(valid): Both bounds are calc(): calc(10% + 10%) calc(20% + 20%) assert_equals: Unexpected resulting value. expected "20% 40%" but got "calc(20%) calc(40%)"
-FAIL font-stretch(valid): Bounds out of order: 200% 100% assert_not_equals: Valid value should be accepted. got disallowed value ""
+PASS font-stretch(valid): Bounds out of order: 200% 100%
 PASS font-stretch(invalid): Extra content after upper bound: 100% 200% 300%
 PASS font-style(valid): 'normal' keyword: normal
 PASS font-style(valid): 'italic' keyword: italic
@@ -80,7 +80,7 @@
 PASS font-style(valid): Simple range: oblique 10deg 20deg
 FAIL font-style(valid): Simple range with equal upper and lower bounds: oblique 10deg 10deg assert_equals: Unexpected resulting value. expected "oblique 10deg" but got "oblique 10deg 10deg"
 FAIL font-style(valid): Simple range with  default angle for both bounds: oblique 20deg 20deg assert_equals: Unexpected resulting value. expected "oblique" but got "oblique 20deg 20deg"
-FAIL font-style(valid): Bounds out of order: oblique 20deg 10deg assert_not_equals: Valid value should be accepted. got disallowed value ""
+PASS font-style(valid): Bounds out of order: oblique 20deg 10deg
 PASS font-style(invalid): Lower bound out of range: oblique -100deg 20deg
 PASS font-style(invalid): Upper bound out of range: oblique 20deg 100deg
 PASS font-style(invalid): Extra content after upper bound: oblique 10deg 20deg 30deg
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/at-font-face-descriptors.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/at-font-face-descriptors.html
index cd1c22b..7083639d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/at-font-face-descriptors.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/at-font-face-descriptors.html
@@ -88,7 +88,7 @@
             { value: "calc(100 + 100) 400",             isValid: true,  expectedValue: "200 400", description: "Lower bound calc()" },
             { value: "200 calc(200 + 200)",             isValid: true,  expectedValue: "200 400", description: "Upper bound calc()" },
             { value: "calc(100 + 100) calc(200 + 200)", isValid: true,  expectedValue: "200 400", description: "Both bounds are calc()" },
-            { value: "400 200",                         isValid: true,  expectedValue: "200 400", description: "Bounds out of order are valid" },
+            { value: "400 200",                         isValid: true,  expectedValue: "400 200", description: "Bounds out of order are valid" },
             { value: "100 200 300",                     isValid: false, description: "Extra content after upper bound" },
         ]);
 
@@ -130,7 +130,7 @@
             { value: "calc(10% + 10%) 30%",             isValid: true,  expectedValue: "20% 30%", description: "Lower bound calc()" },
             { value: "10% calc(10% + 10%)",             isValid: true,  expectedValue: "10% 20%", description: "Upper bound calc()" },
             { value: "calc(10% + 10%) calc(20% + 20%)", isValid: true,  expectedValue: "20% 40%", description: "Both bounds are calc()" },
-            { value: "200% 100%",                       isValid: true,  expectedValue: "100% 200%", description: "Bounds out of order" },
+            { value: "200% 100%",                       isValid: true,  expectedValue: "200% 100%", description: "Bounds out of order" },
             { value: "100% 200% 300%",                  isValid: false, description: "Extra content after upper bound" },
         ]);
 
@@ -161,7 +161,7 @@
             { value: "oblique 10deg 20deg",         isValid: true,  description: "Simple range" },
             { value: "oblique 10deg 10deg",         isValid: true,  expectedValue: "oblique 10deg", description: "Simple range with equal upper and lower bounds" },
             { value: "oblique 20deg 20deg",         isValid: true,  expectedValue: "oblique", description: "Simple range with  default angle for both bounds" },
-            { value: "oblique 20deg 10deg",         isValid: true,  expectedValue: "oblique 10deg 20deg", description: "Bounds out of order" },
+            { value: "oblique 20deg 10deg",         isValid: true,  expectedValue: "oblique 20deg 10deg", description: "Bounds out of order" },
             { value: "oblique -100deg 20deg",       isValid: false, description: "Lower bound out of range" },
             { value: "oblique 20deg 100deg",        isValid: false, description: "Upper bound out of range" },
             { value: "oblique 10deg 20deg 30deg",   isValid: false, description: "Extra content after upper bound" },
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/font-parse-numeric-stretch-style-weight-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/font-parse-numeric-stretch-style-weight-expected.txt
index 5e8d789..1fd5e2f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/font-parse-numeric-stretch-style-weight-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/font-parse-numeric-stretch-style-weight-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 81 tests; 78 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 81 tests; 79 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Valid value bold for font property weight used for styling.
 PASS Valid value 700 for font property weight used for styling.
 PASS Valid value 900 for font property weight used for styling.
@@ -37,7 +37,7 @@
 PASS Valid value 100 400 matches 100 400 for weight in @font-face.
 PASS Valid value 100 101.5 matches 100 101.5 for weight in @font-face.
 PASS Valid value 999.8 999.9 matches 999.8 999.9 for weight in @font-face.
-FAIL Valid value 500 400 matches 500 400 for weight in @font-face. Failed to set the 'weight' property on 'FontFace': Failed to set '500 400' as a property value.
+PASS Valid value 500 400 matches 500 400 for weight in @font-face.
 PASS Valid value 0% matches 0% for stretch in @font-face.
 PASS Valid value calc(0% - 10%) matches calc(-10%) for stretch in @font-face.
 PASS Valid value 100% matches 100% for stretch in @font-face.
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/reference/line-breaking-atomic-005-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/reference/line-breaking-atomic-005-ref.html
index e22d482..bd88329 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/reference/line-breaking-atomic-005-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/reference/line-breaking-atomic-005-ref.html
@@ -5,7 +5,7 @@
 <link rel=author title="Florian Rivoal" href="https://florian.rivoal.net">
 <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
 <style>
-.red {
+div {
   font: 50px/1 Ahem;
   position: absolute;
   z-index: -1;
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/pre-line-051.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/pre-line-051.html
new file mode 100644
index 0000000..2a918fc2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/pre-line-051.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text: inline starting with a collapsible white space</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-phase-2">
+  <link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+  <link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+
+  <style>
+  div
+    {
+      background-color: red;
+      color: green;
+      float: left;
+      font-family: Ahem;
+      font-size: 50px;
+      line-height: 1;
+     }
+
+  span#pre-line
+    {
+      white-space: pre-line;
+    }
+  </style>
+
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.
+
+  <div><span id="pre-line">AB&NewLine;</span><span> CD</span></div>
+
+<!--
+
+  &NewLine; == Line feed == &#x000A; == &#0010;
+
+-->
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/pre-line-052.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/pre-line-052.html
new file mode 100644
index 0000000..76eea859
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/pre-line-052.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text: inline starting with a collapsible white space</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-phase-2">
+  <link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+  <link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+
+  <style>
+  div
+    {
+      background-color: red;
+      color: green;
+      display: inline-block;
+      font-family: Ahem;
+      font-size: 50px;
+      line-height: 1;
+     }
+
+  span#pre-line
+    {
+      white-space: pre-line;
+    }
+  </style>
+
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.
+
+  <div><span id="pre-line">AB&NewLine;</span><span> CD</span></div>
+
+<!--
+
+  &NewLine; == Line feed == &#x000A; == &#0010;
+
+-->
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/pre-wrap-051.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/pre-wrap-051.html
new file mode 100644
index 0000000..19d7f4d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/pre-wrap-051.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text: inline starting with a collapsible white space</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-phase-2">
+  <link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+  <link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+
+  <style>
+  div
+    {
+      background-color: red;
+      color: green;
+      float: left;
+      font-family: Ahem;
+      font-size: 50px;
+      line-height: 1;
+     }
+
+  span#pre-wrap
+    {
+      white-space: pre-wrap;
+    }
+  </style>
+
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.
+
+  <div><span id="pre-wrap">AB&NewLine;</span><span> CD</span></div>
+
+<!--
+
+  &NewLine; == Line feed == &#x000A; == &#0010;
+
+-->
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/pre-wrap-052.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/pre-wrap-052.html
new file mode 100644
index 0000000..4794f52
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/pre-wrap-052.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text: inline starting with a collapsible white space</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-phase-2">
+  <link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+  <link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+
+  <style>
+  div
+    {
+      background-color: red;
+      color: green;
+      display: inline-block;
+      font-family: Ahem;
+      font-size: 50px;
+      line-height: 1;
+     }
+
+  span#pre-wrap
+    {
+      white-space: pre-wrap;
+    }
+  </style>
+
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.
+
+  <div><span id="pre-wrap">AB&NewLine;</span><span> CD</span></div>
+
+<!--
+
+  &NewLine; == Line feed == &#x000A; == &#0010;
+
+-->
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/white-space-pre-031-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/white-space-pre-031-ref.html
new file mode 100644
index 0000000..9318135
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/white-space-pre-031-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Reference Test</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+
+  <style>
+  div
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      margin-bottom: 0.25em;
+      width: 16ch;
+    }
+  </style>
+
+  <body>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div>Lorem ipsum. <br>
+Dolor<br>
+&nbsp;&nbsp;&nbsp;sit&nbsp;&nbsp;amet.</div>
+
+  <div>Lorem ipsum. <br>
+Dolor<br>
+&nbsp;&nbsp;&nbsp;sit&nbsp;&nbsp;amet.</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/white-space-pre-034-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/white-space-pre-034-ref.html
new file mode 100644
index 0000000..b920bdb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/white-space-pre-034-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Reference Test</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+
+  <style>
+  div
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      margin-bottom: 0.25em;
+      width: 16ch;
+    }
+  </style>
+
+  <body>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div>Lorem ipsum.&nbsp;&nbsp;&nbsp;&nbsp;<br>
+&nbsp;&nbsp;Dolor<br>
+&nbsp;&nbsp;&nbsp;sit&nbsp;&nbsp;amet.</div>
+
+  <div>Lorem ipsum.&nbsp;&nbsp;&nbsp;&nbsp;<br>
+&nbsp;&nbsp;Dolor<br>
+&nbsp;&nbsp;&nbsp;sit&nbsp;&nbsp;amet.</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/ws-break-spaces-applies-to-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/ws-break-spaces-applies-to-001-ref.html
new file mode 100644
index 0000000..842f469
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/ws-break-spaces-applies-to-001-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Reference Test</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+
+  <style>
+  div
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      margin-bottom: 0.25em;
+      white-space: pre;
+      width: 4ch;
+    }
+  </style>
+
+  <body>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div>123
+   8</div>
+
+  <div>123
+   8</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/ws-break-spaces-applies-to-003-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/ws-break-spaces-applies-to-003-ref.html
new file mode 100644
index 0000000..3bb36177
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/ws-break-spaces-applies-to-003-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Reference Test</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+
+  <style>
+  li
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      margin-bottom: 0.25em;
+      white-space: pre;
+      width: 4ch;
+    }
+  </style>
+
+  <body>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles (both preceded with a small filled disc) are laid out identically.
+
+  <ul><li>123
+   8</ul>
+
+  <ul><li>123
+   8</ul>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/ws-break-spaces-applies-to-006-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/ws-break-spaces-applies-to-006-ref.html
new file mode 100644
index 0000000..d8a5b906
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/ws-break-spaces-applies-to-006-ref.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Reference Test</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+
+  <style>
+  table
+    {
+      border: black solid 2px;
+      border-spacing: 0px;
+      font-family: monospace;
+      font-size: 32px;
+      margin-bottom: 0.25em;
+      white-space: pre;
+      width: 4ch;
+    }
+
+  td
+    {
+      padding: 0px;
+    }
+  </style>
+
+  <body>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <table><tr><td>123
+   8</table>
+
+  <table><tr><td>123
+   8</table>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/ws-break-spaces-applies-to-012-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/ws-break-spaces-applies-to-012-ref.html
new file mode 100644
index 0000000..942bd8f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/ws-break-spaces-applies-to-012-ref.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Reference Test</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+
+  <style>
+  table
+    {
+      border: black solid 2px;
+      border-spacing: 0px;
+      font-family: monospace;
+      font-size: 32px;
+      margin-bottom: 0.25em;
+    }
+
+  td
+    {
+      padding: 0px;
+      width: 4ch;
+      white-space: normal;
+    }
+  </style>
+
+  <body>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <table><tr><td>123
+   8</table>
+
+  <table><tr><td>123
+   8</table>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-031.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-031.html
new file mode 100644
index 0000000..c96f51ee
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-031.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text: wrappable ('normal') inline inside a 'white-space: pre' block</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/white-space-pre-031-ref.html">
+
+  <style>
+  div
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      margin-bottom: 0.25em;
+      width: 16ch;
+    }
+
+  div#test
+    {
+      white-space: pre;
+    }
+
+  span
+    {
+      white-space: normal;
+    }
+
+  div#reference
+    {
+      white-space: normal;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="test">Lorem ips<span>um. Dolo</span>r
+   sit&Tab;amet.</div>
+
+  <div id="reference">Lorem ipsum. <br>
+Dolor<br>
+&nbsp;&nbsp;&nbsp;sit&nbsp;&nbsp;amet.</div>
+
+<!--
+
+  <div id="test">Lorem ipsum. Dolor
+                             ^
+                             1
+
+   sit&Tab;amet.</div>
+^^^
+123
+
+Since 'tab-size' is by default 8,
+since "sit" uses 3 characters
+and since there are 3 preserved
+white space characters before "sit",
+
+  8
+-
+  3
+-
+  3
+=====
+  2
+
+then the horizontal tabulation will use as much space
+as a sequence of 2 preserved white space characters.
+
+
+  &Tab; == Horizontal tabulation == &#x0009; == &#09;
+
+-->
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-032.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-032.html
new file mode 100644
index 0000000..7c066b7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-032.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text: wrappable ('pre-wrap') inline inside a 'white-space: pre' block</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/white-space-pre-031-ref.html">
+
+  <style>
+  div
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      margin-bottom: 0.25em;
+      width: 16ch;
+    }
+
+  div#test
+    {
+      white-space: pre;
+    }
+
+  span
+    {
+      white-space: pre-wrap;
+    }
+
+  div#reference
+    {
+      white-space: normal;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="test">Lorem ips<span>um. Dolo</span>r
+   sit&Tab;amet.</div>
+
+  <div id="reference">Lorem ipsum. <br>
+Dolor<br>
+&nbsp;&nbsp;&nbsp;sit&nbsp;&nbsp;amet.</div>
+
+<!--
+
+  <div id="test">Lorem ipsum. Dolor
+                             ^
+                             1
+
+   sit&Tab;amet.</div>
+^^^
+123
+
+
+Since 'tab-size' is by default 8,
+since "sit" uses 3 characters
+and since there are 3 preserved
+white space characters before "sit",
+
+  8
+-
+  3
+-
+  3
+=====
+  2
+
+then the horizontal tabulation will be converted
+into a sequence of 2 preserved white space characters.
+
+
+  &Tab; == Horizontal tabulation == &#x0009; == &#09;
+
+-->
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-034.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-034.html
new file mode 100644
index 0000000..29bf8a5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-034.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text: wrappable ('break-spaces') inline inside a 'white-space: pre' block</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/white-space-pre-034-ref.html">
+
+  <style>
+  div
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      margin-bottom: 0.25em;
+      width: 16ch;
+    }
+
+  div#test
+    {
+      white-space: pre;
+    }
+
+  span
+    {
+      white-space: break-spaces;
+    }
+
+  div#reference
+    {
+      white-space: normal;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="test">Lorem ips<span>um.      Dolo</span>r
+   sit&Tab;amet.</div>
+
+  <div id="reference">Lorem ipsum.&nbsp;&nbsp;&nbsp;&nbsp;<br>
+&nbsp;&nbsp;Dolor<br>
+&nbsp;&nbsp;&nbsp;sit&nbsp;&nbsp;amet.</div>
+
+<!--
+
+  <div id="test">Lorem ipsum.      Dolor
+                             ^^^^^^
+                             123456
+
+   sit&Tab;amet.</div>
+^^^
+123
+
+
+Since 'tab-size' is by default 8,
+since "sit" uses 3 characters
+and since there are 3 preserved
+white space characters before "sit",
+
+  8
+-
+  3
+-
+  3
+=====
+  2
+
+then the horizontal tabulation will use as much space
+as a sequence of 2 preserved white space characters.
+
+
+  &Tab; == Horizontal tabulation == &#x0009; == &#09;
+
+-->
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-035.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-035.html
new file mode 100644
index 0000000..2723c5c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-035.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text: wrappable ('pre-line') inline inside a 'white-space: pre' block</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/white-space-pre-031-ref.html">
+
+  <style>
+  div
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      margin-bottom: 0.25em;
+      width: 16ch;
+    }
+
+  div#test
+    {
+      white-space: pre;
+    }
+
+  span
+    {
+      white-space: pre-line;
+    }
+
+  div#reference
+    {
+      white-space: normal;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="test">Lorem ips<span>um.      Dolo</span>r
+   sit&Tab;amet.</div>
+
+  <div id="reference">Lorem ipsum. <br>
+Dolor<br>
+&nbsp;&nbsp;&nbsp;sit&nbsp;&nbsp;amet.</div>
+
+<!--
+
+  <div id="test">Lorem ipsum.      Dolor
+                             ^^^^^^
+                             123456
+
+   sit&Tab;amet.</div>
+^^^
+123
+
+
+Since 'tab-size' is by default 8,
+since "sit" uses 3 characters
+and since there are 3 preserved
+white space characters before "sit",
+
+  8
+-
+  3
+-
+  3
+=====
+  2
+
+then the horizontal tabulation will be converted
+into a sequence of 2 preserved white space characters.
+
+
+  &Tab; == Horizontal tabulation == &#x0009; == &#09;
+
+-->
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-051.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-051.html
new file mode 100644
index 0000000..b6879133
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-051.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text: inline starting with a collapsible white space</title>
+
+  <!--
+  Same as
+
+  http://test.csswg.org/suites/css-text-3_dev/nightly-unstable/html/white-space-mixed-003.htm
+
+  but more compact
+  -->
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-phase-2">
+  <link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+  <link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+
+  <style>
+  div
+    {
+      background-color: red;
+      color: green;
+      float: left;
+      font-family: Ahem;
+      font-size: 50px;
+      line-height: 1;
+     }
+
+  span#pre
+    {
+      white-space: pre;
+    }
+  </style>
+
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.
+
+  <div><span id="pre">AB&NewLine;</span><span> CD</span></div>
+
+<!--
+
+  &NewLine; == Line feed == &#x000A; == &#0010;
+
+-->
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-052.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-052.html
new file mode 100644
index 0000000..a2f03ef
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-052.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text: inline starting with a collapsible white space</title>
+
+  <!--
+  Same as
+
+  http://test.csswg.org/suites/css-text-3_dev/nightly-unstable/html/white-space-mixed-003.htm
+
+  but more compact
+  -->
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-phase-2">
+  <link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+  <link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+
+  <style>
+  div
+    {
+      background-color: red;
+      color: green;
+      display: inline-block;
+      font-family: Ahem;
+      font-size: 50px;
+      line-height: 1;
+     }
+
+  span#pre
+    {
+      white-space: pre;
+    }
+  </style>
+
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.
+
+  <div><span id="pre">AB&NewLine;</span><span> CD</span></div>
+
+<!--
+
+  &NewLine; == Line feed == &#x000A; == &#0010;
+
+-->
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-009.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-009.html
deleted file mode 100644
index fad3832..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-009.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>CSS Text test: hanging trailing spaces with white-space:pre-wrap</title>
-<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com">
-<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
-<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-pre-wrap">
-<link rel="help" title="4.1.3. Phase II: Trimming and Positioning" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2">
-<link rel="help" title="5.5. Overflow Wrapping: the overflow-wrap/word-wrap property" href="https://drafts.csswg.org/css-text-3/#overflow-wrap-property">
-<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-overflow-wrap-anywhere">
-<link rel="match" href="reference/white-space-pre-wrap-trailing-spaces-004-ref.html">
-<meta name="assert" content="Preserved white space at the end of a soft-wrapped line is hanged when white-space is pre-wrap.">
-<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
-<style>
-div {
-  font: 10px/1 Ahem;
-}
-.ref {
-  position: absolute;
-  color: red;
-  z-index: -1;
-}
-.ref span { color: green; }
-.test {
-  color: green;
-
-  width: 5ch;
-  white-space: pre-wrap;
-  overflow-wrap: anywhere;
-}
-</style>
-
-<p>This test passes if there is a green square and no red.
-<div class="ref">XX<span>XXX</span><br>X<span>XX</span>X<span>X</span><br><span>XXXXX</span><br><span>XXXXX</span><br><span>XXXXX</span></div>
-<div class="test">XX<span>    </span><span>X  X  </span></div>
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-010.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-010.html
index ba0c5364..55e24371 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-010.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-010.html
@@ -18,7 +18,7 @@
 
   width: 2ch;
   white-space: pre-wrap;
-  overflow-wrap: break-word;
+  word-break: break-all;
 }
 </style>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-011.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-011.html
index 00f97f03..e54475c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-011.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-011.html
@@ -25,7 +25,7 @@
 
   width: 5ch;
   white-space: pre-wrap;
-  overflow-wrap: break-word;
+  word-break: break-all;
 }
 </style>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-001.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-001.html
new file mode 100644
index 0000000..fb0de71
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-001.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text Test:  'white-space: break-spaces' applies to 'display: inline' element</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/ws-break-spaces-applies-to-001-ref.html">
+
+  <meta content="" name="flags">
+  <meta content="When 'white-space' is 'break-spaces', then new lines, sequence of white spaces and tabs are preserved and text can wrap, just like pre-wrap, but sequence of preserved white space always takes up space, including at the end of the line and line breaking opportunity exists after every preserved white space character, including between white space characters. In this test, 'display: inline' is tested." name="assert">
+
+
+  <!--
+
+  Testing of 'white-space: break-spaces':
+
+  ws-break-spaces-applies-to-001: display: inline
+
+  ws-break-spaces-applies-to-002: display: block
+
+  ws-break-spaces-applies-to-003: display: list-item
+
+  ws-break-spaces-applies-to-005: display: inline-block
+
+  ws-break-spaces-applies-to-006: display: table
+
+  ws-break-spaces-applies-to-007: display: inline-table
+
+  ws-break-spaces-applies-to-008: display: table-row-group
+
+  ws-break-spaces-applies-to-009: display: table-header-group
+
+  ws-break-spaces-applies-to-010: display: table-footer-group
+
+  ws-break-spaces-applies-to-011: display: table-row
+
+  ws-break-spaces-applies-to-012: display: table-column-group
+
+  ws-break-spaces-applies-to-013: display: table-column
+
+  ws-break-spaces-applies-to-014: display: table-cell
+
+  ws-break-spaces-applies-to-015: display: table-caption
+
+  -->
+
+  <style>
+  div#wrapper, div#reference
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      margin-bottom: 0.25em;
+      width: 4ch;
+    }
+
+  div#test
+    {
+      display: inline;
+      white-space: break-spaces;
+    }
+
+  div#reference
+    {
+      white-space: pre;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="wrapper"><div id="test">123    8</div></div>
+  <!--                   45678     -->
+  <!--     will wrap here ^        -->
+
+  <div id="reference">123
+   8</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-002.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-002.html
new file mode 100644
index 0000000..4777ccb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-002.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text Test: 'white-space: break-spaces' applies to 'display: block' element</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/ws-break-spaces-applies-to-001-ref.html">
+
+  <meta content="" name="flags">
+  <meta content="When 'white-space' is 'break-spaces', then new lines, sequence of white spaces and tabs are preserved and text can wrap, just like pre-wrap, but sequence of preserved white space always takes up space, including at the end of the line and line breaking opportunity exists after every preserved white space character, including between white space characters. In this test, 'display: block' is tested." name="assert">
+
+  <!--
+
+  Testing of 'white-space: break-spaces':
+
+  ws-break-spaces-applies-to-001: display: inline
+
+  ws-break-spaces-applies-to-002: display: block
+
+  ws-break-spaces-applies-to-003: display: list-item
+
+  ws-break-spaces-applies-to-005: display: inline-block
+
+  ws-break-spaces-applies-to-006: display: table
+
+  ws-break-spaces-applies-to-007: display: inline-table
+
+  ws-break-spaces-applies-to-008: display: table-row-group
+
+  ws-break-spaces-applies-to-009: display: table-header-group
+
+  ws-break-spaces-applies-to-010: display: table-footer-group
+
+  ws-break-spaces-applies-to-011: display: table-row
+
+  ws-break-spaces-applies-to-012: display: table-column-group
+
+  ws-break-spaces-applies-to-013: display: table-column
+
+  ws-break-spaces-applies-to-014: display: table-cell
+
+  ws-break-spaces-applies-to-015: display: table-caption
+
+  -->
+  <style>
+  div
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      margin-bottom: 0.25em;
+      width: 4ch;
+    }
+
+  div#test
+    {
+      display: block;
+      white-space: break-spaces;
+    }
+
+  div#reference
+    {
+      white-space: pre;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="test">123    8</div>
+  <!--              45678      -->
+ <!-- will wrap here ^         -->
+
+  <div id="reference">123
+   8</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-003.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-003.html
new file mode 100644
index 0000000..14306d8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-003.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text Test: 'white-space: break-spaces' applies to 'display: list-item' element</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/ws-break-spaces-applies-to-003-ref.html">
+
+  <meta content="" name="flags">
+  <meta content="When 'white-space' is 'break-spaces', then new lines, sequence of white spaces and tabs are preserved and text can wrap, just like pre-wrap, but sequence of preserved white space always takes up space, including at the end of the line and line breaking opportunity exists after every preserved white space character, including between white space characters. In this test, 'display: list-item' is tested." name="assert">
+
+  <!--
+
+  Testing of 'white-space: break-spaces':
+
+  ws-break-spaces-applies-to-001: display: inline
+
+  ws-break-spaces-applies-to-002: display: block
+
+  ws-break-spaces-applies-to-003: display: list-item
+
+  ws-break-spaces-applies-to-005: display: inline-block
+
+  ws-break-spaces-applies-to-006: display: table
+
+  ws-break-spaces-applies-to-007: display: inline-table
+
+  ws-break-spaces-applies-to-008: display: table-row-group
+
+  ws-break-spaces-applies-to-009: display: table-header-group
+
+  ws-break-spaces-applies-to-010: display: table-footer-group
+
+  ws-break-spaces-applies-to-011: display: table-row
+
+  ws-break-spaces-applies-to-012: display: table-column-group
+
+  ws-break-spaces-applies-to-013: display: table-column
+
+  ws-break-spaces-applies-to-014: display: table-cell
+
+  ws-break-spaces-applies-to-015: display: table-caption
+
+  -->
+
+  <style>
+  div#test, li#reference
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      margin-bottom: 0.25em;
+      width: 4ch;
+    }
+
+  div#test
+    {
+      display: list-item;
+      margin-left: 40px;
+      white-space: break-spaces;
+    }
+
+  li#reference
+    {
+      white-space: pre;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles (both preceded with a small filled disc) are laid out identically.
+
+  <div id="test">123    8</div>
+    <!--              45678      -->
+   <!-- will wrap here ^         -->
+
+  <ul><li id="reference">123
+   8</ul>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-005.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-005.html
new file mode 100644
index 0000000..38974e00
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-005.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text Test: 'white-space: break-spaces' applies to 'display: inline-block' element</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/ws-break-spaces-applies-to-001-ref.html">
+
+  <meta content="" name="flags">
+  <meta content="When 'white-space' is 'break-spaces', then new lines, sequence of white spaces and tabs are preserved and text can wrap, just like pre-wrap, but sequence of preserved white space always takes up space, including at the end of the line and line breaking opportunity exists after every preserved white space character, including between white space characters. In this test, 'display: inline-block' is tested." name="assert">
+
+  <!--
+
+  Testing of 'white-space: break-spaces':
+
+  ws-break-spaces-applies-to-001: display: inline
+
+  ws-break-spaces-applies-to-002: display: block
+
+  ws-break-spaces-applies-to-003: display: list-item
+
+  ws-break-spaces-applies-to-005: display: inline-block
+
+  ws-break-spaces-applies-to-006: display: table
+
+  ws-break-spaces-applies-to-007: display: inline-table
+
+  ws-break-spaces-applies-to-008: display: table-row-group
+
+  ws-break-spaces-applies-to-009: display: table-header-group
+
+  ws-break-spaces-applies-to-010: display: table-footer-group
+
+  ws-break-spaces-applies-to-011: display: table-row
+
+  ws-break-spaces-applies-to-012: display: table-column-group
+
+  ws-break-spaces-applies-to-013: display: table-column
+
+  ws-break-spaces-applies-to-014: display: table-cell
+
+  ws-break-spaces-applies-to-015: display: table-caption
+
+  -->
+
+  <style>
+  div
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      margin-bottom: 0.25em;
+      width: 4ch;
+    }
+
+  div#test
+    {
+      display: inline-block;
+      white-space: break-spaces;
+    }
+
+  div#reference
+    {
+      white-space: pre;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="test">123    8</div>
+  <!--              45678      -->
+ <!-- will wrap here ^         -->
+
+  <div id="reference">123
+   8</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-006.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-006.html
new file mode 100644
index 0000000..110feb5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-006.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text Test: 'white-space: break-spaces' applies to 'display: table' element</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/ws-break-spaces-applies-to-006-ref.html">
+
+  <meta content="" name="flags">
+  <meta content="When 'white-space' is 'break-spaces', then new lines, sequence of white spaces and tabs are preserved and text can wrap, just like pre-wrap, but sequence of preserved white space always takes up space, including at the end of the line and line breaking opportunity exists after every preserved white space character, including between white space characters. In this test, 'display: table' is tested." name="assert">
+
+  <!--
+
+  Testing of 'white-space: break-spaces':
+
+  ws-break-spaces-applies-to-001: display: inline
+
+  ws-break-spaces-applies-to-002: display: block
+
+  ws-break-spaces-applies-to-003: display: list-item
+
+  ws-break-spaces-applies-to-005: display: inline-block
+
+  ws-break-spaces-applies-to-006: display: table
+
+  ws-break-spaces-applies-to-007: display: inline-table
+
+  ws-break-spaces-applies-to-008: display: table-row-group
+
+  ws-break-spaces-applies-to-009: display: table-header-group
+
+  ws-break-spaces-applies-to-010: display: table-footer-group
+
+  ws-break-spaces-applies-to-011: display: table-row
+
+  ws-break-spaces-applies-to-012: display: table-column-group
+
+  ws-break-spaces-applies-to-013: display: table-column
+
+  ws-break-spaces-applies-to-014: display: table-cell
+
+  ws-break-spaces-applies-to-015: display: table-caption
+
+  -->
+
+  <style>
+  div#test, table#reference
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      margin-bottom: 0.25em;
+      width: 4ch;
+    }
+
+  div#test
+    {
+      display: table;
+      white-space: break-spaces;
+    }
+
+  div#row
+    {
+      display: table-row;
+    }
+
+  div#cell
+    {
+      display: table-cell;
+    }
+
+  table#reference
+    {
+      border-spacing: 0px;
+      white-space: pre;
+    }
+
+  td
+    {
+      padding: 0px;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="test">
+    <div id="row">
+      <div id="cell">123    8</div>
+      <!--              45678      -->
+     <!-- will wrap here ^         -->
+    </div>
+  </div>
+
+  <table id="reference"><tr><td>123
+   8</table>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-007.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-007.html
new file mode 100644
index 0000000..6c99a930
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-007.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text Test: 'white-space: break-spaces' applies to 'display: inline-table' element</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/ws-break-spaces-applies-to-006-ref.html">
+
+  <meta content="" name="flags">
+  <meta content="When 'white-space' is 'break-spaces', then new lines, sequence of white spaces and tabs are preserved and text can wrap, just like pre-wrap, but sequence of preserved white space always takes up space, including at the end of the line and line breaking opportunity exists after every preserved white space character, including between white space characters. In this test, 'display: inline-table' is tested." name="assert">
+
+  <!--
+
+  Testing of 'white-space: break-spaces':
+
+  ws-break-spaces-applies-to-001: display: inline
+
+  ws-break-spaces-applies-to-002: display: block
+
+  ws-break-spaces-applies-to-003: display: list-item
+
+  ws-break-spaces-applies-to-005: display: inline-block
+
+  ws-break-spaces-applies-to-006: display: table
+
+  ws-break-spaces-applies-to-007: display: inline-table
+
+  ws-break-spaces-applies-to-008: display: table-row-group
+
+  ws-break-spaces-applies-to-009: display: table-header-group
+
+  ws-break-spaces-applies-to-010: display: table-footer-group
+
+  ws-break-spaces-applies-to-011: display: table-row
+
+  ws-break-spaces-applies-to-012: display: table-column-group
+
+  ws-break-spaces-applies-to-013: display: table-column
+
+  ws-break-spaces-applies-to-014: display: table-cell
+
+  ws-break-spaces-applies-to-015: display: table-caption
+
+  -->
+
+  <style>
+  div#test, table
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      margin-bottom: 0.25em;
+      width: 4ch;
+    }
+
+  div#test
+    {
+      display: inline-table;
+      white-space: break-spaces;
+    }
+
+  div#row
+    {
+      display: table-row;
+    }
+
+  div#cell
+    {
+      display: table-cell;
+    }
+
+  table#reference
+    {
+      border-spacing: 0px;
+      white-space: pre;
+    }
+
+  td
+    {
+      padding: 0px;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="test">
+    <div id="row">
+      <div id="cell">123    8</div>
+      <!--              45678      -->
+     <!-- will wrap here ^         -->
+    </div>
+  </div>
+
+  <table id="reference"><tr><td>123
+   8</table>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-008.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-008.html
new file mode 100644
index 0000000..007c95a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-008.html
@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text Test: 'white-space: break-spaces' applies to 'display: table-row-group' element</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/ws-break-spaces-applies-to-006-ref.html">
+
+  <meta content="" name="flags">
+  <meta content="When 'white-space' is 'break-spaces', then new lines, sequence of white spaces and tabs are preserved and text can wrap, just like pre-wrap, but sequence of preserved white space always takes up space, including at the end of the line and line breaking opportunity exists after every preserved white space character, including between white space characters. In this test, 'display: table-row-group' is tested." name="assert">
+
+  <!--
+
+  Testing of 'white-space: break-spaces':
+
+  ws-break-spaces-applies-to-001: display: inline
+
+  ws-break-spaces-applies-to-002: display: block
+
+  ws-break-spaces-applies-to-003: display: list-item
+
+  ws-break-spaces-applies-to-005: display: inline-block
+
+  ws-break-spaces-applies-to-006: display: table
+
+  ws-break-spaces-applies-to-007: display: inline-table
+
+  ws-break-spaces-applies-to-008: display: table-row-group
+
+  ws-break-spaces-applies-to-009: display: table-header-group
+
+  ws-break-spaces-applies-to-010: display: table-footer-group
+
+  ws-break-spaces-applies-to-011: display: table-row
+
+  ws-break-spaces-applies-to-012: display: table-column-group
+
+  ws-break-spaces-applies-to-013: display: table-column
+
+  ws-break-spaces-applies-to-014: display: table-cell
+
+  ws-break-spaces-applies-to-015: display: table-caption
+
+  -->
+
+  <style>
+  div#cell, td#reference
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      width: 4ch;
+    }
+
+  div#table
+    {
+      display: table;
+    }
+
+  div#test
+    {
+      display: table-row-group;
+      white-space: break-spaces;
+    }
+
+  div#row
+    {
+      display: table-row;
+    }
+
+  div#cell
+    {
+      display: table-cell;
+      width: 4ch;
+    }
+
+  table
+    {
+      border-spacing: 0px;
+      margin-top: 0.5em;
+    }
+
+  td#reference
+    {
+      padding: 0px;
+      white-space: pre;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="table">
+    <div id="test">
+      <div id="row">
+        <div id="cell">123    8</div>
+        <!--              45678      -->
+       <!-- will wrap here ^         -->
+      </div>
+    </div>
+  </div>
+
+  <table><tbody><tr><td id="reference">123
+   8</table>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-009.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-009.html
new file mode 100644
index 0000000..6bd4dbb3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-009.html
@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text Test: 'white-space: break-spaces' applies to 'display: table-header-group' element</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/ws-break-spaces-applies-to-006-ref.html">
+
+  <meta content="" name="flags">
+  <meta content="When 'white-space' is 'break-spaces', then new lines, sequence of white spaces and tabs are preserved and text can wrap, just like pre-wrap, but sequence of preserved white space always takes up space, including at the end of the line and line breaking opportunity exists after every preserved white space character, including between white space characters. In this test, 'display: table-header-group' is tested." name="assert">
+
+  <!--
+
+  Testing of 'white-space: break-spaces':
+
+  ws-break-spaces-applies-to-001: display: inline
+
+  ws-break-spaces-applies-to-002: display: block
+
+  ws-break-spaces-applies-to-003: display: list-item
+
+  ws-break-spaces-applies-to-005: display: inline-block
+
+  ws-break-spaces-applies-to-006: display: table
+
+  ws-break-spaces-applies-to-007: display: inline-table
+
+  ws-break-spaces-applies-to-008: display: table-row-group
+
+  ws-break-spaces-applies-to-009: display: table-header-group
+
+  ws-break-spaces-applies-to-010: display: table-footer-group
+
+  ws-break-spaces-applies-to-011: display: table-row
+
+  ws-break-spaces-applies-to-012: display: table-column-group
+
+  ws-break-spaces-applies-to-013: display: table-column
+
+  ws-break-spaces-applies-to-014: display: table-cell
+
+  ws-break-spaces-applies-to-015: display: table-caption
+
+  -->
+
+  <style>
+  div#cell, td#reference
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      width: 4ch;
+    }
+
+  div#table
+    {
+      display: table;
+    }
+
+  div#test
+    {
+      display: table-header-group;
+      white-space: break-spaces;
+    }
+
+  div#row
+    {
+      display: table-row;
+    }
+
+  div#cell
+    {
+      display: table-cell;
+      width: 4ch;
+    }
+
+  table
+    {
+      border-spacing: 0px;
+      margin-top: 0.5em;
+    }
+
+  td#reference
+    {
+      padding: 0px;
+      white-space: pre;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="table">
+    <div id="test">
+      <div id="row">
+        <div id="cell">123    8</div>
+        <!--              45678      -->
+       <!-- will wrap here ^         -->
+      </div>
+    </div>
+  </div>
+
+  <table><thead><tr><td id="reference">123
+   8</table>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-010.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-010.html
new file mode 100644
index 0000000..caf1e89eb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-010.html
@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text Test: 'white-space: break-spaces' applies to 'display: table-footer-group' element</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/ws-break-spaces-applies-to-006-ref.html">
+
+  <meta content="" name="flags">
+  <meta content="When 'white-space' is 'break-spaces', then new lines, sequence of white spaces and tabs are preserved and text can wrap, just like pre-wrap, but sequence of preserved white space always takes up space, including at the end of the line and line breaking opportunity exists after every preserved white space character, including between white space characters. In this test, 'display: table-footer-group' is tested." name="assert">
+
+  <!--
+
+  Testing of 'white-space: break-spaces':
+
+  ws-break-spaces-applies-to-001: display: inline
+
+  ws-break-spaces-applies-to-002: display: block
+
+  ws-break-spaces-applies-to-003: display: list-item
+
+  ws-break-spaces-applies-to-005: display: inline-block
+
+  ws-break-spaces-applies-to-006: display: table
+
+  ws-break-spaces-applies-to-007: display: inline-table
+
+  ws-break-spaces-applies-to-008: display: table-row-group
+
+  ws-break-spaces-applies-to-009: display: table-header-group
+
+  ws-break-spaces-applies-to-010: display: table-footer-group
+
+  ws-break-spaces-applies-to-011: display: table-row
+
+  ws-break-spaces-applies-to-012: display: table-column-group
+
+  ws-break-spaces-applies-to-013: display: table-column
+
+  ws-break-spaces-applies-to-014: display: table-cell
+
+  ws-break-spaces-applies-to-015: display: table-caption
+
+  -->
+
+  <style>
+  div#cell, td#reference
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      width: 4ch;
+    }
+
+  div#table
+    {
+      display: table;
+    }
+
+  div#test
+    {
+      display: table-footer-group;
+      white-space: break-spaces;
+    }
+
+  div#row
+    {
+      display: table-row;
+    }
+
+  div#cell
+    {
+      display: table-cell;
+      width: 4ch;
+    }
+
+  table
+    {
+      border-spacing: 0px;
+      margin-top: 0.5em;
+    }
+
+  td#reference
+    {
+      padding: 0px;
+      white-space: pre;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="table">
+    <div id="test">
+      <div id="row">
+        <div id="cell">123    8</div>
+        <!--              45678      -->
+       <!-- will wrap here ^         -->
+      </div>
+    </div>
+  </div>
+
+  <table><tfoot><tr><td id="reference">123
+   8</table>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-011.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-011.html
new file mode 100644
index 0000000..df43ab4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-011.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text Test: 'white-space: break-spaces' applies to 'display: table-row' element</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/ws-break-spaces-applies-to-006-ref.html">
+
+  <meta content="" name="flags">
+  <meta content="When 'white-space' is 'break-spaces', then new lines, sequence of white spaces and tabs are preserved and text can wrap, just like pre-wrap, but sequence of preserved white space always takes up space, including at the end of the line and line breaking opportunity exists after every preserved white space character, including between white space characters. In this test, 'display: table-row' is tested." name="assert">
+
+  <!--
+
+  Testing of 'white-space: break-spaces':
+
+  ws-break-spaces-applies-to-001: display: inline
+
+  ws-break-spaces-applies-to-002: display: block
+
+  ws-break-spaces-applies-to-003: display: list-item
+
+  ws-break-spaces-applies-to-005: display: inline-block
+
+  ws-break-spaces-applies-to-006: display: table
+
+  ws-break-spaces-applies-to-007: display: inline-table
+
+  ws-break-spaces-applies-to-008: display: table-row-group
+
+  ws-break-spaces-applies-to-009: display: table-header-group
+
+  ws-break-spaces-applies-to-010: display: table-footer-group
+
+  ws-break-spaces-applies-to-011: display: table-row
+
+  ws-break-spaces-applies-to-012: display: table-column-group
+
+  ws-break-spaces-applies-to-013: display: table-column
+
+  ws-break-spaces-applies-to-014: display: table-cell
+
+  ws-break-spaces-applies-to-015: display: table-caption
+
+  -->
+
+  <style>
+  div#cell, td#reference
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      width: 4ch;
+    }
+
+  div#table
+    {
+      display: table;
+    }
+
+  div#test
+    {
+      display: table-row;
+      white-space: break-spaces;
+    }
+
+  div#cell
+    {
+      display: table-cell;
+      width: 4ch;
+    }
+
+  table
+    {
+      border-spacing: 0px;
+      margin-top: 0.5em;
+    }
+
+  td#reference
+    {
+      padding: 0px;
+      white-space: pre;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="table">
+    <div id="test">
+      <div id="cell">123    8</div>
+      <!--              45678      -->
+     <!-- will wrap here ^         -->
+    </div>
+  </div>
+
+  <table><tr><td id="reference">123
+   8</table>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-012.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-012.html
new file mode 100644
index 0000000..540ce4a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-012.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text Test: 'white-space: break-spaces' does not apply to 'display: table-column-group' element</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/ws-break-spaces-applies-to-012-ref.html">
+
+  <meta content="" name="flags">
+  <meta content="In this test, we verify that 'white-space: break-spaces' does not apply to element with 'display: table-column-group'." name="assert">
+
+  <!--
+
+  Testing of 'white-space: break-spaces':
+
+  ws-break-spaces-applies-to-001: display: inline
+
+  ws-break-spaces-applies-to-002: display: block
+
+  ws-break-spaces-applies-to-003: display: list-item
+
+  ws-break-spaces-applies-to-005: display: inline-block
+
+  ws-break-spaces-applies-to-006: display: table
+
+  ws-break-spaces-applies-to-007: display: inline-table
+
+  ws-break-spaces-applies-to-008: display: table-row-group
+
+  ws-break-spaces-applies-to-009: display: table-header-group
+
+  ws-break-spaces-applies-to-010: display: table-footer-group
+
+  ws-break-spaces-applies-to-011: display: table-row
+
+  ws-break-spaces-applies-to-012: display: table-column-group
+
+  ws-break-spaces-applies-to-013: display: table-column
+
+  ws-break-spaces-applies-to-014: display: table-cell
+
+  ws-break-spaces-applies-to-015: display: table-caption
+
+  -->
+
+  <style>
+  div#cell, td#reference
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      width: 4ch;
+    }
+
+  div#table
+    {
+      display: table;
+    }
+
+  div#test
+    {
+      display: table-column-group;
+      white-space: break-spaces;
+    }
+
+  div#row
+    {
+      display: table-row;
+    }
+
+  div#cell
+    {
+      display: table-cell;
+    }
+
+  table
+    {
+      border-spacing: 0px;
+      margin-top: 0.5em;
+    }
+
+  td#reference
+    {
+      padding: 0px;
+      white-space: normal;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="table">
+    <div id="test"></div>
+    <div id="row">
+      <div id="cell">123    8</div>
+      <!--              45678      -->
+     <!-- will wrap here ^         -->
+    </div>
+  </div>
+
+  <table><tr><td id="reference">123
+   8</table>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-013.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-013.html
new file mode 100644
index 0000000..5274bd7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-013.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text Test: 'white-space: break-spaces' does not apply to 'display: table-column' element</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/ws-break-spaces-applies-to-012-ref.html">
+
+  <meta content="" name="flags">
+  <meta content="In this test, we verify that 'white-space: break-spaces' does not apply to element with 'display: table-column'." name="assert">
+
+  <!--
+
+  Testing of 'white-space: break-spaces':
+
+  ws-break-spaces-applies-to-001: display: inline
+
+  ws-break-spaces-applies-to-002: display: block
+
+  ws-break-spaces-applies-to-003: display: list-item
+
+  ws-break-spaces-applies-to-005: display: inline-block
+
+  ws-break-spaces-applies-to-006: display: table
+
+  ws-break-spaces-applies-to-007: display: inline-table
+
+  ws-break-spaces-applies-to-008: display: table-row-group
+
+  ws-break-spaces-applies-to-009: display: table-header-group
+
+  ws-break-spaces-applies-to-010: display: table-footer-group
+
+  ws-break-spaces-applies-to-011: display: table-row
+
+  ws-break-spaces-applies-to-012: display: table-column-group
+
+  ws-break-spaces-applies-to-013: display: table-column
+
+  ws-break-spaces-applies-to-014: display: table-cell
+
+  ws-break-spaces-applies-to-015: display: table-caption
+
+  -->
+
+  <style>
+  div#cell, td#reference
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      width: 4ch;
+    }
+
+  div#table
+    {
+      display: table;
+    }
+
+  div#test
+    {
+      display: table-column;
+      white-space: break-spaces;
+    }
+
+  div#row
+    {
+      display: table-row;
+    }
+
+  div#cell
+    {
+      display: table-cell;
+    }
+
+  table
+    {
+      border-spacing: 0px;
+      margin-top: 0.5em;
+    }
+
+  td#reference
+    {
+      padding: 0px;
+      white-space: normal;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="table">
+    <div id="test"></div>
+    <div id="row">
+      <div id="cell">123    8</div>
+      <!--              45678      -->
+     <!-- will wrap here ^         -->
+    </div>
+  </div>
+
+  <table><tr><td id="reference">123
+   8</table>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-014.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-014.html
new file mode 100644
index 0000000..1454b3f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-014.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text Test: 'white-space: break-spaces' applies to 'display: table-cell' element</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/ws-break-spaces-applies-to-006-ref.html">
+
+  <meta content="" name="flags">
+  <meta content="When 'white-space' is 'break-spaces', then new lines, sequence of white spaces and tabs are preserved and text can wrap, just like pre-wrap, but sequence of preserved white space always takes up space, including at the end of the line and line breaking opportunity exists after every preserved white space character, including between white space characters. In this test, 'display: table-cell' is tested." name="assert">
+
+  <!--
+
+  Testing of 'white-space: break-spaces':
+
+  ws-break-spaces-applies-to-001: display: inline
+
+  ws-break-spaces-applies-to-002: display: block
+
+  ws-break-spaces-applies-to-003: display: list-item
+
+  ws-break-spaces-applies-to-005: display: inline-block
+
+  ws-break-spaces-applies-to-006: display: table
+
+  ws-break-spaces-applies-to-007: display: inline-table
+
+  ws-break-spaces-applies-to-008: display: table-row-group
+
+  ws-break-spaces-applies-to-009: display: table-header-group
+
+  ws-break-spaces-applies-to-010: display: table-footer-group
+
+  ws-break-spaces-applies-to-011: display: table-row
+
+  ws-break-spaces-applies-to-012: display: table-column-group
+
+  ws-break-spaces-applies-to-013: display: table-column
+
+  ws-break-spaces-applies-to-014: display: table-cell
+
+  ws-break-spaces-applies-to-015: display: table-caption
+
+  -->
+
+  <style>
+  div#test, td#reference
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      width: 4ch;
+    }
+
+  div#table
+    {
+      display: table;
+    }
+
+  div#row
+    {
+      display: table-row;
+    }
+
+  div#test
+    {
+      display: table-cell;
+      white-space: break-spaces;
+    }
+
+  table
+    {
+      border-spacing: 0px;
+      margin-top: 0.5em;
+    }
+
+  td#reference
+    {
+      padding: 0px;
+      white-space: pre;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="table">
+    <div id="row">
+      <div id="test">123    8</div>
+      <!--              45678      -->
+     <!-- will wrap here ^         -->
+    </div>
+  </div>
+
+  <table><tr><td id="reference">123
+   8</table>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-015.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-015.html
new file mode 100644
index 0000000..b8634bb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/ws-break-spaces-applies-to-015.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Text Test: 'white-space: break-spaces' applies to 'display: table-caption' element</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+  <link rel="match" href="reference/ws-break-spaces-applies-to-001-ref.html">
+
+  <meta content="" name="flags">
+  <meta content="When 'white-space' is 'break-spaces', then new lines, sequence of white spaces and tabs are preserved and text can wrap, just like pre-wrap, but sequence of preserved white space always takes up space, including at the end of the line and line breaking opportunity exists after every preserved white space character, including between white space characters. In this test, 'display: table-caption' is tested." name="assert">
+
+  <!--
+
+  Testing of 'white-space: break-spaces':
+
+  ws-break-spaces-applies-to-001: display: inline
+
+  ws-break-spaces-applies-to-002: display: block
+
+  ws-break-spaces-applies-to-003: display: list-item
+
+  ws-break-spaces-applies-to-005: display: inline-block
+
+  ws-break-spaces-applies-to-006: display: table
+
+  ws-break-spaces-applies-to-007: display: inline-table
+
+  ws-break-spaces-applies-to-008: display: table-row-group
+
+  ws-break-spaces-applies-to-009: display: table-header-group
+
+  ws-break-spaces-applies-to-010: display: table-footer-group
+
+  ws-break-spaces-applies-to-011: display: table-row
+
+  ws-break-spaces-applies-to-012: display: table-column-group
+
+  ws-break-spaces-applies-to-013: display: table-column
+
+  ws-break-spaces-applies-to-014: display: table-cell
+
+  ws-break-spaces-applies-to-015: display: table-caption
+
+  -->
+
+  <style>
+  div#test, caption#reference
+    {
+      border: black solid 2px;
+      font-family: monospace;
+      font-size: 32px;
+      width: 4ch;
+    }
+
+  div#table
+    {
+      display: table;
+    }
+
+  div#test
+    {
+      display: table-caption;
+      white-space: break-spaces;
+    }
+
+  table
+    {
+      margin-top: 0.5em;
+    }
+
+  caption#reference
+    {
+      text-align: left;
+      white-space: pre;
+    }
+  </style>
+
+  <p>Test passes if the characters inside of each black-bordered rectangles are laid out identically.
+
+  <div id="table">
+    <div id="test">123    8</div>
+      <!--            45678      -->
+   <!-- will wrap here ^         -->
+  </div>
+
+  <table><caption id="reference">123
+   8</table>
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom-view/MediaQueryList-addListener-handleEvent.html b/third_party/blink/web_tests/external/wpt/css/cssom-view/MediaQueryList-addListener-handleEvent.html
index 30c8cf2..43b144a9 100644
--- a/third_party/blink/web_tests/external/wpt/css/cssom-view/MediaQueryList-addListener-handleEvent.html
+++ b/third_party/blink/web_tests/external/wpt/css/cssom-view/MediaQueryList-addListener-handleEvent.html
@@ -72,21 +72,26 @@
 }, "doesn't look up handleEvent method on callable event listeners");
 
 const uncaught_error_test = async (t, mql, getHandleEvent) => {
+    const eventWatcher = new EventWatcher(t, window, "error", waitForChangesReported);
+    const errorPromise = eventWatcher.wait_for("error");
+
     let calls = 0;
-    mql.addListener({
+    const listener = {
         get handleEvent() {
             calls++;
             return getHandleEvent();
         },
-    });
+    };
 
-    const eventWatcher = new EventWatcher(t, window, "error", waitForChangesReported);
-    const errorPromise = eventWatcher.wait_for("error");
-    triggerMQLEvent(mql);
+    try {
+        mql.addListener(listener);
+        triggerMQLEvent(mql);
 
-    const event = await errorPromise;
-    assert_equals(calls, 1, "handleEvent property was not looked up");
-    throw event.error;
+        const event = await errorPromise;
+        throw event.error;
+    } finally {
+        assert_equals(calls, 1, "handleEvent property was not looked up");
+    }
 };
 
 promise_test(async t => {
diff --git a/third_party/blink/web_tests/external/wpt/dom/nodes/Node-lookupNamespaceURI.html b/third_party/blink/web_tests/external/wpt/dom/nodes/Node-lookupNamespaceURI.html
index f5401e54..74c1ac8 100644
--- a/third_party/blink/web_tests/external/wpt/dom/nodes/Node-lookupNamespaceURI.html
+++ b/third_party/blink/web_tests/external/wpt/dom/nodes/Node-lookupNamespaceURI.html
@@ -33,7 +33,7 @@
 isDefaultNamespace(frag, 'xmlns', false, 'DocumentFragment is in default namespace, prefix "xmlns"');
 
 var docType = document.doctype;
-lookupNamespaceURI(docType, null, null, 'DocumentType should have null nemspace, prefix null');
+lookupNamespaceURI(docType, null, null, 'DocumentType should have null namespace, prefix null');
 lookupNamespaceURI(docType, '', null, 'DocumentType should have null namespace, prefix ""');
 lookupNamespaceURI(docType, 'foo', null, 'DocumentType should have null namespace, prefix "foo"');
 lookupNamespaceURI(docType, 'xmlns', null, 'DocumentType should have null namespace, prefix "xmlns"');
diff --git a/third_party/blink/web_tests/external/wpt/domxpath/callback-interface-expected.txt b/third_party/blink/web_tests/external/wpt/domxpath/callback-interface-expected.txt
new file mode 100644
index 0000000..a89cd98
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/domxpath/callback-interface-expected.txt
@@ -0,0 +1,12 @@
+This is a testharness.js-based test.
+PASS callable resolver
+PASS callable resolver: result is not cached
+PASS callable resolver: abrupt completion from Call
+FAIL callable resolver: no 'lookupNamespaceURI' lookups assert_equals: expected 0 but got 1
+PASS object resolver
+PASS object resolver: 'lookupNamespaceURI' is not cached
+FAIL object resolver: abrupt completion from Get assert_true: Timed out waiting for error expected true got false
+FAIL object resolver: 'lookupNamespaceURI' is thruthy and not callable assert_true: Timed out waiting for error expected true got false
+FAIL object resolver: 'lookupNamespaceURI' is falsy and not callable assert_true: Timed out waiting for error expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/domxpath/callback-interface.html b/third_party/blink/web_tests/external/wpt/domxpath/callback-interface.html
new file mode 100644
index 0000000..4596814
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/domxpath/callback-interface.html
@@ -0,0 +1,147 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>XPathNSResolver implements callback interface</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://heycam.github.io/webidl/#call-a-user-objects-operation">
+<div id=log></div>
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+test(() => {
+  let resolverCalls = 0;
+  document.evaluate("/foo:bar", document.documentElement, () => {
+    resolverCalls++;
+    return "";
+  });
+
+  assert_equals(resolverCalls, 1);
+}, "callable resolver");
+
+test(() => {
+  let resolverCalls = 0;
+  const resolver = () => {
+    resolverCalls++;
+    return "";
+  };
+
+  document.evaluate("/foo:bar", document.documentElement, resolver);
+  document.evaluate("/foo:bar", document.documentElement, resolver);
+
+  assert_equals(resolverCalls, 2);
+}, "callable resolver: result is not cached");
+
+const uncaught_error_test = async (t, resolver) => {
+  const timeout = () => {
+    return new Promise(resolve => {
+      t.step_timeout(resolve, 0);
+    });
+  };
+
+  const eventWatcher = new EventWatcher(t, window, "error", timeout);
+  const errorPromise = eventWatcher.wait_for("error");
+
+  assert_throws_dom("NAMESPACE_ERR", () => {
+    document.evaluate("/foo:bar", document.documentElement, resolver);
+  });
+
+  const event = await errorPromise;
+  throw event.error;
+};
+
+promise_test(t => {
+  const testError = { name: "test" };
+  const resolver = () => {
+    throw testError;
+  };
+
+  return promise_rejects_exactly(t, testError,
+    uncaught_error_test(t, resolver)
+  );
+}, "callable resolver: abrupt completion from Call");
+
+test(() => {
+  let resolverCalls = 0;
+  const resolver = () => {
+    resolverCalls++;
+    return "";
+  };
+
+  let resolverGets = 0;
+  Object.defineProperty(resolver, "lookupNamespaceURI", {
+    get() {
+      resolverGets++;
+    },
+  });
+
+  document.evaluate("/foo:bar", document.documentElement, resolver);
+
+  assert_equals(resolverCalls, 1);
+  assert_equals(resolverGets, 0);
+}, "callable resolver: no 'lookupNamespaceURI' lookups");
+
+test(() => {
+  let resolverCalls = 0;
+  document.evaluate("/foo:bar", document.documentElement, {
+    lookupNamespaceURI() {
+      resolverCalls++;
+      return "";
+    },
+  });
+
+  assert_equals(resolverCalls, 1);
+}, "object resolver");
+
+test(() => {
+  let resolverCalls = 0;
+  const lookupNamespaceURI = () => {
+    resolverCalls++;
+    return "";
+  };
+
+  let resolverGets = 0;
+  const resolver = {
+    get lookupNamespaceURI() {
+      resolverGets++;
+      return lookupNamespaceURI;
+    },
+  };
+
+  document.evaluate("/foo:bar", document.documentElement, resolver);
+  document.evaluate("/foo:bar", document.documentElement, resolver);
+
+  assert_equals(resolverCalls, 2);
+  assert_equals(resolverGets, 2);
+}, "object resolver: 'lookupNamespaceURI' is not cached");
+
+promise_test(t => {
+  const testError = { name: "test" };
+  const resolver = {
+    get lookupNamespaceURI() {
+      throw testError;
+    },
+  };
+
+  return promise_rejects_exactly(t, testError,
+    uncaught_error_test(t, resolver)
+  );
+}, "object resolver: abrupt completion from Get");
+
+promise_test(t => {
+  const resolver = {
+    lookupNamespaceURI: {},
+  };
+
+  return promise_rejects_js(t, TypeError,
+    uncaught_error_test(t, resolver)
+  );
+}, "object resolver: 'lookupNamespaceURI' is thruthy and not callable");
+
+promise_test(t => {
+  return promise_rejects_js(t, TypeError,
+    uncaught_error_test(t, {})
+  );
+}, "object resolver: 'lookupNamespaceURI' is falsy and not callable");
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_lostpointercapture_for_disconnected_node_in_shadow_dom.html b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_lostpointercapture_for_disconnected_node_in_shadow_dom.html
new file mode 100644
index 0000000..f92daeb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_lostpointercapture_for_disconnected_node_in_shadow_dom.html
@@ -0,0 +1,129 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Lostpointercapture fires on document when target in shadow dom  is removed</title>
+    <meta name="viewport" content="width=device-width">
+    <link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=810882">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/resources/testdriver.js"></script>
+    <script src="/resources/testdriver-actions.js"></script>
+    <script src="/resources/testdriver-vendor.js"></script>
+  </head>
+  <body onload="onLoad()">
+    <template id="template">
+      <style>
+          #content{
+              height:100px;
+              width:100px;
+              background-color: lightgrey;
+          }
+      </style>
+      <div id="content"></div>
+    </template>
+    <h1>Pointer Events - lostpointercapture when capturing element in shadow dom is removed</h1>
+    <h4>
+        Test Description:
+        This test checks if lostpointercapture is fired at the document when the capturing node that is in shadow dom is removed from the shadow dom.
+        Complete the following actions:
+        <ol>
+            <li>Press left mouse button over "Set Capture" button. Pointer should be captured by the gray rectangle which is in shadow dom.</li>
+            <li>Gray rectangle will be removed from shadow dom.</li>
+            <li>"lostpointercapture" should be received on the document not on the gray rectangle.</li>
+        </ol>
+    </h4>
+    <div id="shadowhost"></div>
+    <br>
+    <input type="button" id="btnCapture" value="Set Capture">
+    <div id="log"></div>
+    <script>
+      async function onLoad(){
+        var logDiv = document.getElementById("log");
+        function logMessage(message){
+          var log = document.createElement("div");
+          var messageNode = document.createTextNode(message);
+          log.appendChild(messageNode);
+          logDiv.appendChild(log);
+        }
+        var events = [];
+
+        var host = document.getElementById("shadowhost");
+        var shadowRoot = host.attachShadow({mode: "open"});
+        var template = document.getElementById("template");
+        var node = template.content.cloneNode(true);
+        shadowRoot.appendChild(node);
+
+        var content = host.shadowRoot.getElementById("content");
+        var captureButton = document.getElementById("btnCapture");
+
+        captureButton.addEventListener("pointerdown", function(event){
+          logMessage("Pointer will be captured by the shadow dom gray rectangle.");
+          content.setPointerCapture(event.pointerId);
+          events.push("pointerdown@captureButton");
+        });
+        content.addEventListener("gotpointercapture", function(event){
+          logMessage("Gray rectangle received pointercapture.");
+          logMessage("Removing gray rectangle from shadow dom.")
+          content.parentNode.removeChild(content);
+          events.push("gotpointercapture@content");
+        });
+        content.addEventListener("lostpointercapture", function(event){
+          logMessage("Test Failed! The element removed from shadow dom should not receive lostpointercapture.")
+          events.push("lostpointercapture@content");
+          if(window.promise_test && shadow_dom_test){
+            shadow_dom_test.step(function(){
+              assert_unreached("lostpointercapture must be fired on the document, not the capturing element");
+              reject_test("lostpointercapture must not be dispatched on the disconnected node");
+              shadow_dom_test.done();
+            });
+          }
+        });
+        document.addEventListener("lostpointercapture", function(event){
+          logMessage("Test Passed! Document received lostpointercapture.");
+          events.push("lostpointercapture@document");
+          if(window.promise_test && shadow_dom_test){
+            shadow_dom_test.step(function(){
+              assert_array_equals(events, ["pointerdown@captureButton",
+                "gotpointercapture@content",
+                "lostpointercapture@document"]);
+              resolve_test();
+            });
+          }
+        });
+
+        var shadow_dom_test = null;
+        var resolve_test = null;
+        var reject_test = null;
+
+        function cleanup(){
+          events = [];
+          shadow_dom_test = null;
+          resolve_test = null;
+          reject_test = null;
+        }
+        if(window.promise_test){
+          promise_test(async function(t){
+            var actions_promise;
+            return new Promise(async function(resolve, reject){
+              shadow_dom_test = t;
+              resolve_test = resolve;
+              reject_test = reject;
+              t.add_cleanup(function(){
+                cleanup();
+              });
+              var actions = new test_driver.Actions();
+              actions_promise = actions
+                  .pointerMove(0, 0, {origin:captureButton})
+                  .pointerDown({button: actions.ButtonType.LEFT})
+                  .pointerUp({button: actions.ButtonType.LEFT})
+                  .send();
+            }).finally(async () => {
+              await actions_promise;
+              t.done();
+            });
+          }, "lostpointercapture is dispatched on the document when shadow dom capturing element is removed");
+        }
+      }
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_pointercapture-in-custom-element.html b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_pointercapture-in-custom-element.html
new file mode 100644
index 0000000..e8f143b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_pointercapture-in-custom-element.html
@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>PointerCapture for Custome Shadow DOM</title>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width">
+    <link rel="help" href= "https://bugs.chromium.org/p/chromium/issues/detail?id=810882">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/resources/testdriver.js"></script>
+    <script src="/resources/testdriver-actions.js"></script>
+    <script src="/resources/testdriver-vendor.js"></script>
+    <script>
+       class WC extends HTMLElement{
+        constructor(){
+          super();
+          let template = document.getElementById('template-wc');
+          let node = template.content.cloneNode(true) ;
+
+          let shadowRoot = this.attachShadow({mode: 'open'});
+          shadowRoot.appendChild(node);
+        }
+       }
+       customElements.define("wc-wc", WC);
+    </script>
+    </head>
+  <body onload="onLoad()">
+    <template id="template-wc">
+      <style>
+          #content{
+              height:50px;
+              width:50px;
+              background-color: magenta;
+          }
+      </style>
+      <div id="content"></div>
+    </template>
+    <h4>PointerCapture by Custom Element's Shadow DOM</h4>
+      The magenta box below is part of a custom element's Shadow DOM.
+    <ul>
+      <li> Click left mouse button inside the box and keep mouse button depressed</li>
+      <li> Move the mouse</li>
+      <li> There should be a message stating <em>Pointer was captured by custom element's Shadow DOM!</em></li>
+      <li> Release left mouse button</li>
+      <li> There should be a message stating <em>Pointer was released by custom element's Shadow DOM!</em></li>
+    </ul>
+
+    <wc-wc id="wc-wc"></wc-wc>
+    <div id="log"></div>
+    <script>
+      var logDiv = document.getElementById("log");
+      function logMessage(message){
+        var log = document.createElement("div");
+        var messageNode = document.createTextNode(message);
+        log.appendChild(messageNode);
+        logDiv.appendChild(log);
+      }
+      var events = [];
+
+      var content = document.getElementById("wc-wc")
+         .shadowRoot.getElementById("content");
+
+      content.addEventListener("pointerdown", function(e){
+        content.setPointerCapture(e.pointerId);
+        events.push("pointerdown@content");
+      });
+      content.addEventListener("gotpointercapture", function(e){
+        logMessage("Pointer was captured by custom element's Shadow DOM!");
+        events.push("gotpointercapture@content");
+      });
+      content.addEventListener("pointerup", function(e){
+        content.releasePointerCapture(e.pointerId);
+        events.push("pointerup@content");
+      });
+      content.addEventListener("lostpointercapture", function(e){
+        logMessage("Pointer was released by custom element's Shadow DOM!");
+        events.push("lostpointercapture@content");
+        if(window.promise_test && wc_shadow_dom_test){
+          wc_shadow_dom_test.step(function(){
+            assert_array_equals(events, ["pointerdown@content",
+              "gotpointercapture@content", "pointerup@content",
+              "lostpointercapture@content"]);
+            resolve_test();
+            wc_shadow_dom_test.done();
+          });
+        }
+      });
+
+      var wc_shadow_dom_test = null;
+      var resolve_test = null;
+      var reject_test = null;
+
+      function cleanup(){
+        events = [];
+        shadow_dom_test = null;
+        resolve_test = null;
+        reject_test = null;
+      }
+
+      function onLoad(){
+        if(window.promise_test){
+          promise_test(function(t){
+            return new Promise(function(resolve, reject){
+              wc_shadow_dom_test = t;
+              resolve_test = resolve;
+              reject_test = reject;
+              t.add_cleanup(function(){
+                cleanup();
+              });
+              var contentRect = content.getBoundingClientRect();
+              var actions = new test_driver.Actions();
+              var actions_promise = actions
+                  .pointerMove(contentRect.x, contentRect.y)
+                  .pointerDown({button: actions.ButtonType.LEFT})
+                  .pointerUp({button: actions.ButtonType.LEFT})
+                  .send();
+            });
+          }, "PointerCapture works for custom element Shadow DOM.");
+        }
+      }
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_pointercapture-in-shadow-dom.html b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_pointercapture-in-shadow-dom.html
new file mode 100644
index 0000000..8279665f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_pointercapture-in-shadow-dom.html
@@ -0,0 +1,114 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>PointerCapture for Shadow DOM Elements</title>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width">
+    <link rel="help" href= "https://bugs.chromium.org/p/chromium/issues/detail?id=810882">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/resources/testdriver.js"></script>
+    <script src="/resources/testdriver-actions.js"></script>
+    <script src="/resources/testdriver-vendor.js"></script>
+  </head>
+  <body onload="onLoad()">
+    <template id="template">
+      <style>
+          #content{
+              height:100px;
+              width:100px;
+              background-color: lightgrey;
+          }
+      </style>
+      <div id="content"></div>
+    </template>
+    <h4>PointerCapture by Shadow DOM element</h4>
+    The light gray box below is part of Shadow DOM.
+    <ul>
+      <li> Click left mouse button inside the box and keep mouse button depressed </li>
+      <li> Move the mouse </li>
+      <li> There should be a message stating <em>Pointer was captured by Shadow DOM!</em></li>
+      <li> Release left mouse button
+      <li> There should be a message stating <em>Pointer was released by Shadow DOM!</em></li>
+    </ul>
+    <div id="shadowhost"></div>
+    <div id="log"></div>
+    <script>
+      var logDiv = document.getElementById("log");
+      function logMessage(message){
+        var log = document.createElement("div");
+        var messageNode = document.createTextNode(message);
+        log.appendChild(messageNode);
+        logDiv.appendChild(log);
+      }
+      var events = [];
+
+      var host = document.getElementById("shadowhost");
+      var shadowRoot = host.attachShadow({mode: "open"});
+      var template = document.getElementById("template");
+      var node = template.content.cloneNode(true);
+      shadowRoot.appendChild(node);
+
+      var content = host.shadowRoot.getElementById("content");
+
+      content.addEventListener("pointerdown", function(e){
+        content.setPointerCapture(e.pointerId);
+        events.push("pointerdown@content");
+      });
+      content.addEventListener("gotpointercapture", function(e){
+        logMessage("Pointer was captured by Shadow DOM!");
+        events.push("gotpointercapture@content");
+      });
+      content.addEventListener("pointerup", function(e){
+        content.releasePointerCapture(e.pointerId);
+        events.push("pointerup@content");
+      });
+      content.addEventListener("lostpointercapture", function(e){
+        logMessage("Pointer was released by Shadow DOM!");
+        events.push("lostpointercapture@content");
+        if(window.promise_test && shadow_dom_test){
+          shadow_dom_test.step(function(){
+            assert_array_equals(events, ["pointerdown@content",
+              "gotpointercapture@content", "pointerup@content",
+              "lostpointercapture@content"]);
+            resolve_test();
+            shadow_dom_test.done();
+          });
+        }
+      });
+
+      var shadow_dom_test = null;
+      var resolve_test = null;
+      var reject_test = null;
+
+      function cleanup(){
+        events = [];
+        shadow_dom_test = null;
+        resolve_test = null;
+        reject_test = null;
+      }
+
+      function onLoad(){
+        if(window.promise_test){
+          promise_test(function(t){
+            return new Promise(function(resolve, reject){
+              shadow_dom_test = t;
+              resolve_test = resolve;
+              reject_test = reject;
+              t.add_cleanup(function(){
+                cleanup();
+              });
+              var contentRect = content.getBoundingClientRect();
+              var actions = new test_driver.Actions();
+              var actions_promise = actions
+                  .pointerMove(contentRect.x, contentRect.y)
+                  .pointerDown({button: actions.ButtonType.LEFT})
+                  .pointerUp({button: actions.ButtonType.LEFT})
+                  .send();
+            });
+          }, "PointerCapture works for Shadow DOM element.");
+        }
+      }
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/uievents/legacy/Event-subclasses-init-expected.txt b/third_party/blink/web_tests/external/wpt/uievents/legacy/Event-subclasses-init-expected.txt
deleted file mode 100644
index 7d1f2aa..0000000
--- a/third_party/blink/web_tests/external/wpt/uievents/legacy/Event-subclasses-init-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-FAIL Call initUIEvent without parameters assert_throws_js: function "function() { eventType.prototype[initFunction].call(event) }" did not throw
-FAIL Call initMouseEvent without parameters assert_throws_js: function "function() { eventType.prototype[initFunction].call(event) }" did not throw
-PASS Call initKeyboardEvent without parameters
-FAIL Call initCompositionEvent without parameters assert_throws_js: function "function() { eventType.prototype[initFunction].call(event) }" did not throw
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/iframe-blank-inherit.meta/unset/sharedworker-import.https.html b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/iframe-blank-inherit.meta/unset/sharedworker-import.https.html
new file mode 100644
index 0000000..70ccb7a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/iframe-blank-inherit.meta/unset/sharedworker-import.https.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! Generated by `common/security-features/tools/generate.py --spec upgrade-insecure-requests/` -->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="timeout" content="long">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/security-features/resources/common.sub.js"></script>
+    <script src="../../../generic/test-case.sub.js"></script>
+  </head>
+  <body>
+    <script>
+      TestCase(
+        [
+          {
+            "expectation": "blocked",
+            "origin": "cross-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to cross-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "cross-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to cross-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "cross-https",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to cross-https origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to same-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to same-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-https",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to same-https origin and downgrade redirection from https context."
+          }
+        ],
+        new SanityChecker()
+      ).start();
+    </script>
+    <div id="log"></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/iframe-blank-inherit.meta/unset/worker-import.https.html b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/iframe-blank-inherit.meta/unset/worker-import.https.html
new file mode 100644
index 0000000..d9f3a8cd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/iframe-blank-inherit.meta/unset/worker-import.https.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! Generated by `common/security-features/tools/generate.py --spec upgrade-insecure-requests/` -->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="timeout" content="long">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/security-features/resources/common.sub.js"></script>
+    <script src="../../../generic/test-case.sub.js"></script>
+  </head>
+  <body>
+    <script>
+      TestCase(
+        [
+          {
+            "expectation": "blocked",
+            "origin": "cross-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to cross-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "cross-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to cross-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "cross-https",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to cross-https origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to same-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to same-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-https",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to same-https origin and downgrade redirection from https context."
+          }
+        ],
+        new SanityChecker()
+      ).start();
+    </script>
+    <div id="log"></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/iframe-blank-inherit.meta/upgrade/sharedworker-import.https.html b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/iframe-blank-inherit.meta/upgrade/sharedworker-import.https.html
new file mode 100644
index 0000000..23ccc739
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/iframe-blank-inherit.meta/upgrade/sharedworker-import.https.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! Generated by `common/security-features/tools/generate.py --spec upgrade-insecure-requests/` -->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="timeout" content="long">
+    <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/security-features/resources/common.sub.js"></script>
+    <script src="../../../generic/test-case.sub.js"></script>
+  </head>
+  <body>
+    <script>
+      TestCase(
+        [
+          {
+            "expectation": "allowed",
+            "origin": "cross-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to cross-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "cross-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to cross-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "cross-https",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to cross-https origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to same-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to same-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-https",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to same-https origin and downgrade redirection from https context."
+          }
+        ],
+        new SanityChecker()
+      ).start();
+    </script>
+    <div id="log"></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/iframe-blank-inherit.meta/upgrade/worker-import.https.html b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/iframe-blank-inherit.meta/upgrade/worker-import.https.html
new file mode 100644
index 0000000..17a9e12
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/iframe-blank-inherit.meta/upgrade/worker-import.https.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! Generated by `common/security-features/tools/generate.py --spec upgrade-insecure-requests/` -->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="timeout" content="long">
+    <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/security-features/resources/common.sub.js"></script>
+    <script src="../../../generic/test-case.sub.js"></script>
+  </head>
+  <body>
+    <script>
+      TestCase(
+        [
+          {
+            "expectation": "allowed",
+            "origin": "cross-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to cross-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "cross-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to cross-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "cross-https",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to cross-https origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to same-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to same-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-https",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "iframe-blank"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to same-https origin and downgrade redirection from https context."
+          }
+        ],
+        new SanityChecker()
+      ).start();
+    </script>
+    <div id="log"></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/srcdoc-inherit.meta/unset/sharedworker-import.https.html b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/srcdoc-inherit.meta/unset/sharedworker-import.https.html
new file mode 100644
index 0000000..7543651
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/srcdoc-inherit.meta/unset/sharedworker-import.https.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! Generated by `common/security-features/tools/generate.py --spec upgrade-insecure-requests/` -->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="timeout" content="long">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/security-features/resources/common.sub.js"></script>
+    <script src="../../../generic/test-case.sub.js"></script>
+  </head>
+  <body>
+    <script>
+      TestCase(
+        [
+          {
+            "expectation": "blocked",
+            "origin": "cross-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to cross-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "cross-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to cross-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "cross-https",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to cross-https origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to same-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to same-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-https",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to same-https origin and downgrade redirection from https context."
+          }
+        ],
+        new SanityChecker()
+      ).start();
+    </script>
+    <div id="log"></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/srcdoc-inherit.meta/unset/worker-import.https.html b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/srcdoc-inherit.meta/unset/worker-import.https.html
new file mode 100644
index 0000000..8472dea
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/srcdoc-inherit.meta/unset/worker-import.https.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! Generated by `common/security-features/tools/generate.py --spec upgrade-insecure-requests/` -->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="timeout" content="long">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/security-features/resources/common.sub.js"></script>
+    <script src="../../../generic/test-case.sub.js"></script>
+  </head>
+  <body>
+    <script>
+      TestCase(
+        [
+          {
+            "expectation": "blocked",
+            "origin": "cross-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to cross-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "cross-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to cross-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "cross-https",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to cross-https origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to same-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to same-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-https",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to same-https origin and downgrade redirection from https context."
+          }
+        ],
+        new SanityChecker()
+      ).start();
+    </script>
+    <div id="log"></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/srcdoc-inherit.meta/upgrade/sharedworker-import.https.html b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/srcdoc-inherit.meta/upgrade/sharedworker-import.https.html
new file mode 100644
index 0000000..b2444a8c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/srcdoc-inherit.meta/upgrade/sharedworker-import.https.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! Generated by `common/security-features/tools/generate.py --spec upgrade-insecure-requests/` -->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="timeout" content="long">
+    <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/security-features/resources/common.sub.js"></script>
+    <script src="../../../generic/test-case.sub.js"></script>
+  </head>
+  <body>
+    <script>
+      TestCase(
+        [
+          {
+            "expectation": "allowed",
+            "origin": "cross-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to cross-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "cross-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to cross-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "cross-https",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to cross-https origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to same-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to same-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-https",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to same-https origin and downgrade redirection from https context."
+          }
+        ],
+        new SanityChecker()
+      ).start();
+    </script>
+    <div id="log"></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/srcdoc-inherit.meta/upgrade/worker-import.https.html b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/srcdoc-inherit.meta/upgrade/worker-import.https.html
new file mode 100644
index 0000000..a8370a0a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/srcdoc-inherit.meta/upgrade/worker-import.https.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! Generated by `common/security-features/tools/generate.py --spec upgrade-insecure-requests/` -->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="timeout" content="long">
+    <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/security-features/resources/common.sub.js"></script>
+    <script src="../../../generic/test-case.sub.js"></script>
+  </head>
+  <body>
+    <script>
+      TestCase(
+        [
+          {
+            "expectation": "allowed",
+            "origin": "cross-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to cross-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "cross-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to cross-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "cross-https",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to cross-https origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to same-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to same-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-https",
+            "redirection": "downgrade",
+            "source_context_list": [
+              {
+                "policyDeliveries": [],
+                "sourceContextType": "srcdoc"
+              }
+            ],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to same-https origin and downgrade redirection from https context."
+          }
+        ],
+        new SanityChecker()
+      ).start();
+    </script>
+    <div id="log"></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.http-rp/upgrade/sharedworker-import.https.html b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.http-rp/upgrade/sharedworker-import.https.html
new file mode 100644
index 0000000..3041d08
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.http-rp/upgrade/sharedworker-import.https.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! Generated by `common/security-features/tools/generate.py --spec upgrade-insecure-requests/` -->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="timeout" content="long">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/security-features/resources/common.sub.js"></script>
+    <script src="../../../generic/test-case.sub.js"></script>
+  </head>
+  <body>
+    <script>
+      TestCase(
+        [
+          {
+            "expectation": "allowed",
+            "origin": "cross-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to cross-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "cross-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to cross-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "cross-https",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to cross-https origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to same-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to same-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-https",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to same-https origin and downgrade redirection from https context."
+          }
+        ],
+        new SanityChecker()
+      ).start();
+    </script>
+    <div id="log"></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.http-rp/upgrade/sharedworker-import.https.html.headers b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.http-rp/upgrade/sharedworker-import.https.html.headers
new file mode 100644
index 0000000..602d9dc3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.http-rp/upgrade/sharedworker-import.https.html.headers
@@ -0,0 +1 @@
+Content-Security-Policy: upgrade-insecure-requests
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.http-rp/upgrade/worker-import.https.html b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.http-rp/upgrade/worker-import.https.html
new file mode 100644
index 0000000..7098954
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.http-rp/upgrade/worker-import.https.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! Generated by `common/security-features/tools/generate.py --spec upgrade-insecure-requests/` -->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="timeout" content="long">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/security-features/resources/common.sub.js"></script>
+    <script src="../../../generic/test-case.sub.js"></script>
+  </head>
+  <body>
+    <script>
+      TestCase(
+        [
+          {
+            "expectation": "allowed",
+            "origin": "cross-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to cross-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "cross-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to cross-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "cross-https",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to cross-https origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to same-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to same-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-https",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to same-https origin and downgrade redirection from https context."
+          }
+        ],
+        new SanityChecker()
+      ).start();
+    </script>
+    <div id="log"></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.http-rp/upgrade/worker-import.https.html.headers b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.http-rp/upgrade/worker-import.https.html.headers
new file mode 100644
index 0000000..602d9dc3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.http-rp/upgrade/worker-import.https.html.headers
@@ -0,0 +1 @@
+Content-Security-Policy: upgrade-insecure-requests
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.meta/unset/sharedworker-import.https.html b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.meta/unset/sharedworker-import.https.html
new file mode 100644
index 0000000..fbba95b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.meta/unset/sharedworker-import.https.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! Generated by `common/security-features/tools/generate.py --spec upgrade-insecure-requests/` -->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="timeout" content="long">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/security-features/resources/common.sub.js"></script>
+    <script src="../../../generic/test-case.sub.js"></script>
+  </head>
+  <body>
+    <script>
+      TestCase(
+        [
+          {
+            "expectation": "blocked",
+            "origin": "cross-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to cross-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "cross-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to cross-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "cross-https",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to cross-https origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to same-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to same-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-https",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for sharedworker-import to same-https origin and downgrade redirection from https context."
+          }
+        ],
+        new SanityChecker()
+      ).start();
+    </script>
+    <div id="log"></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.meta/unset/worker-import.https.html b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.meta/unset/worker-import.https.html
new file mode 100644
index 0000000..4743c2c9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.meta/unset/worker-import.https.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! Generated by `common/security-features/tools/generate.py --spec upgrade-insecure-requests/` -->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="timeout" content="long">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/security-features/resources/common.sub.js"></script>
+    <script src="../../../generic/test-case.sub.js"></script>
+  </head>
+  <body>
+    <script>
+      TestCase(
+        [
+          {
+            "expectation": "blocked",
+            "origin": "cross-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to cross-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "cross-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to cross-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "cross-https",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to cross-https origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to same-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to same-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "blocked",
+            "origin": "same-https",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects blocked for worker-import to same-https origin and downgrade redirection from https context."
+          }
+        ],
+        new SanityChecker()
+      ).start();
+    </script>
+    <div id="log"></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.meta/upgrade/sharedworker-import.https.html b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.meta/upgrade/sharedworker-import.https.html
new file mode 100644
index 0000000..eea7d4d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.meta/upgrade/sharedworker-import.https.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! Generated by `common/security-features/tools/generate.py --spec upgrade-insecure-requests/` -->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="timeout" content="long">
+    <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/security-features/resources/common.sub.js"></script>
+    <script src="../../../generic/test-case.sub.js"></script>
+  </head>
+  <body>
+    <script>
+      TestCase(
+        [
+          {
+            "expectation": "allowed",
+            "origin": "cross-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to cross-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "cross-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to cross-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "cross-https",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to cross-https origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to same-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to same-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-https",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "sharedworker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for sharedworker-import to same-https origin and downgrade redirection from https context."
+          }
+        ],
+        new SanityChecker()
+      ).start();
+    </script>
+    <div id="log"></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.meta/upgrade/worker-import.https.html b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.meta/upgrade/worker-import.https.html
new file mode 100644
index 0000000..020dc79
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/gen/top.meta/upgrade/worker-import.https.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! Generated by `common/security-features/tools/generate.py --spec upgrade-insecure-requests/` -->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="timeout" content="long">
+    <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/security-features/resources/common.sub.js"></script>
+    <script src="../../../generic/test-case.sub.js"></script>
+  </head>
+  <body>
+    <script>
+      TestCase(
+        [
+          {
+            "expectation": "allowed",
+            "origin": "cross-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to cross-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "cross-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to cross-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "cross-https",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to cross-https origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-http-downgrade",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to same-http-downgrade origin and downgrade redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-http-downgrade",
+            "redirection": "no-redirect",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to same-http-downgrade origin and no-redirect redirection from https context."
+          },
+          {
+            "expectation": "allowed",
+            "origin": "same-https",
+            "redirection": "downgrade",
+            "source_context_list": [],
+            "source_scheme": "https",
+            "subresource": "worker-import",
+            "subresource_policy_deliveries": [],
+            "test_description": "Upgrade-Insecure-Requests: Expects allowed for worker-import to same-https origin and downgrade redirection from https context."
+          }
+        ],
+        new SanityChecker()
+      ).start();
+    </script>
+    <div id="log"></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/spec.src.json b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/spec.src.json
index 0b987d7..283147a 100644
--- a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/spec.src.json
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/spec.src.json
@@ -144,9 +144,7 @@
         "object-tag",
         "picture-tag",
         "script-tag",
-        "sharedworker-import",
-        "video-tag",
-        "worker-import"
+        "video-tag"
       ],
       "origin": "*",
       "expectation": "*"
diff --git a/third_party/blink/web_tests/fast/canvas/OffscreenCanvas-zero-size-readback.html b/third_party/blink/web_tests/fast/canvas/OffscreenCanvas-zero-size-readback.html
index d416e239..f8b45ac8 100644
--- a/third_party/blink/web_tests/fast/canvas/OffscreenCanvas-zero-size-readback.html
+++ b/third_party/blink/web_tests/fast/canvas/OffscreenCanvas-zero-size-readback.html
@@ -59,6 +59,6 @@
     assert_equals(imgdata.height, 1);
   }
 
-  return promise_rejects(t, new DOMException('', 'IndexSizeError'), offscreen.convertToBlob());
+  return promise_rejects_dom(t, 'IndexSizeError', offscreen.convertToBlob());
 }
 </script>
diff --git a/third_party/blink/web_tests/fast/dom/HTMLScriptElement/reparent-to-new-document-integrity-report-crash-expected.txt b/third_party/blink/web_tests/fast/dom/HTMLScriptElement/reparent-to-new-document-integrity-report-crash-expected.txt
new file mode 100644
index 0000000..9f2f4dd
--- /dev/null
+++ b/third_party/blink/web_tests/fast/dom/HTMLScriptElement/reparent-to-new-document-integrity-report-crash-expected.txt
@@ -0,0 +1,2 @@
+CONSOLE ERROR: Failed to find a valid digest in the 'integrity' attribute for resource 'data:text/javascript,' with computed SHA-256 integrity '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='. The resource has been blocked.
+PASS if no crash.
diff --git a/third_party/blink/web_tests/fast/dom/HTMLScriptElement/reparent-to-new-document-integrity-report-crash.html b/third_party/blink/web_tests/fast/dom/HTMLScriptElement/reparent-to-new-document-integrity-report-crash.html
new file mode 100644
index 0000000..b2fe634
--- /dev/null
+++ b/third_party/blink/web_tests/fast/dom/HTMLScriptElement/reparent-to-new-document-integrity-report-crash.html
@@ -0,0 +1,13 @@
+<body>
+PASS if no crash.
+<script>
+if (window.testRunner)
+ testRunner.dumpAsText();
+
+var s = document.createElementNS('http://www.w3.org/1999/xhtml', 'script') 
+s.src = 'data:text/javascript,';
+s.setAttribute('integrity', 'sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC') 
+document.body.appendChild(s)
+new Document().prepend(s) 
+</script>
+</body>
diff --git a/third_party/blink/web_tests/fast/mediastream/MediaStreamTrack-applyConstraints-expected.txt b/third_party/blink/web_tests/fast/mediastream/MediaStreamTrack-applyConstraints-expected.txt
new file mode 100644
index 0000000..d280be81
--- /dev/null
+++ b/third_party/blink/web_tests/fast/mediastream/MediaStreamTrack-applyConstraints-expected.txt
@@ -0,0 +1,34 @@
+This is a testharness.js-based test.
+PASS applyConstraints() sets the value of a constraint set by getUserMedia()
+FAIL Attempting to change the deviceId with applyConstraints() fails promise_reject_js: function "function OverconstrainedError() { [native code] }" is not an Error subtype
+PASS An unsupported constraint is ignored by applyConstraints()
+PASS All valid keys are returned for complex constraints
+PASS Simple integer
+PASS Ideal integer
+PASS Min/max integer
+PASS Exact unwrapped integer
+PASS Simple double
+PASS Ideal double
+PASS Min/max double
+PASS Exact unwrapped double
+PASS Simple String
+PASS Ideal String
+PASS Multiple String in Brackets
+PASS Multiple Bracketed Naked String
+PASS Single Bracketed string unwrapped
+PASS Both Ideal and Exact string
+PASS echoCancellation with simple boolean value
+PASS echoCancellation with ideal boolean value
+PASS echoCancellation with exact unwrapped boolean value
+PASS autoGainControl with simple boolean value
+PASS autoGainControl with ideal boolean value
+PASS autoGainControl with exact unwrapped boolean value
+PASS noiseSuppression with simple boolean value
+PASS noiseSuppression with ideal boolean value
+PASS noiseSuppression with exact unwrapped boolean value
+PASS applyConstraints() supports resizeMode in getUserMedia() tracks
+PASS Contradicting echoCancellation constraints
+PASS Contradicting noiseSuppression constraints
+PASS Contradicting autoGainControl constraints
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/fast/mediastream/MediaStreamTrack-applyConstraints.html b/third_party/blink/web_tests/fast/mediastream/MediaStreamTrack-applyConstraints.html
index 32f68c1..651deee 100644
--- a/third_party/blink/web_tests/fast/mediastream/MediaStreamTrack-applyConstraints.html
+++ b/third_party/blink/web_tests/fast/mediastream/MediaStreamTrack-applyConstraints.html
@@ -31,9 +31,9 @@
 // The deviceId constraint must be rejected, since the source of a track cannot
 // be changed with applyConstraints.
 promise_test(test => {
-  return promise_rejects(test,
-                         new OverconstrainedError('deviceId', ''),
-                         navigator.mediaDevices.getUserMedia({audio: true})
+  return promise_rejects_js(test,
+                            OverconstrainedError,
+                            navigator.mediaDevices.getUserMedia({audio: true})
       .then(s => {
     return s.getAudioTracks()[0].applyConstraints(
         { deviceId: {exact: 'mydevice-id'}});
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-createOffer-promise.html b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-createOffer-promise.html
index 2598f13..9503e09f5 100644
--- a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-createOffer-promise.html
+++ b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-createOffer-promise.html
@@ -20,15 +20,14 @@
 
       // Test type error
       promise_test(function() {
-        return promise_rejects(this, new TypeError(), pc.createOffer(1));
+        return promise_rejects_js(this, TypeError, pc.createOffer(1));
       }, 'createOffer(1)' );
 
       // Test closed connection
       closedPC = new RTCPeerConnection();
       closedPC.close();
       promise_test(function() {
-        var invalidStateError = new DOMException('', 'InvalidStateError');
-        return promise_rejects(this, invalidStateError, closedPC.createOffer({iceRestart:true}));
+        return promise_rejects_dom(this, 'InvalidStateError', closedPC.createOffer({iceRestart:true}));
       }, 'createOffer() with closed peer connection' );
     </script>
   </body>
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-dataChannelTypeCollision.html b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-dataChannelTypeCollision.html
index df993af..9585715 100644
--- a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-dataChannelTypeCollision.html
+++ b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-dataChannelTypeCollision.html
@@ -20,7 +20,7 @@
     type: 'offer',
     sdp: 'v=0\r\no=- 2512342596970960666 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE data\r\na=msid-semantic: WMS\r\nm=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\nc=IN IP4 0.0.0.0\r\na=ice-ufrag:xwIt\r\na=ice-pwd:WnJ0omUfzZhL3WX63jJH5mU8\r\na=ice-options:trickle\r\na=fingerprint:sha-256 E5:61:EC:E3:D4:04:55:16:E9:47:95:A7:4E:0C:82:10:A2:70:82:4F:59:29:99:B2:66:40:F2:46:86:A6:23:A3\r\na=setup:actpass\r\na=mid:data\r\na=sctp-port:5000\r\na=max-message-size:262144\r\n',
   })
-  return promise_rejects(t, "InvalidAccessError", pc.setRemoteDescription(sd));
+  return promise_rejects_dom(t, "InvalidAccessError", pc.setRemoteDescription(sd));
 }, 'Local RTP channel, remote offer of SCTP channel');
 
 </script>
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-getStats-overrides.https.html b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-getStats-overrides.https.html
index 20bb02e..cd0d849 100644
--- a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-getStats-overrides.https.html
+++ b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-getStats-overrides.https.html
@@ -69,7 +69,7 @@
   let pc = new RTCPeerConnection();
   t.add_cleanup(() => pc.close());
   let nonsense = 5;
-  await promise_rejects(t, new TypeError(), pc.getStats(nonsense));
+  await promise_rejects_js(t, TypeError, pc.getStats(nonsense));
 }, 'Signature: getStats(nonsense) throws exception');
 
 promise_test(async t => {
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCRtpSender-setParameters.html b/third_party/blink/web_tests/fast/peerconnection/RTCRtpSender-setParameters.html
index 5c9bcb1d..8b47674 100644
--- a/third_party/blink/web_tests/fast/peerconnection/RTCRtpSender-setParameters.html
+++ b/third_party/blink/web_tests/fast/peerconnection/RTCRtpSender-setParameters.html
@@ -69,11 +69,9 @@
   videoParameters.encodings[0].active = true;
   videoParameters.encodings[0].priority = 'low';
   videoParameters.encodings[0].maxBitrate = 1337001;
-  return promise_rejects(
+  return promise_rejects_dom(
     t,
-    new DOMException(
-      "getParameters() needs to be called before setParameters().",
-      "InvalidStateError"),
+    "InvalidStateError",
     videoSender.setParameters(videoParameters));
 }, 'setParameters() with multiple calls after single getParameters()');
 
@@ -88,11 +86,9 @@
   let videoSender = pc1.addTrack(stream.getVideoTracks()[0], stream);
   await doSignalingHandshake(pc1, pc2);
 
-  return promise_rejects(
+  return promise_rejects_dom(
     t,
-    new DOMException(
-      "getParameters() needs to be called before setParameters().",
-      "InvalidStateError"),
+    "InvalidStateError",
     videoSender.setParameters({
       transactionId: "",
       codecs: [],
@@ -247,9 +243,8 @@
     let videoParameters = videoSender.getParameters();
     if (!check || check(videoParameters)) {
       transform(videoParameters);
-      return promise_rejects(t,
-        new DOMException("Read-only field modified in setParameters().",
-          "InvalidModificationError"),
+      return promise_rejects_dom(t,
+        "InvalidModificationError",
         videoSender.setParameters(videoParameters));
     }
   }).bind(undefined, testCase.name, testCase.transform, testCase.check),
@@ -268,9 +263,8 @@
     let audioParameters = audioSender.getParameters();
     if (!check || check(audioParameters)) {
       transform(audioParameters);
-      return promise_rejects(t,
-        new DOMException("Read-only field modified in setParameters().",
-          "InvalidModificationError"),
+      return promise_rejects_dom(t,
+          "InvalidModificationError",
           audioSender.setParameters(audioParameters));
     }
   }).bind(undefined, testCase.name, testCase.transform, testCase.check),
@@ -331,8 +325,8 @@
 
     let videoParameters = videoSender.getParameters();
     transform(videoParameters);
-      return promise_rejects(t,
-        new TypeError(),
+      return promise_rejects_js(t,
+        TypeError,
         videoSender.setParameters(videoParameters));
   }).bind(undefined, testCase.name, testCase.transform, testCase.check),
   'video setParameters() check for ' + testCase.name);
diff --git a/third_party/blink/web_tests/hid/hidDevice_openAndClose.html b/third_party/blink/web_tests/hid/hidDevice_openAndClose.html
index 57c3fb1..1765dc7 100644
--- a/third_party/blink/web_tests/hid/hidDevice_openAndClose.html
+++ b/third_party/blink/web_tests/hid/hidDevice_openAndClose.html
@@ -64,7 +64,7 @@
   assert_false(device.opened);
 
   await device.open();
-  return promise_rejects(t, 'InvalidStateError', device.open());
+  return promise_rejects_dom(t, 'InvalidStateError', device.open());
 }, 'Opening a device twice is an error');
 
 hid_test(async (t, fake) => {
@@ -81,7 +81,7 @@
   assert_false(device.opened);
 
   const firstRequest = device.open();
-  await promise_rejects(t, 'InvalidStateError', device.open());
+  await promise_rejects_dom(t, 'InvalidStateError', device.open());
   await firstRequest;
 }, 'Opening a device twice simultaneously is an error');
 
diff --git a/third_party/blink/web_tests/hid/hidDevice_reports.html b/third_party/blink/web_tests/hid/hidDevice_reports.html
index a00047a..af85f895 100644
--- a/third_party/blink/web_tests/hid/hidDevice_reports.html
+++ b/third_party/blink/web_tests/hid/hidDevice_reports.html
@@ -60,8 +60,8 @@
   const {device, fakeConnection} = await addAndOpenDevice(fake);
   // Simulate write() failure.
   fakeConnection.queueExpectedWrite(false, kReportId, kReportBytes);
-  await promise_rejects(t, 'NotAllowedError',
-                        device.sendReport(kReportId, kReportBytes));
+  await promise_rejects_dom(t, 'NotAllowedError',
+                            device.sendReport(kReportId, kReportBytes));
   fakeConnection.assertExpectationsMet();
 }, 'Failed sendReport rejects');
 
@@ -78,8 +78,8 @@
   const {device, fakeConnection} = await addAndOpenDevice(fake);
   // Simulate getFeatureReport() failure.
   fakeConnection.queueExpectedGetFeatureReport(false, kReportId, kReportBytes);
-  await promise_rejects(t, 'NotAllowedError',
-                        device.receiveFeatureReport(kReportId));
+  await promise_rejects_dom(t, 'NotAllowedError',
+                            device.receiveFeatureReport(kReportId));
   fakeConnection.assertExpectationsMet();
 }, 'Failed receiveFeatureReport rejects');
 
@@ -95,8 +95,8 @@
   const {device, fakeConnection} = await addAndOpenDevice(fake);
   // Simulate sendFeatureReport() failure.
   fakeConnection.queueExpectedSendFeatureReport(false, kReportId, kReportBytes);
-  await promise_rejects(t, 'NotAllowedError',
-                        device.sendFeatureReport(kReportId, kReportBytes));
+  await promise_rejects_dom(t, 'NotAllowedError',
+                            device.sendFeatureReport(kReportId, kReportBytes));
   fakeConnection.assertExpectationsMet();
 }, 'Failed sendFeatureReport rejects');
 
diff --git a/third_party/blink/web_tests/hid/hid_requestDevice.html b/third_party/blink/web_tests/hid/hid_requestDevice.html
index aed3546..1c09732 100644
--- a/third_party/blink/web_tests/hid/hid_requestDevice.html
+++ b/third_party/blink/web_tests/hid/hid_requestDevice.html
@@ -14,7 +14,7 @@
 const kTestProductId = 0xabcd;
 
 promise_test((t) => {
-  return promise_rejects(
+  return promise_rejects_dom(
       t, 'SecurityError', navigator.hid.requestDevice({filters: []}));
 }, 'requestDevice() rejects without a user gesture');
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/settings/geolocations-a11y-test-expected.txt b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/settings/geolocations-a11y-test-expected.txt
deleted file mode 100644
index 319c7af..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/settings/geolocations-a11y-test-expected.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-Tests accessibility in the settings tool geolocations pane using the axe-core linter.
-
-Running: testAddLocation
-Opened input box: true
-aXe violations: []
-
-
-Running: testNewLocationError
-Invalidating the Location name input
-Error message: Location name cannot be empty
-Invalidating the Latitude input
-Error message: Latitude must be a number
-Invalidating the Longitude input
-Error message: Longitude must be a number
-aXe violations: []
-
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/settings/geolocations-a11y-test.js b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/settings/geolocations-a11y-test.js
deleted file mode 100644
index 8483537..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/settings/geolocations-a11y-test.js
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  TestRunner.addResult(
-      'Tests accessibility in the settings tool geolocations pane using the axe-core linter.');
-
-  await TestRunner.loadModule('axe_core_test_runner');
-  await UI.viewManager.showView('emulation-geolocations');
-  const geolocationsWidget = await UI.viewManager.view('emulation-geolocations').widget();
-
-  async function testAddLocation() {
-    const addLocationButton = geolocationsWidget._defaultFocusedElement;
-    addLocationButton.click();
-
-    const newLocationInputs = geolocationsWidget._list._editor._controls;
-    TestRunner.addResult(`Opened input box: ${!!newLocationInputs}`);
-
-    await AxeCoreTestRunner.runValidation(geolocationsWidget.contentElement);
-  }
-
-  async function testNewLocationError() {
-    const locationsEditor = geolocationsWidget._list._editor;
-    const newLocationInputs = locationsEditor._controls;
-    const nameInput = newLocationInputs[0];
-    const latitudeInput = newLocationInputs[1];
-    const longitudeInput = newLocationInputs[2];
-    let errorMessage;
-
-    TestRunner.addResult(`Invalidating the ${nameInput.getAttribute('aria-label')} input`);
-    nameInput.blur();
-    errorMessage = locationsEditor._errorMessageContainer.textContent;
-    TestRunner.addResult(`Error message: ${errorMessage}`);
-
-    TestRunner.addResult(`Invalidating the ${latitudeInput.getAttribute('aria-label')} input`);
-    nameInput.value = 'location';
-    latitudeInput.value = 'a.a';
-    latitudeInput.dispatchEvent(new Event('input'));
-    errorMessage = locationsEditor._errorMessageContainer.textContent;
-    TestRunner.addResult(`Error message: ${errorMessage}`);
-
-    TestRunner.addResult(`Invalidating the ${longitudeInput.getAttribute('aria-label')} input`);
-    latitudeInput.value = '1.1';
-    longitudeInput.value = '1a.1';
-    longitudeInput.dispatchEvent(new Event('input'));
-    errorMessage = locationsEditor._errorMessageContainer.textContent;
-    TestRunner.addResult(`Error message: ${errorMessage}`);
-
-    await AxeCoreTestRunner.runValidation(geolocationsWidget.contentElement);
-  }
-
-  TestRunner.runAsyncTestSuite([testAddLocation, testNewLocationError]);
-})();
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/devtools/modules-load-initial-expected.txt b/third_party/blink/web_tests/http/tests/devtools/modules-load-initial-expected.txt
index 15c6280..e82c5e3c8 100644
--- a/third_party/blink/web_tests/http/tests/devtools/modules-load-initial-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/modules-load-initial-expected.txt
@@ -15,7 +15,8 @@
     mobile_throttling
     persistence
     platform
-    protocol
+    protocol_client
+    root
     sdk
     services
     text_utils
diff --git a/third_party/blink/web_tests/http/tests/devtools/network/load-resource-for-frontend-expected.txt b/third_party/blink/web_tests/http/tests/devtools/network/load-resource-for-frontend-expected.txt
index f7b741c..b965d8e 100644
--- a/third_party/blink/web_tests/http/tests/devtools/network/load-resource-for-frontend-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/network/load-resource-for-frontend-expected.txt
@@ -3,31 +3,31 @@
 
 Running: testNetworkResourceNonCached
 Loading resource from http://localhost:8080/devtools/network/resources/resource.php
-Success: 200
+Success: true
 Logged headers: cache-control, expires, pragma
 Content: "Hello world"
 
 Running: testNetworkResourceCached
 Loading resource from http://localhost:8080/devtools/network/resources/resource.php?cached=1
-Success: 200
+Success: true
 Logged headers: cache-control, expires, last-modified
 Content: "Hello world"
 
 Running: testNetworkResourceIfModifiedSince
 Loading resource from http://localhost:8080/devtools/network/resources/resource.php?cached=1
-Success: 304
+Success: false
 Logged headers: 
 Content: ""
 
 Running: testResourceFromIncorrectURL
 Loading resource from resource.php
-Success: 404
+Success: false
 Logged headers: 
 Content: ""
 
 Running: testResourceWithCookie
 Loading resource from http://127.0.0.1:8000/devtools/network/resources/print-cookie.php
-Success: 200
+Success: true
 Logged headers: cache-control, expires, pragma
 Content: "Cookie value: TestCookieValue."
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-in-multiple-workers-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-in-multiple-workers-expected.txt
deleted file mode 100644
index 5d66e56..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-in-multiple-workers-expected.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-Tests breakpoint works in multiple workers.
-Set different breakpoints and dump them
-breakpoint at 1
-breakpoint at 2 conditional
-breakpoint at 3 disabled
-Reload page and add script again and dump breakpoints
-Page reloaded.
-breakpoint at 1
-breakpoint at 2 conditional
-breakpoint at 3 disabled
-Added two more workers
-Show uiSourceCode and dump breakpoints
-breakpoint at 1
-breakpoint at 2 conditional
-Show uiSourceCode and dump breakpoints
-breakpoint at 1
-breakpoint at 2 conditional
-Show uiSourceCode and dump breakpoints
-breakpoint at 1
-breakpoint at 2 conditional
-breakpoint at 3 disabled
-Test breakpoint in each worker
-Script execution paused.
-Call stack:
-    0) onmessage (worker.js:2)
-Script execution resumed.
-Script execution paused.
-Call stack:
-    0) onmessage (worker.js:2)
-Script execution resumed.
-Script execution paused.
-Call stack:
-    0) onmessage (worker.js:2)
-Script execution resumed.
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-in-multiple-workers.js b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-in-multiple-workers.js
deleted file mode 100644
index 815df890..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-in-multiple-workers.js
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  TestRunner.addResult('Tests breakpoint works in multiple workers.');
-  await TestRunner.loadModule('sources_test_runner');
-  await TestRunner.showPanel('sources');
-
-  // Pairs of line number plus breakpoint decoration counts.
-  // We expected line 1-3 to have one decoration each.
-  const expectedDecorations = [[1, 1], [2, 1], [3, 1]];
-
-  await addWorker('resources/worker.js');
-  let workerSourceFrame = await SourcesTestRunner.showScriptSourcePromise('worker.js');
-  TestRunner.addResult('Set different breakpoints and dump them');
-  await SourcesTestRunner.runActionAndWaitForExactBreakpointDecorations(
-      workerSourceFrame, expectedDecorations, async () => {
-    await SourcesTestRunner.toggleBreakpoint(workerSourceFrame, 1, false);
-    await SourcesTestRunner.createNewBreakpoint(workerSourceFrame, 2, 'a === 3', true);
-    await SourcesTestRunner.createNewBreakpoint(workerSourceFrame, 3, '', false);
-  });
-
-  TestRunner.addResult('Reload page and add script again and dump breakpoints');
-  await TestRunner.reloadPagePromise();
-  await addWorker(TestRunner.url('resources/worker.js'));
-  let sourceFrameAfterReload = await SourcesTestRunner.showScriptSourcePromise('worker.js');
-  await SourcesTestRunner.waitForExactBreakpointDecorations(
-      sourceFrameAfterReload, expectedDecorations, true);
-  SourcesTestRunner.dumpDebuggerPluginBreakpoints(sourceFrameAfterReload);
-
-  TestRunner.addResult('Added two more workers');
-  await addWorker(TestRunner.url('resources/worker.js'));
-  await addWorker(TestRunner.url('resources/worker.js'));
-  const uiSourceCodes = await waitForNScriptSources('worker.js', 3);
-
-  // The disabled breakpoint on line 3 is not included in the newly added workers.
-  const expectedDecorationsArray = [
-      [[1, 1], [2, 1]],
-      [[1, 1], [2, 1]],
-      expectedDecorations
-  ];
-  let index = 0;
-  for (const uiSourceCode of uiSourceCodes) {
-    TestRunner.addResult('Show uiSourceCode and dump breakpoints');
-    const sourceFrame = await SourcesTestRunner.showUISourceCodePromise(uiSourceCode);
-    await SourcesTestRunner.waitForExactBreakpointDecorations(
-        sourceFrame, expectedDecorationsArray[index++], true);
-    SourcesTestRunner.dumpDebuggerPluginBreakpoints(sourceFrame);
-  }
-
-  TestRunner.addResult('Test breakpoint in each worker');
-  await SourcesTestRunner.startDebuggerTestPromise();
-  for (var i = 0; i < 3; ++i) {
-    const pausedPromise = SourcesTestRunner.waitUntilPausedPromise();
-
-    await TestRunner.evaluateInPageAsync(`window.workers[${i}].postMessage('')`);
-
-    const callFrames = await pausedPromise;
-    await SourcesTestRunner.captureStackTrace(callFrames);
-    await new Promise(resolve => SourcesTestRunner.resumeExecution(resolve));
-  }
-  SourcesTestRunner.completeDebuggerTest();
-
-  async function waitForNScriptSources(scriptName, N) {
-    while (true) {
-      let uiSourceCodes = UI.panels.sources._workspace.uiSourceCodes();
-      uiSourceCodes = uiSourceCodes.filter(uiSourceCode => uiSourceCode.project().type() !== Workspace.projectTypes.Service && uiSourceCode.name() === scriptName);
-      if (uiSourceCodes.length === N)
-        return uiSourceCodes;
-      await TestRunner.addSnifferPromise(Sources.SourcesView.prototype, '_addUISourceCode');
-    }
-  }
-
-  function addWorker(url) {
-    return TestRunner.evaluateInPageAsync(`
-      (function(){
-        window.workers = window.workers || [];
-        window.workers.push(new Worker('${url}'));
-      })();
-    `);
-  }
-})();
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/sourcemap-src-not-loaded-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/sourcemap-src-not-loaded-expected.txt
index f74d184..0c10bd7c 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/sourcemap-src-not-loaded-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/sourcemap-src-not-loaded-expected.txt
@@ -1,6 +1,6 @@
 Tests that an error loading a source-map-referred file will display an error message in the source panel.
 
-error: Could not load content for http://127.0.0.1:8000/devtools/sources/debugger-ui/resources/sourcemap-src-not-loaded.ts (HTTP status code: 404, net error code 0)
+error: Could not load content for http://127.0.0.1:8000/devtools/sources/debugger-ui/resources/sourcemap-src-not-loaded.ts (HTTP error: status code 404, net::ERR_HTTP_RESPONSE_CODE_FAILURE)
 JavaScript source file:
 var __extends = (this && this.__extends) || (function () {
     var extendStatics = function (d, b) {
@@ -32,5 +32,5 @@
 TypeScript source file:
 undefined
 TypeScript resolution error:
-Could not load content for http://127.0.0.1:8000/devtools/sources/debugger-ui/resources/sourcemap-src-not-loaded.ts (HTTP status code: 404, net error code 0)
+Could not load content for http://127.0.0.1:8000/devtools/sources/debugger-ui/resources/sourcemap-src-not-loaded.ts (HTTP error: status code 404, net::ERR_HTTP_RESPONSE_CODE_FAILURE)
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-details-expected.txt b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-details-expected.txt
index ae55d6b3..256ebcd 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-details-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-details-expected.txt
@@ -7,13 +7,13 @@
 Mime Type: string
 Encoded Data: string
 Decoded Body: 223 B
-Initiator: timeline-network-resource-details.js:18
+Initiator: ​
 URL: anImage.png
 Duration: string
 Request Method: GET
 Priority: Low
 Mime Type: string
 Encoded Data: string
-Decoded Body: 12.9 KB
+Decoded Body: 13.3 kB
 Preview: 
 
diff --git a/third_party/blink/web_tests/payments/payment-request-interface.html b/third_party/blink/web_tests/payments/payment-request-interface.html
index e9c5132..1899be8 100644
--- a/third_party/blink/web_tests/payments/payment-request-interface.html
+++ b/third_party/blink/web_tests/payments/payment-request-interface.html
@@ -270,7 +270,7 @@
 }, 'Array value for payment method specific data parameter should not throw');
 
 promise_test(function(t) {
-    return promise_rejects(t, 'InvalidStateError', new PaymentRequest([{'supportedMethods': 'foo'}], buildDetails()).abort());
+    return promise_rejects_dom(t, 'InvalidStateError', new PaymentRequest([{'supportedMethods': 'foo'}], buildDetails()).abort());
 }, 'abort() without show() should reject with error');
 
 generate_tests(assert_throws, [
diff --git a/third_party/blink/web_tests/wake-lock/wakelock-document-hidden.https.html b/third_party/blink/web_tests/wake-lock/wakelock-document-hidden.https.html
index 6b47191..2635b1f 100644
--- a/third_party/blink/web_tests/wake-lock/wakelock-document-hidden.https.html
+++ b/third_party/blink/web_tests/wake-lock/wakelock-document-hidden.https.html
@@ -12,7 +12,7 @@
 
   window.testRunner.setPageVisibility('hidden');
   assert_true(document.hidden);
-  return promise_rejects(t, "NotAllowedError", navigator.wakeLock.request('screen'));
+  return promise_rejects_dom(t, "NotAllowedError", navigator.wakeLock.request('screen'));
 }, "navigator.wakeLock.request('screen') fails when the document is hidden");
 
 // This test currently checks for Chromium-specific behavior: WakeLock::request()
@@ -31,7 +31,7 @@
   const screenLock = navigator.wakeLock.request('screen');
   window.testRunner.setPageVisibility('hidden');
   assert_true(document.hidden);
-  return promise_rejects(t, "NotAllowedError", screenLock);
+  return promise_rejects_dom(t, "NotAllowedError", screenLock);
 }, "navigator.wakeLock.request('screen') aborts when the page is hidden");
 
 promise_test(async t => {
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/matchable/subtree-visibility-matchable-007.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/matchable/subtree-visibility-matchable-007.html
new file mode 100644
index 0000000..055c6f8
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/matchable/subtree-visibility-matchable-007.html
@@ -0,0 +1,55 @@
+<!doctype HTML>
+
+<html class="reftest-wait">
+<meta charset="utf8">
+<title>subtree-visibility hidden-matchable + focus</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<link rel="match" href="../spacer-and-container-ref.html">
+<meta name="assert" content="focus does not scroll or focus element under hidden-matchable">
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+.spacer {
+  width: 150px;
+  height: 3000px;
+  background: lightblue;
+}
+#container {
+  width: 150px;
+  height: 150px;
+  background: lightblue;
+}
+#target {
+  position: relative;
+  top: 75px;
+
+  width: 50px;
+  height: 50px;
+  background: red;
+}
+.hidden {
+  subtree-visibility: hidden-matchable;
+}
+</style>
+
+<div class=spacer></div>
+<div id=container class=hidden>
+  <div id=target tabindex=0></div>
+</div>
+
+<script>
+function runTest() {
+  document.getElementById("target").focus();
+  requestAnimationFrame(takeScreenshot);
+}
+
+window.onload = requestAnimationFrame(() => {
+  requestAnimationFrame(() => {
+    requestAnimationFrame(() => {
+      runTest();
+    });
+  });
+});
+</script>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-015.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-015.html
index 3360599..34a94a1 100644
--- a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-015.html
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-015.html
@@ -4,7 +4,6 @@
 <title>Subtree Visibility: hit testing</title>
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://github.com/WICG/display-locking">
-<link rel="match" href="scroll-into-view-ref.html">
 <meta name="assert" content="subtree-visibility hidden prevents hit-testing in the subtree">
 
 <script src="/resources/testharness.js"></script>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-016.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-016.html
index 8d02bc6..99cfe1b 100644
--- a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-016.html
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-016.html
@@ -4,7 +4,6 @@
 <title>Subtree Visibility: hit testing (composited)</title>
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://github.com/WICG/display-locking">
-<link rel="match" href="scroll-into-view-ref.html">
 <meta name="assert" content="subtree-visibility hidden prevents hit-testing in the subtree">
 
 <script src="/resources/testharness.js"></script>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-017.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-017.html
index 1429c66..eeb0214 100644
--- a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-017.html
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-017.html
@@ -4,7 +4,6 @@
 <title>Subtree Visibility: hit testing (composited child)</title>
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://github.com/WICG/display-locking">
-<link rel="match" href="scroll-into-view-ref.html">
 <meta name="assert" content="subtree-visibility hidden prevents hit-testing in the subtree">
 
 <script src="/resources/testharness.js"></script>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-018.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-018.html
index 84b0f898..7df6c65 100644
--- a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-018.html
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-018.html
@@ -4,7 +4,6 @@
 <title>Subtree Visibility: hit testing (composited with a composited child)</title>
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://github.com/WICG/display-locking">
-<link rel="match" href="scroll-into-view-ref.html">
 <meta name="assert" content="subtree-visibility hidden prevents hit-testing in the subtree">
 
 <script src="/resources/testharness.js"></script>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-069-ref.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-069-ref.html
new file mode 100644
index 0000000..e05d2fe1
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-069-ref.html
@@ -0,0 +1,56 @@
+<!doctype HTML>
+<html class="reftest-wait">
+<meta charset="utf8">
+<title>CSS Subtree Visibility: hidden + scrollIntoView (reference)</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+.spacer {
+  height: 1000px;
+  background: lightgreen;
+}
+#container {
+  position: relative;
+  width: 150px;
+  background: lightblue;
+}
+#child {
+  width: 150px;
+  height: 300px;
+}
+#target {
+  position: absolute;
+  bottom: 0;
+
+  width: 50px;
+  height: 50px;
+  background: red;
+}
+</style>
+
+<div>top of the page</div>
+<div class=spacer></div>
+<div id=container>
+  <div id=child></div>
+  <div id=target tabindex=0></div>
+</div>
+<div class=spacer></div>
+<div>bottom of the page</div>
+
+<script>
+function runReference() {
+  document.getElementById("target").focus();
+  requestAnimationFrame(takeScreenshot);
+}
+
+window.onload = requestAnimationFrame(() => {
+  requestAnimationFrame(() => {
+    requestAnimationFrame(() => {
+      runReference();
+    });
+  });
+});
+</script>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-069.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-069.html
new file mode 100644
index 0000000..88d55b3
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-069.html
@@ -0,0 +1,63 @@
+<!doctype HTML>
+
+<html class="reftest-wait">
+<meta charset="utf8">
+<title>CSS Subtree Visibility: auto + focus</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<link rel="match" href="subtree-visibility-069-ref.html">
+<meta name="assert" content="focus() can focus auto skipped subtree elements">
+<meta name="assert" content="focus() scrolls after removing contain:size">
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+.spacer {
+  height: 1000px;
+  background: lightgreen;
+}
+#container {
+  width: 150px;
+  background: lightblue;
+  contain-intrinsic-size: 50px 150px;
+}
+#child {
+  width: 150px;
+  height: 300px;
+}
+#target {
+  position: absolute;
+  bottom: 0;
+
+  width: 50px;
+  height: 50px;
+  background: red;
+}
+.auto {
+  subtree-visibility: auto;
+}
+</style>
+
+<div>top of the page</div>
+<div class=spacer></div>
+<div id=container class=auto>
+  <div id=child></div>
+  <div id=target tabindex=0></div>
+</div>
+<div class=spacer></div>
+<div>bottom of the page</div>
+
+<script>
+function runTest() {
+  document.getElementById("target").focus();
+  requestAnimationFrame(takeScreenshot);
+}
+
+window.onload = requestAnimationFrame(() => {
+  requestAnimationFrame(() => {
+    requestAnimationFrame(() => {
+      runTest();
+    });
+  });
+});
+</script>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-070.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-070.html
new file mode 100644
index 0000000..3c557eb
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-070.html
@@ -0,0 +1,57 @@
+<!doctype HTML>
+
+<html class="reftest-wait">
+<meta charset="utf8">
+<title>CSS Subtree Visibility: auto + focus on display:none</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<link rel="match" href="spacer-and-container-ref.html">
+<meta name="assert" content="focus ignores display:none element in an auto subtree">
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+.spacer {
+  width: 150px;
+  height: 3000px;
+  background: lightblue;
+}
+#container {
+  width: 150px;
+  height: 150px;
+  background: lightblue;
+}
+#target {
+  display: none;
+
+  position: relative;
+  top: 75px;
+
+  width: 50px;
+  height: 50px;
+  background: red;
+}
+.auto {
+  subtree-visibility: auto;
+}
+</style>
+
+<div class=spacer></div>
+<div id=container class=auto>
+  <div id=target tabindex=0></div>
+</div>
+
+<script>
+function runTest() {
+  document.getElementById("target").focus();
+  requestAnimationFrame(takeScreenshot);
+}
+
+window.onload = requestAnimationFrame(() => {
+  requestAnimationFrame(() => {
+    requestAnimationFrame(() => {
+      runTest();
+    });
+  });
+});
+</script>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-071.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-071.html
new file mode 100644
index 0000000..4188c9c6
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-071.html
@@ -0,0 +1,57 @@
+<!doctype HTML>
+
+<html class="reftest-wait">
+<meta charset="utf8">
+<title>CSS Subtree Visibility: auto + focus on display:contents</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<link rel="match" href="spacer-and-container-ref.html">
+<meta name="assert" content="focus ignores display:contents element in an auto subtree">
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+.spacer {
+  width: 150px;
+  height: 3000px;
+  background: lightblue;
+}
+#container {
+  width: 150px;
+  height: 150px;
+  background: lightblue;
+}
+#target {
+  display: contents;
+
+  position: relative;
+  top: 75px;
+
+  width: 50px;
+  height: 50px;
+  background: red;
+}
+.auto {
+  subtree-visibility: auto;
+}
+</style>
+
+<div class=spacer></div>
+<div id=container class=auto>
+  <div id=target tabindex=0></div>
+</div>
+
+<script>
+function runTest() {
+  document.getElementById("target").focus();
+  requestAnimationFrame(takeScreenshot);
+}
+
+window.onload = requestAnimationFrame(() => {
+  requestAnimationFrame(() => {
+    requestAnimationFrame(() => {
+      runTest();
+    });
+  });
+});
+</script>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-072.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-072.html
new file mode 100644
index 0000000..c8dda80c
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-072.html
@@ -0,0 +1,55 @@
+<!doctype HTML>
+
+<html class="reftest-wait">
+<meta charset="utf8">
+<title>CSS Subtree Visibility: hidden + focus</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<link rel="match" href="spacer-and-container-ref.html">
+<meta name="assert" content="focus ignores element in a hidden subtree">
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+.spacer {
+  width: 150px;
+  height: 3000px;
+  background: lightblue;
+}
+#container {
+  width: 150px;
+  height: 150px;
+  background: lightblue;
+}
+#target {
+  position: relative;
+  top: 75px;
+
+  width: 50px;
+  height: 50px;
+  background: red;
+}
+.hidden {
+  subtree-visibility: hidden;
+}
+</style>
+
+<div class=spacer></div>
+<div id=container class=hidden>
+  <div id=target tabindex=0></div>
+</div>
+
+<script>
+function runTest() {
+  document.getElementById("target").focus();
+  requestAnimationFrame(takeScreenshot);
+}
+
+window.onload = requestAnimationFrame(() => {
+  requestAnimationFrame(() => {
+    requestAnimationFrame(() => {
+      runTest();
+    });
+  });
+});
+</script>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-073.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-073.html
new file mode 100644
index 0000000..19fd7623
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-073.html
@@ -0,0 +1,98 @@
+<!doctype HTML>
+<html>
+<meta charset="utf8">
+<title>Subtree Visibility: off-screen focus</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<meta name="assert" content="subtree-visibility auto element remains non-skipped when elements in its subtree have focus.">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body, html {
+  padding: 0;
+  margin: 0;
+}
+
+.spacer {
+  height: 3000px;
+  background: lightblue;
+}
+#container {
+  background: lightgreen;
+  contain-intrinsic-size: 50px 100px;
+  subtree-visibility: auto;
+}
+#focusable {
+  width: 10px;
+  height: 10px;
+}
+</style>
+
+<div class=spacer></div>
+<div id=container>
+  <div id=focusable tabindex=0></div>
+</div>
+<div class=spacer></div>
+
+<script>
+async_test((t) => {
+  // Initially container should be 3000px offscreen with contained height 100px.
+  function step1() {
+    const r = container.getBoundingClientRect();
+    t.step(() => {
+      assert_equals(r.y, 3000, "step1 offset");
+      assert_equals(r.height, 100, "step1 height");
+    });
+    
+    focusable.focus();
+    requestAnimationFrame(step2);
+  }
+  // After focusing the subtree, the container should be somewhere closer than
+  // 3000px (scrolled into view) and the height should be 10px, since it no
+  // longer has containment.
+  function step2() {
+    const r = container.getBoundingClientRect();
+    t.step(() => {
+      assert_less_than(r.y, 3000, "step2 offset");
+      assert_equals(r.height, 10, "step2 height");
+    });
+    document.scrollingElement.scrollTop = 0;
+    requestAnimationFrame(step3);
+  }
+  // After scrolling the document back to the top, the container should be back
+  // at 3000px but because its subtree is still focused, it should have height
+  // 10px.
+  function step3() {
+    const r = container.getBoundingClientRect();
+    t.step(() => {
+      assert_equals(r.y, 3000, "step3 offset");
+      assert_equals(r.height, 10, "step3 height");
+    });
+    requestAnimationFrame(step4);
+  }
+  // This is a repeat of step3, to ensure that this is a stable state.
+  function step4() {
+    const r = container.getBoundingClientRect();
+    t.step(() => {
+      assert_equals(r.y, 3000, "step4 offset");
+      assert_equals(r.height, 10, "step4 height");
+    });
+    focusable.blur();
+    requestAnimationFrame(step5);
+  }
+  // After blurring the focused element, we should go back to the contained
+  // height of 100px.
+  function step5() {
+    const r = container.getBoundingClientRect();
+    t.step(() => {
+      assert_equals(r.y, 3000, "step5 offset");
+      assert_equals(r.height, 100, "step5 height");
+    });
+    t.done();
+  }
+  step1();
+});
+</script>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_detached.https-expected.txt b/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_detached.https-expected.txt
deleted file mode 100644
index fe7d90a2..0000000
--- a/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_detached.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Check that navigator.xr.supportsSession and navigator.xr.requestSession reject on a detached navigator. promise_rejects is not defined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_detached.https.html b/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_detached.https.html
index 78a78438..9244cf3 100644
--- a/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_detached.https.html
+++ b/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_detached.https.html
@@ -11,9 +11,10 @@
 // This behavior is not in the webxr spec, so this is an internal-only test.
 promise_test((t) => {
   var nav_xr = window.frames[0].navigator.xr;
+  const dom_exception = window.frames[0].DOMException;
   document.getElementById("subframe").remove();
-  return promise_rejects(t, "InvalidStateError", nav_xr.supportsSession('inline'))
-      .then(() => promise_rejects(t, "InvalidStateError", nav_xr.requestSession('inline')));
+  return promise_rejects_dom(t, "InvalidStateError", dom_exception, nav_xr.supportsSession('inline'))
+      .then(() => promise_rejects_dom(t, "InvalidStateError", dom_exception, nav_xr.requestSession('inline')));
 }, "Check that navigator.xr.supportsSession and navigator.xr.requestSession reject on a detached navigator.");
 
 </script>
diff --git a/third_party/blink/web_tests/wpt_internal/webxr/xrDevice_supportsSession_immersive_unsupported.https-expected.txt b/third_party/blink/web_tests/wpt_internal/webxr/xrDevice_supportsSession_immersive_unsupported.https-expected.txt
deleted file mode 100644
index 7aa8de8..0000000
--- a/third_party/blink/web_tests/wpt_internal/webxr/xrDevice_supportsSession_immersive_unsupported.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL supportsSession rejects when options not supported promise_test: Unhandled rejection with value: object "ReferenceError: promise_rejects is not defined"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/wpt_internal/webxr/xrDevice_supportsSession_immersive_unsupported.https.html b/third_party/blink/web_tests/wpt_internal/webxr/xrDevice_supportsSession_immersive_unsupported.https.html
index caa9c23..fa7b215 100644
--- a/third_party/blink/web_tests/wpt_internal/webxr/xrDevice_supportsSession_immersive_unsupported.https.html
+++ b/third_party/blink/web_tests/wpt_internal/webxr/xrDevice_supportsSession_immersive_unsupported.https.html
@@ -13,7 +13,7 @@
       (t) => {
       return navigator.xr.test.simulateDeviceConnection(VALID_NON_IMMERSIVE_DEVICE)
         .then( (controller) => {
-          return promise_rejects(
+          return promise_rejects_dom(
             t,
             "NotSupportedError",
             navigator.xr.supportsSession('immersive-vr')
diff --git a/third_party/closure_compiler/externs/passwords_private.js b/third_party/closure_compiler/externs/passwords_private.js
index e4e47b6..667a1665 100644
--- a/third_party/closure_compiler/externs/passwords_private.js
+++ b/third_party/closure_compiler/externs/passwords_private.js
@@ -72,7 +72,6 @@
  * @typedef {{
  *   urls: !chrome.passwordsPrivate.UrlCollection,
  *   username: string,
- *   numCharactersInPassword: number,
  *   federationText: (string|undefined),
  *   id: number,
  *   fromAccountStore: boolean
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index fef2682..68fb0adf 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: unknown
-Revision: 1d75af9bf5918fa1c365a4ac696f038e6028a30b
+Revision: 311a5a2fdd5b6be8cee01b66991933397094204f
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
@@ -37,4 +37,3 @@
 Local Modifications:
  - codereview.settings has been excluded.
  - elf_image_reader_fuzzer is enabled only when use_fuzzing_engine is true.
- - cherry-pick upstream 296501351816
diff --git a/third_party/crashpad/crashpad/BUILD.gn b/third_party/crashpad/crashpad/BUILD.gn
index 3d0dc26..17c380e 100644
--- a/third_party/crashpad/crashpad/BUILD.gn
+++ b/third_party/crashpad/crashpad/BUILD.gn
@@ -52,6 +52,30 @@
       # TODO(fuchsia:46559): Fix the leaks and remove this.
       deps += [ "//build/config/sanitizers:suppress-lsan.DO-NOT-USE-THIS" ]
     }
+    if (crashpad_is_android) {
+      use_raw_android_executable = true
+
+      copy("crashpad_test_data") {
+        testonly = true
+        sources = [
+          "test/test_paths_test_data_root.txt",
+          "util/net/testdata/ascii_http_body.txt",
+          "util/net/testdata/binary_http_body.dat",
+        ]
+
+        outputs = [
+          "$root_out_dir/crashpad_test_data/{{source}}",
+        ]
+      }
+
+      deps += [ ":crashpad_test_data" ]
+
+      extra_dist_files = [
+        "$root_out_dir/crashpad_handler",
+        "$root_out_dir/crashpad_test_test_multiprocess_exec_test_child",
+        "$root_out_dir/crashpad_test_data",
+      ]
+    }
   }
 
   if (crashpad_is_in_fuchsia) {
diff --git a/third_party/crashpad/crashpad/DEPS b/third_party/crashpad/crashpad/DEPS
index f7355b78..8b958f1 100644
--- a/third_party/crashpad/crashpad/DEPS
+++ b/third_party/crashpad/crashpad/DEPS
@@ -25,7 +25,7 @@
 deps = {
   'buildtools':
       Var('chromium_git') + '/chromium/src/buildtools.git@' +
-      'afc5b798c72905e85f9991152be878714c579958',
+      '4164a305626786b1912d467003acf4c4995bec7d',
   'crashpad/third_party/edo/edo': {
       'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git@' +
       '243fc89ae95b24717d41f3786f6a9abeeef87c92',
diff --git a/third_party/crashpad/crashpad/snapshot/BUILD.gn b/third_party/crashpad/crashpad/snapshot/BUILD.gn
index 32d69a4..7a53b3a 100644
--- a/third_party/crashpad/crashpad/snapshot/BUILD.gn
+++ b/third_party/crashpad/crashpad/snapshot/BUILD.gn
@@ -17,6 +17,9 @@
 
 if (crashpad_is_in_chromium) {
   import("//build/config/compiler/compiler.gni")
+
+  # Prevent Chromium source assignment filters from being inherited.
+  set_sources_assignment_filter([])
 }
 
 static_library("snapshot") {
@@ -110,15 +113,20 @@
 
   if (crashpad_is_ios) {
     sources += [
+      "ios/memory_snapshot_ios.cc",
+      "ios/memory_snapshot_ios.h",
       "ios/module_snapshot_ios.cc",
       "ios/module_snapshot_ios.h",
       "ios/process_snapshot_ios.cc",
       "ios/process_snapshot_ios.h",
+      "ios/thread_snapshot_ios.cc",
+      "ios/thread_snapshot_ios.h",
+      "mac/cpu_context_mac.cc",
+      "mac/cpu_context_mac.h",
     ]
   }
 
   if (crashpad_is_linux || crashpad_is_android) {
-    set_sources_assignment_filter([])
     sources += [
       "linux/cpu_context_linux.cc",
       "linux/cpu_context_linux.h",
diff --git a/third_party/crashpad/crashpad/snapshot/ios/memory_snapshot_ios.cc b/third_party/crashpad/crashpad/snapshot/ios/memory_snapshot_ios.cc
new file mode 100644
index 0000000..e760465
--- /dev/null
+++ b/third_party/crashpad/crashpad/snapshot/ios/memory_snapshot_ios.cc
@@ -0,0 +1,65 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/ios/memory_snapshot_ios.h"
+
+namespace crashpad {
+namespace internal {
+
+void MemorySnapshotIOS::Initialize(vm_address_t address, vm_size_t size) {
+  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+  address_ = address;
+  size_ = base::checked_cast<size_t>(size);
+
+  // TODO(justincohen): This is temporary, as MemorySnapshotIOS will likely be
+  // able to point directly to the deserialized data dump rather than copying
+  // data around.
+  buffer_ = std::unique_ptr<uint8_t[]>(new uint8_t[size_]);
+  memcpy(buffer_.get(), reinterpret_cast<void*>(address_), size_);
+  INITIALIZATION_STATE_SET_VALID(initialized_);
+}
+
+uint64_t MemorySnapshotIOS::Address() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return address_;
+}
+
+size_t MemorySnapshotIOS::Size() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return size_;
+}
+
+bool MemorySnapshotIOS::Read(Delegate* delegate) const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+  if (size_ == 0) {
+    return delegate->MemorySnapshotDelegateRead(nullptr, size_);
+  }
+
+  return delegate->MemorySnapshotDelegateRead(buffer_.get(), size_);
+}
+
+const MemorySnapshot* MemorySnapshotIOS::MergeWithOtherSnapshot(
+    const MemorySnapshot* other) const {
+  CheckedRange<uint64_t, size_t> merged(0, 0);
+  if (!LoggingDetermineMergedRange(this, other, &merged))
+    return nullptr;
+
+  auto result = std::make_unique<MemorySnapshotIOS>();
+  result->Initialize(merged.base(), merged.size());
+  return result.release();
+}
+
+}  // namespace internal
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/ios/memory_snapshot_ios.h b/third_party/crashpad/crashpad/snapshot/ios/memory_snapshot_ios.h
new file mode 100644
index 0000000..be991056
--- /dev/null
+++ b/third_party/crashpad/crashpad/snapshot/ios/memory_snapshot_ios.h
@@ -0,0 +1,63 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_IOS_MEMORY_SNAPSHOT_IOS_H_
+#define CRASHPAD_SNAPSHOT_IOS_MEMORY_SNAPSHOT_IOS_H_
+
+#include "base/macros.h"
+#include "snapshot/memory_snapshot.h"
+#include "util/misc/address_types.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief A MemorySnapshot of a memory region.
+class MemorySnapshotIOS final : public MemorySnapshot {
+ public:
+  MemorySnapshotIOS() = default;
+  ~MemorySnapshotIOS() = default;
+
+  //! \brief Initializes the object.
+  //!
+  //! \param[in] address The base address of the memory region to snapshot.
+  //! \param[in] size The size of the memory region to snapshot.
+  void Initialize(vm_address_t address, vm_size_t size);
+
+  // MemorySnapshot:
+  uint64_t Address() const override;
+  size_t Size() const override;
+  bool Read(Delegate* delegate) const override;
+  const MemorySnapshot* MergeWithOtherSnapshot(
+      const MemorySnapshot* other) const override;
+
+ private:
+  template <class T>
+  friend const MemorySnapshot* MergeWithOtherSnapshotImpl(
+      const T* self,
+      const MemorySnapshot* other);
+
+  // TODO(justincohen): This is temporary until deserialization is worked out.
+  std::unique_ptr<uint8_t[]> buffer_;
+  vm_address_t address_;
+  vm_size_t size_;
+  InitializationStateDcheck initialized_;
+
+  DISALLOW_COPY_AND_ASSIGN(MemorySnapshotIOS);
+};
+
+}  // namespace internal
+}  // namespace crashpad
+
+#endif  // CRASHPAD_SNAPSHOT_IOS_MEMORY_SNAPSHOT_IOS_H_
diff --git a/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios.cc b/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios.cc
index 666fc19..27f9de4 100644
--- a/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios.cc
+++ b/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios.cc
@@ -26,6 +26,7 @@
 
 ProcessSnapshotIOS::ProcessSnapshotIOS()
     : ProcessSnapshot(),
+      threads_(),
       modules_(),
       report_id_(),
       client_id_(),
@@ -43,6 +44,7 @@
     return false;
   }
 
+  InitializeThreads();
   InitializeModules();
 
   INITIALIZATION_STATE_SET_VALID(initialized_);
@@ -96,7 +98,11 @@
 
 std::vector<const ThreadSnapshot*> ProcessSnapshotIOS::Threads() const {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
-  return std::vector<const ThreadSnapshot*>();
+  std::vector<const ThreadSnapshot*> threads;
+  for (const auto& thread : threads_) {
+    threads.push_back(thread.get());
+  }
+  return threads;
 }
 
 std::vector<const ModuleSnapshot*> ProcessSnapshotIOS::Modules() const {
@@ -140,6 +146,25 @@
   return nullptr;
 }
 
+void ProcessSnapshotIOS::InitializeThreads() {
+  mach_msg_type_number_t thread_count = 0;
+  const thread_act_array_t threads =
+      internal::ThreadSnapshotIOS::GetThreads(&thread_count);
+  for (uint32_t thread_index = 0; thread_index < thread_count; ++thread_index) {
+    thread_t thread = threads[thread_index];
+    auto thread_snapshot = std::make_unique<internal::ThreadSnapshotIOS>();
+    if (thread_snapshot->Initialize(thread)) {
+      threads_.push_back(std::move(thread_snapshot));
+    }
+    mach_port_deallocate(mach_task_self(), thread);
+  }
+  // TODO(justincohen): This dealloc above and below needs to move with the
+  // call to task_threads inside internal::ThreadSnapshotIOS::GetThreads.
+  vm_deallocate(mach_task_self(),
+                reinterpret_cast<vm_address_t>(threads),
+                sizeof(thread_t) * thread_count);
+}
+
 void ProcessSnapshotIOS::InitializeModules() {
   const dyld_all_image_infos* image_infos =
       internal::ModuleSnapshotIOS::DyldAllImageInfo();
diff --git a/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios.h b/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios.h
index 4ebcaf13..19d43b2 100644
--- a/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios.h
+++ b/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios.h
@@ -18,7 +18,9 @@
 #include <vector>
 
 #include "snapshot/ios/module_snapshot_ios.h"
+#include "snapshot/ios/thread_snapshot_ios.h"
 #include "snapshot/process_snapshot.h"
+#include "snapshot/thread_snapshot.h"
 #include "snapshot/unloaded_module_snapshot.h"
 
 namespace crashpad {
@@ -60,6 +62,10 @@
   // Initializes modules_ on behalf of Initialize().
   void InitializeModules();
 
+  // Initializes threads_ on behalf of Initialize().
+  void InitializeThreads();
+
+  std::vector<std::unique_ptr<internal::ThreadSnapshotIOS>> threads_;
   std::vector<std::unique_ptr<internal::ModuleSnapshotIOS>> modules_;
   UUID report_id_;
   UUID client_id_;
diff --git a/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios.cc b/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios.cc
new file mode 100644
index 0000000..a5e9696
--- /dev/null
+++ b/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios.cc
@@ -0,0 +1,472 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/ios/thread_snapshot_ios.h"
+
+#include "base/mac/mach_logging.h"
+#include "snapshot/mac/cpu_context_mac.h"
+
+namespace {
+
+#if defined(ARCH_CPU_X86_64)
+const thread_state_flavor_t kThreadStateFlavor = x86_THREAD_STATE64;
+const thread_state_flavor_t kFloatStateFlavor = x86_FLOAT_STATE64;
+const thread_state_flavor_t kDebugStateFlavor = x86_DEBUG_STATE64;
+#elif defined(ARCH_CPU_ARM64)
+const thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64;
+const thread_state_flavor_t kFloatStateFlavor = ARM_NEON_STATE64;
+#endif
+
+kern_return_t MachVMRegionRecurseDeepest(task_t task,
+                                         vm_address_t* address,
+                                         vm_size_t* size,
+                                         natural_t* depth,
+                                         vm_prot_t* protection,
+                                         unsigned int* user_tag) {
+  vm_region_submap_short_info_64 submap_info;
+  mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
+  while (true) {
+    kern_return_t kr = vm_region_recurse_64(
+        task,
+        address,
+        size,
+        depth,
+        reinterpret_cast<vm_region_recurse_info_t>(&submap_info),
+        &count);
+    if (kr != KERN_SUCCESS) {
+      return kr;
+    }
+
+    if (!submap_info.is_submap) {
+      *protection = submap_info.protection;
+      *user_tag = submap_info.user_tag;
+      return KERN_SUCCESS;
+    }
+
+    ++*depth;
+  }
+}
+
+//! \brief Adjusts the region for the red zone, if the ABI requires one.
+//!
+//! This method performs red zone calculation for CalculateStackRegion(). Its
+//! parameters are local variables used within that method, and may be
+//! modified as needed.
+//!
+//! Where a red zone is required, the region of memory captured for a thread’s
+//! stack will be extended to include the red zone below the stack pointer,
+//! provided that such memory is mapped, readable, and has the correct user
+//! tag value. If these conditions cannot be met fully, as much of the red
+//! zone will be captured as is possible while meeting these conditions.
+//!
+//! \param[in,out] start_address The base address of the region to begin
+//!     capturing stack memory from. On entry, \a start_address is the stack
+//!     pointer. On return, \a start_address may be decreased to encompass a
+//!     red zone.
+//! \param[in,out] region_base The base address of the region that contains
+//!     stack memory. This is distinct from \a start_address in that \a
+//!     region_base will be page-aligned. On entry, \a region_base is the
+//!     base address of a region that contains \a start_address. On return,
+//!     if \a start_address is decremented and is outside of the region
+//!     originally described by \a region_base, \a region_base will also be
+//!     decremented appropriately.
+//! \param[in,out] region_size The size of the region that contains stack
+//!     memory. This region begins at \a region_base. On return, if \a
+//!     region_base is decremented, \a region_size will be incremented
+//!     appropriately.
+//! \param[in] user_tag The Mach VM system’s user tag for the region described
+//!     by the initial values of \a region_base and \a region_size. The red
+//!     zone will only be allowed to extend out of the region described by
+//!     these initial values if the user tag is appropriate for stack memory
+//!     and the expanded region has the same user tag value.
+void LocateRedZone(vm_address_t* const start_address,
+                   vm_address_t* const region_base,
+                   vm_address_t* const region_size,
+                   const unsigned int user_tag) {
+  // x86_64 has a red zone. See AMD64 ABI 0.99.8,
+  // https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-r252.pdf#page=19,
+  // section 3.2.2, “The Stack Frame”.
+  // So does ARM64,
+  // https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html
+  // section "Red Zone".
+  constexpr vm_size_t kRedZoneSize = 128;
+  vm_address_t red_zone_base =
+      *start_address >= kRedZoneSize ? *start_address - kRedZoneSize : 0;
+  bool red_zone_ok = false;
+  if (red_zone_base >= *region_base) {
+    // The red zone is within the region already discovered.
+    red_zone_ok = true;
+  } else if (red_zone_base < *region_base && user_tag == VM_MEMORY_STACK) {
+    // Probe to see if there’s a region immediately below the one already
+    // discovered.
+    vm_address_t red_zone_region_base = red_zone_base;
+    vm_size_t red_zone_region_size;
+    natural_t red_zone_depth = 0;
+    vm_prot_t red_zone_protection;
+    unsigned int red_zone_user_tag;
+    kern_return_t kr = MachVMRegionRecurseDeepest(mach_task_self(),
+                                                  &red_zone_region_base,
+                                                  &red_zone_region_size,
+                                                  &red_zone_depth,
+                                                  &red_zone_protection,
+                                                  &red_zone_user_tag);
+    if (kr != KERN_SUCCESS) {
+      MACH_LOG(INFO, kr) << "vm_region_recurse";
+      *start_address = *region_base;
+    } else if (red_zone_region_base + red_zone_region_size == *region_base &&
+               (red_zone_protection & VM_PROT_READ) != 0 &&
+               red_zone_user_tag == user_tag) {
+      // The region containing the red zone is immediately below the region
+      // already found, it’s readable (not the guard region), and it has the
+      // same user tag as the region already found, so merge them.
+      red_zone_ok = true;
+      *region_base -= red_zone_region_size;
+      *region_size += red_zone_region_size;
+    }
+  }
+
+  if (red_zone_ok) {
+    // Begin capturing from the base of the red zone (but not the entire
+    // region that encompasses the red zone).
+    *start_address = red_zone_base;
+  } else {
+    // The red zone would go lower into another region in memory, but no
+    // region was found. Memory can only be captured to an address as low as
+    // the base address of the region already found.
+    *start_address = *region_base;
+  }
+}
+
+//! \brief Calculates the base address and size of the region used as a
+//!     thread’s stack.
+//!
+//! The region returned by this method may be formed by merging multiple
+//! adjacent regions in a process’ memory map if appropriate. The base address
+//! of the returned region may be lower than the \a stack_pointer passed in
+//! when the ABI mandates a red zone below the stack pointer.
+//!
+//! \param[in] stack_pointer The stack pointer, referring to the top (lowest
+//!     address) of a thread’s stack.
+//! \param[out] stack_region_size The size of the memory region used as the
+//!     thread’s stack.
+//!
+//! \return The base address (lowest address) of the memory region used as the
+//!     thread’s stack.
+vm_address_t CalculateStackRegion(vm_address_t stack_pointer,
+                                  vm_size_t* stack_region_size) {
+  // For pthreads, it may be possible to compute the stack region based on the
+  // internal _pthread::stackaddr and _pthread::stacksize. The _pthread struct
+  // for a thread can be located at TSD slot 0, or the known offsets of
+  // stackaddr and stacksize from the TSD area could be used.
+  vm_address_t region_base = stack_pointer;
+  vm_size_t region_size;
+  natural_t depth = 0;
+  vm_prot_t protection;
+  unsigned int user_tag;
+  kern_return_t kr = MachVMRegionRecurseDeepest(mach_task_self(),
+                                                &region_base,
+                                                &region_size,
+                                                &depth,
+                                                &protection,
+                                                &user_tag);
+  if (kr != KERN_SUCCESS) {
+    MACH_LOG(INFO, kr) << "mach_vm_region_recurse";
+    *stack_region_size = 0;
+    return 0;
+  }
+
+  if (region_base > stack_pointer) {
+    // There’s nothing mapped at the stack pointer’s address. Something may have
+    // trashed the stack pointer. Note that this shouldn’t happen for a normal
+    // stack guard region violation because the guard region is mapped but has
+    // VM_PROT_NONE protection.
+    *stack_region_size = 0;
+    return 0;
+  }
+
+  vm_address_t start_address = stack_pointer;
+
+  if ((protection & VM_PROT_READ) == 0) {
+    // If the region isn’t readable, the stack pointer probably points to the
+    // guard region. Don’t include it as part of the stack, and don’t include
+    // anything at any lower memory address. The code below may still possibly
+    // find the real stack region at a memory address higher than this region.
+    start_address = region_base + region_size;
+  } else {
+    // If the ABI requires a red zone, adjust the region to include it if
+    // possible.
+    LocateRedZone(&start_address, &region_base, &region_size, user_tag);
+
+    // Regardless of whether the ABI requires a red zone, capture up to
+    // kExtraCaptureSize additional bytes of stack, but only if present in the
+    // region that was already found.
+    constexpr vm_size_t kExtraCaptureSize = 128;
+    start_address = std::max(start_address >= kExtraCaptureSize
+                                 ? start_address - kExtraCaptureSize
+                                 : start_address,
+                             region_base);
+
+    // Align start_address to a 16-byte boundary, which can help readers by
+    // ensuring that data is aligned properly. This could page-align instead,
+    // but that might be wasteful.
+    constexpr vm_size_t kDesiredAlignment = 16;
+    start_address &= ~(kDesiredAlignment - 1);
+    DCHECK_GE(start_address, region_base);
+  }
+
+  region_size -= (start_address - region_base);
+  region_base = start_address;
+
+  vm_size_t total_region_size = region_size;
+
+  // The stack region may have gotten split up into multiple abutting regions.
+  // Try to coalesce them. This frequently happens for the main thread’s stack
+  // when setrlimit(RLIMIT_STACK, …) is called. It may also happen if a region
+  // is split up due to an mprotect() or vm_protect() call.
+  //
+  // Stack regions created by the kernel and the pthreads library will be marked
+  // with the VM_MEMORY_STACK user tag. Scanning for multiple adjacent regions
+  // with the same tag should find an entire stack region. Checking that the
+  // protection on individual regions is not VM_PROT_NONE should guarantee that
+  // this algorithm doesn’t collect map entries belonging to another thread’s
+  // stack: well-behaved stacks (such as those created by the kernel and the
+  // pthreads library) have VM_PROT_NONE guard regions at their low-address
+  // ends.
+  //
+  // Other stack regions may not be so well-behaved and thus if user_tag is not
+  // VM_MEMORY_STACK, the single region that was found is used as-is without
+  // trying to merge it with other adjacent regions.
+  if (user_tag == VM_MEMORY_STACK) {
+    vm_address_t try_address = region_base;
+    vm_address_t original_try_address;
+
+    while (try_address += region_size,
+           original_try_address = try_address,
+           (kr = MachVMRegionRecurseDeepest(mach_task_self(),
+                                            &try_address,
+                                            &region_size,
+                                            &depth,
+                                            &protection,
+                                            &user_tag) == KERN_SUCCESS) &&
+               try_address == original_try_address &&
+               (protection & VM_PROT_READ) != 0 &&
+               user_tag == VM_MEMORY_STACK) {
+      total_region_size += region_size;
+    }
+
+    if (kr != KERN_SUCCESS && kr != KERN_INVALID_ADDRESS) {
+      // Tolerate KERN_INVALID_ADDRESS because it will be returned when there
+      // are no more regions in the map at or above the specified |try_address|.
+      MACH_LOG(INFO, kr) << "vm_region_recurse";
+    }
+  }
+
+  *stack_region_size = total_region_size;
+  return region_base;
+}
+
+}  // namespace
+
+namespace crashpad {
+namespace internal {
+
+ThreadSnapshotIOS::ThreadSnapshotIOS()
+    : ThreadSnapshot(),
+      context_(),
+      stack_(),
+      thread_id_(0),
+      thread_specific_data_address_(0),
+      suspend_count_(0),
+      priority_(0),
+      initialized_() {}
+
+ThreadSnapshotIOS::~ThreadSnapshotIOS() {}
+
+// static
+thread_act_array_t ThreadSnapshotIOS::GetThreads(
+    mach_msg_type_number_t* count) {
+  thread_act_array_t threads;
+  kern_return_t kr = task_threads(mach_task_self(), &threads, count);
+  if (kr != KERN_SUCCESS) {
+    MACH_LOG(WARNING, kr) << "task_threads";
+  }
+  return threads;
+}
+
+bool ThreadSnapshotIOS::Initialize(thread_t thread) {
+  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+  // TODO(justincohen): Move the following thread_get_state, thread_get_info,
+  // thread_policy_get and CalculateStackRegion to the serialize-on-read
+  // section.
+  thread_basic_info basic_info;
+  thread_precedence_policy precedence;
+  vm_size_t stack_region_size;
+  vm_address_t stack_region_address;
+#if defined(ARCH_CPU_X86_64)
+  x86_thread_state64_t thread_state;
+  x86_float_state64_t float_state;
+  x86_debug_state64_t debug_state;
+  mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT;
+  mach_msg_type_number_t float_state_count = x86_FLOAT_STATE64_COUNT;
+  mach_msg_type_number_t debug_state_count = x86_DEBUG_STATE64_COUNT;
+#elif defined(ARCH_CPU_ARM64)
+  arm_thread_state64_t thread_state;
+  arm_neon_state64_t float_state;
+  mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT;
+  mach_msg_type_number_t float_state_count = ARM_NEON_STATE64_COUNT;
+#endif
+
+  kern_return_t kr =
+      thread_get_state(thread,
+                       kThreadStateFlavor,
+                       reinterpret_cast<thread_state_t>(&thread_state),
+                       &thread_state_count);
+  if (kr != KERN_SUCCESS) {
+    MACH_LOG(ERROR, kr) << "thread_get_state(" << kThreadStateFlavor << ")";
+  }
+
+  kr = thread_get_state(thread,
+                        kFloatStateFlavor,
+                        reinterpret_cast<thread_state_t>(&float_state),
+                        &float_state_count);
+  if (kr != KERN_SUCCESS) {
+    MACH_LOG(ERROR, kr) << "thread_get_state(" << kFloatStateFlavor << ")";
+  }
+
+#if defined(ARCH_CPU_X86_64)
+  kr = thread_get_state(thread,
+                        kDebugStateFlavor,
+                        reinterpret_cast<thread_state_t>(&debug_state),
+                        &debug_state_count);
+  if (kr != KERN_SUCCESS) {
+    MACH_LOG(ERROR, kr) << "thread_get_state(" << kDebugStateFlavor << ")";
+  }
+#endif
+
+  mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
+  kr = thread_info(thread,
+                   THREAD_BASIC_INFO,
+                   reinterpret_cast<thread_info_t>(&basic_info),
+                   &count);
+  if (kr != KERN_SUCCESS) {
+    MACH_LOG(WARNING, kr) << "thread_info(THREAD_BASIC_INFO)";
+  }
+
+  thread_identifier_info identifier_info;
+  count = THREAD_IDENTIFIER_INFO_COUNT;
+  kr = thread_info(thread,
+                   THREAD_IDENTIFIER_INFO,
+                   reinterpret_cast<thread_info_t>(&identifier_info),
+                   &count);
+  if (kr != KERN_SUCCESS) {
+    MACH_LOG(WARNING, kr) << "thread_info(THREAD_IDENTIFIER_INFO)";
+  }
+
+  count = THREAD_PRECEDENCE_POLICY_COUNT;
+  boolean_t get_default = FALSE;
+  kr = thread_policy_get(thread,
+                         THREAD_PRECEDENCE_POLICY,
+                         reinterpret_cast<thread_policy_t>(&precedence),
+                         &count,
+                         &get_default);
+  if (kr != KERN_SUCCESS) {
+    MACH_LOG(ERROR, kr) << "thread_policy_get";
+  }
+
+#if defined(ARCH_CPU_X86_64)
+  vm_address_t stack_pointer = thread_state.__rsp;
+#elif defined(ARCH_CPU_ARM64)
+  vm_address_t stack_pointer = thread_state.__sp;
+#endif
+  stack_region_address =
+      CalculateStackRegion(stack_pointer, &stack_region_size);
+
+  // TODO(justincohen): Assume the following will fill in snapshot data from
+  // a deserialized object.
+  thread_id_ = identifier_info.thread_id;
+  suspend_count_ = basic_info.suspend_count;
+  priority_ = precedence.importance;
+
+  // thread_identifier_info::thread_handle contains the base of the
+  // thread-specific data area, which on x86 and x86_64 is the thread’s base
+  // address of the %gs segment. 10.9.2 xnu-2422.90.20/osfmk/kern/thread.c
+  // thread_info_internal() gets the value from
+  // machine_thread::cthread_self, which is the same value used to set the
+  // %gs base in xnu-2422.90.20/osfmk/i386/pcb_native.c
+  // act_machine_switch_pcb().
+  //
+  // On ARM64 10.15.0 xnu-6153.11.26/osfmk/kern/thread.c, it sets
+  // thread_identifier_info_t::thread_handle to
+  // thread->machine.cthread_self, which is set to tsd_base in
+  // osfmk/arm64/pcb.c.
+  thread_specific_data_address_ = identifier_info.thread_handle;
+  stack_.Initialize(stack_region_address, stack_region_size);
+
+#if defined(ARCH_CPU_X86_64)
+  context_.architecture = kCPUArchitectureX86_64;
+  context_.x86_64 = &context_x86_64_;
+  InitializeCPUContextX86_64(&context_x86_64_,
+                             THREAD_STATE_NONE,
+                             nullptr,
+                             0,
+                             &thread_state,
+                             &float_state,
+                             &debug_state);
+#elif defined(ARCH_CPU_ARM64)
+  context_.architecture = kCPUArchitectureARM64;
+  context_.arm64 = &context_arm64_;
+  InitializeCPUContextARM64(&context_arm64_, &thread_state, &float_state);
+#endif
+
+  INITIALIZATION_STATE_SET_VALID(initialized_);
+  return true;
+}
+
+const CPUContext* ThreadSnapshotIOS::Context() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return &context_;
+}
+
+const MemorySnapshot* ThreadSnapshotIOS::Stack() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return &stack_;
+}
+
+uint64_t ThreadSnapshotIOS::ThreadID() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return thread_id_;
+}
+
+int ThreadSnapshotIOS::SuspendCount() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return suspend_count_;
+}
+
+int ThreadSnapshotIOS::Priority() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return priority_;
+}
+
+uint64_t ThreadSnapshotIOS::ThreadSpecificDataAddress() const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return thread_specific_data_address_;
+}
+
+std::vector<const MemorySnapshot*> ThreadSnapshotIOS::ExtraMemory() const {
+  return std::vector<const MemorySnapshot*>();
+}
+
+}  // namespace internal
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios.h b/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios.h
new file mode 100644
index 0000000..c6dde7e
--- /dev/null
+++ b/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios.h
@@ -0,0 +1,77 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_IOS_THREAD_SNAPSHOT_IOS_H_
+#define CRASHPAD_SNAPSHOT_IOS_THREAD_SNAPSHOT_IOS_H_
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "snapshot/cpu_context.h"
+#include "snapshot/ios/memory_snapshot_ios.h"
+#include "snapshot/thread_snapshot.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief A ThreadSnapshot of a thread on an iOS system.
+class ThreadSnapshotIOS final : public ThreadSnapshot {
+ public:
+  ThreadSnapshotIOS();
+  ~ThreadSnapshotIOS() override;
+
+  //! \brief Initializes the object.
+  //!
+  //! \brief thread The mach thread used to initialize this object.
+  bool Initialize(thread_t thread);
+
+  //! \brief Returns an array of thread_t threads.
+  //!
+  //! \param[out] count The number of threads returned.
+  //!
+  //! \return An array of of size \a count threads.
+  static thread_act_array_t GetThreads(mach_msg_type_number_t* count);
+
+  // ThreadSnapshot:
+  const CPUContext* Context() const override;
+  const MemorySnapshot* Stack() const override;
+  uint64_t ThreadID() const override;
+  int SuspendCount() const override;
+  int Priority() const override;
+  uint64_t ThreadSpecificDataAddress() const override;
+  std::vector<const MemorySnapshot*> ExtraMemory() const override;
+
+ private:
+#if defined(ARCH_CPU_X86_64)
+  CPUContextX86_64 context_x86_64_;
+#elif defined(ARCH_CPU_ARM64)
+  CPUContextARM64 context_arm64_;
+#else
+#error Port.
+#endif  // ARCH_CPU_X86_64
+  CPUContext context_;
+  MemorySnapshotIOS stack_;
+  uint64_t thread_id_;
+  uint64_t thread_specific_data_address_;
+  int suspend_count_;
+  int priority_;
+  InitializationStateDcheck initialized_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotIOS);
+};
+
+}  // namespace internal
+}  // namespace crashpad
+
+#endif  // CRASHPAD_SNAPSHOT_IOS_THREAD_SNAPSHOT_IOS_H_
diff --git a/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.cc b/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.cc
index c91e331..acec60ec 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.cc
@@ -436,6 +436,32 @@
 
 }  // namespace internal
 
+#elif defined(ARCH_CPU_ARM_FAMILY)
+
+namespace internal {
+
+void InitializeCPUContextARM64(CPUContextARM64* context,
+                               const arm_thread_state64_t* arm_thread_state64,
+                               const arm_neon_state64_t* arm_neon_state64) {
+  // The structures of context->regs and arm_thread_state64->__x are laid out
+  // identically for this copy, even though the members are organized
+  // differently.  Because of this difference, there can't be a static assert
+  // similar to the one below for fpsimd.
+  memcpy(context->regs, arm_thread_state64->__x, sizeof(context->regs));
+  context->sp = arm_thread_state64->__sp;
+  context->pc = arm_thread_state64->__pc;
+  context->spsr =
+      static_cast<decltype(context->spsr)>(arm_thread_state64->__cpsr);
+
+  static_assert(sizeof(context->fpsimd) == sizeof(arm_neon_state64->__v),
+                "fpsimd context size mismatch");
+  memcpy(context->fpsimd, arm_neon_state64->__v, sizeof(arm_neon_state64->__v));
+  context->fpsr = arm_neon_state64->__fpsr;
+  context->fpcr = arm_neon_state64->__fpcr;
+}
+
+}  // namespace internal
+
 #endif
 
 }  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.h b/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.h
index 30281c1..77aedb6 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.h
+++ b/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.h
@@ -108,6 +108,17 @@
                                 const x86_float_state64_t* x86_float_state64,
                                 const x86_debug_state64_t* x86_debug_state64);
 
+#elif defined(ARCH_CPU_ARM_FAMILY) || DOXYGEN
+//! \brief Initializes a CPUContextARM64 structure from native context
+//! structures.
+//!
+//! \param[out] context The CPUContextARM64 structure to initialize.
+//! \param[in] arm_thread_state64 The state of the thread’s integer registers.
+//! \param[in] arm_neon_state64 The state of the thread’s floating-point
+//!     registers.
+void InitializeCPUContextARM64(CPUContextARM64* context,
+                               const arm_thread_state64_t* arm_thread_state64,
+                               const arm_neon_state64_t* arm_neon_state64);
 #endif
 
 }  // namespace internal
diff --git a/third_party/crashpad/crashpad/test/gtest_main.cc b/third_party/crashpad/crashpad/test/gtest_main.cc
index 34394ad..ad3a095 100644
--- a/third_party/crashpad/crashpad/test/gtest_main.cc
+++ b/third_party/crashpad/crashpad/test/gtest_main.cc
@@ -73,6 +73,8 @@
   // runner.
   const bool use_chromium_test_launcher =
       !crashpad::test::WinChildProcess::IsChildProcess();
+#elif defined(OS_ANDROID)
+  constexpr bool use_chromium_test_launcher = false;
 #else  // OS_WIN
   constexpr bool use_chromium_test_launcher = true;
 #endif  // OS_WIN
diff --git a/third_party/crashpad/crashpad/test/test_paths.cc b/third_party/crashpad/crashpad/test/test_paths.cc
index 10bae7a2..dfe6c96 100644
--- a/third_party/crashpad/crashpad/test/test_paths.cc
+++ b/third_party/crashpad/crashpad/test/test_paths.cc
@@ -69,10 +69,15 @@
   // out/{Debug,Release} relative to the Crashpad root.
   base::FilePath executable_path;
   if (Paths::Executable(&executable_path)) {
+#if defined(OS_ANDROID)
+    base::FilePath candidate = executable_path.DirName()
+                               .Append("crashpad_test_data");
+#else
     base::FilePath candidate =
         base::FilePath(executable_path.DirName()
                            .Append(base::FilePath::kParentDirectory)
                            .Append(base::FilePath::kParentDirectory));
+#endif
     if (IsTestDataRoot(candidate)) {
       return candidate;
     }
diff --git a/third_party/crashpad/crashpad/util/fuchsia/scoped_task_suspend.cc b/third_party/crashpad/crashpad/util/fuchsia/scoped_task_suspend.cc
index 4b4ffe7..8591d558 100644
--- a/third_party/crashpad/crashpad/util/fuchsia/scoped_task_suspend.cc
+++ b/third_party/crashpad/crashpad/util/fuchsia/scoped_task_suspend.cc
@@ -41,7 +41,7 @@
   for (const auto& thread : GetThreadHandles(process)) {
     // We omit the crashed thread (blocked in an exception) as it is technically
     // not suspended, cf. ZX-3772.
-    zx_info_thread info;
+    zx_info_thread_t info;
     if (thread.get_info(
             ZX_INFO_THREAD, &info, sizeof(info), nullptr, nullptr) == ZX_OK) {
       if (info.state == ZX_THREAD_STATE_BLOCKED_EXCEPTION) {
@@ -52,8 +52,16 @@
     zx_signals_t observed = 0u;
     const zx_status_t wait_status = thread.wait_one(
         ZX_THREAD_SUSPENDED, zx::deadline_after(zx::msec(50)), &observed);
-    ZX_LOG_IF(ERROR, wait_status != ZX_OK, wait_status)
-        << "thread failed to suspend";
+    if (wait_status != ZX_OK) {
+      zx_info_thread_t info = {};
+      zx_status_t info_status = thread.get_info(
+          ZX_INFO_THREAD, &info, sizeof(info), nullptr, nullptr);
+      ZX_LOG(ERROR, wait_status) << "thread failed to suspend";
+      LOG(ERROR) << "Thread info status " << info_status;
+      if (info_status == ZX_OK) {
+        LOG(ERROR) << "Thread state " << info.state;
+      }
+    }
   }
 }
 
diff --git a/third_party/crashpad/crashpad/util/posix/process_info_test.cc b/third_party/crashpad/crashpad/util/posix/process_info_test.cc
index 6154ef7..69390fa 100644
--- a/third_party/crashpad/crashpad/util/posix/process_info_test.cc
+++ b/third_party/crashpad/crashpad/util/posix/process_info_test.cc
@@ -14,6 +14,7 @@
 
 #include "util/posix/process_info.h"
 
+#include <sys/utsname.h>
 #include <time.h>
 
 #include <algorithm>
@@ -21,6 +22,7 @@
 #include <string>
 #include <vector>
 
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "gtest/gtest.h"
@@ -29,6 +31,7 @@
 #include "test/multiprocess.h"
 #include "util/file/file_io.h"
 #include "util/misc/implicit_cast.h"
+#include "util/string/split_string.h"
 
 #if defined(OS_LINUX) || defined(OS_ANDROID)
 #include "util/linux/direct_ptrace_connection.h"
@@ -93,11 +96,39 @@
   time(&now);
   EXPECT_LE(start_time.tv_sec, now);
 
+  const std::vector<std::string>& expect_argv = GetMainArguments();
+
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+  // Prior to Linux 4.2, the kernel only allowed reading a single page from
+  // /proc/<pid>/cmdline, causing any further arguments to be truncated. Disable
+  // testing arguments in this case.
+  // TODO(jperaza): The main arguments are stored on the main thread's stack
+  // (and so should be included in dumps automatically), and
+  // ProcessInfo::Arguments() might be updated to read the arguments directly,
+  // rather than via procfs on older kernels.
+  utsname uts;
+  ASSERT_EQ(uname(&uts), 0) << ErrnoMessage("uname");
+  std::vector<std::string> parts = SplitString(uts.release, '.');
+  ASSERT_GE(parts.size(), 2u);
+
+  int major, minor;
+  ASSERT_TRUE(base::StringToInt(parts[0], &major));
+  ASSERT_TRUE(base::StringToInt(parts[1], &minor));
+
+  size_t argv_size = 0;
+  for (const auto& arg : expect_argv) {
+    argv_size += arg.size() + 1;
+  }
+
+  if ((major < 4 || (major == 4 && minor < 2)) &&
+      argv_size > static_cast<size_t>(getpagesize())) {
+    return;
+  }
+#endif  // OS_ANDROID || OS_LINUX
+
   std::vector<std::string> argv;
   ASSERT_TRUE(process_info.Arguments(&argv));
 
-  const std::vector<std::string>& expect_argv = GetMainArguments();
-
   // expect_argv always contains the initial view of the arguments at the time
   // the program was invoked. argv may contain this view, or it may contain the
   // current view of arguments after gtest argv processing. argv may be a subset
diff --git a/tools/md_browser/md_browser.py b/tools/md_browser/md_browser.py
index 39fef4f..74259f2f 100755
--- a/tools/md_browser/md_browser.py
+++ b/tools/md_browser/md_browser.py
@@ -141,7 +141,7 @@
     if path.startswith('/chromium/src/+/master'):
       path = path[len('/chromium/src/+/master'):]
 
-    full_path = os.path.realpath(os.path.join(self.server.top_level, path[1:]))
+    full_path = os.path.normpath(os.path.join(self.server.top_level, path[1:]))
 
     if not full_path.startswith(self.server.top_level):
       self._DoUnknown()
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index bf584458..bd14f853 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -19883,6 +19883,7 @@
   <int value="686" label="ReportDeviceBacklightInfo"/>
   <int value="687" label="ScrollToTextFragmentEnabled"/>
   <int value="688" label="ManagedGuestSessionAutoLaunchNotificationReduced"/>
+  <int value="689" label="SystemFeaturesDisableList"/>
 </enum>
 
 <enum name="EnterprisePolicyDeviceIdValidity">
@@ -28554,12 +28555,6 @@
   <int value="3" label="Only normal priority frames seen."/>
 </enum>
 
-<enum name="FrameReportType">
-  <int value="0" label="Presented Frame"/>
-  <int value="1" label="Missed Deadline Frame"/>
-  <int value="2" label="Dropped Frame"/>
-</enum>
-
 <enum name="FrameSiteInstanceProcessRelationship">
   <summary>
     Used to describe the relationship between frames hosted in a process,
@@ -42714,6 +42709,21 @@
   <int value="0" label="Success"/>
   <int value="1" label="Bad type"/>
   <int value="2" label="Bad action status"/>
+  <int value="3" label="Bad author"/>
+  <int value="4" label="Bad action"/>
+  <int value="5" label="Bad interaction counters"/>
+  <int value="6" label="Bad content ratings"/>
+  <int value="7" label="Bad identifiers"/>
+  <int value="8" label="Bad tv episode"/>
+  <int value="9" label="Bad play next candidate"/>
+  <int value="10" label="Bad images"/>
+</enum>
+
+<enum name="MediaFeedReadResult">
+  <int value="0" label="Success"/>
+  <int value="1" label="Bad user status"/>
+  <int value="2" label="Bad fetch result"/>
+  <int value="3" label="Bad logo"/>
 </enum>
 
 <enum name="MediaGalleriesUsageType">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index afd8167..33ac3be 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -26497,24 +26497,6 @@
   </summary>
 </histogram>
 
-<histogram name="CompositorLatency.MissedDeadlineFrame" units="microseconds"
-    expires_after="2021-03-16">
-  <owner>sadrul@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Tracks the duration of various stages in the pipeline as a single frame goes
-    through the various stages in the compositor. This is reported for frames
-    that went through all the compositor stages in chromium, and was presented
-    to the user, but were delayed and missed their deadline.
-
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution. When considering
-    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
-    solution.
-  </summary>
-</histogram>
-
 <histogram name="CompositorLatency.MissedFrame" units="microseconds"
     expires_after="2020-08-31">
   <obsolete>
@@ -26554,15 +26536,6 @@
   </summary>
 </histogram>
 
-<histogram name="CompositorLatency.Type" enum="FrameReportType"
-    expires_after="2021-03-16">
-  <owner>sadrul@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Whether frame is dropped or presented meeting or missing the deadline.
-  </summary>
-</histogram>
-
 <histogram name="ConfigureDisplays.External.Modeset.AttemptSucceeded"
     enum="BooleanSuccess" expires_after="2020-10-30">
   <owner>dcastagna@chromium.org</owner>
@@ -73010,6 +72983,16 @@
   </summary>
 </histogram>
 
+<histogram name="Media.Feeds.Feed.ReadResult" enum="MediaFeedReadResult"
+    expires_after="2020-12-31">
+  <owner>beccahughes@chromium.org</owner>
+  <owner>media-dev@chromium.org</owner>
+  <summary>
+    Recorded when a Media Feed is loaded from the database. If there was an
+    error reading from the database then the result will include the reason.
+  </summary>
+</histogram>
+
 <histogram name="Media.Feeds.FeedItem.ReadResult"
     enum="MediaFeedItemReadResult" expires_after="2020-12-31">
   <owner>beccahughes@chromium.org</owner>
@@ -150853,6 +150836,24 @@
   </summary>
 </histogram>
 
+<histogram name="Signin.SignedInAccountsViewImpression" units="BooleanShown"
+    expires_after="2020-06-01">
+  <owner>fernandex@chromium.org</owner>
+  <owner>jlebel@chromium.org</owner>
+  <owner>msarda@chromium.org</owner>
+  <summary>
+    Track the number of times the view that displays the accounts that are
+    signed is shown. This view only exists on iOS and is shown every time the
+    application enters forground iff the accounts changed while it was in
+    background. Only bucket &quot;Shown&quot; is being recorded (this histogram
+    is a counter).
+
+    This histogram was added following crbug.com/1006717 (stable regression)
+    where the signed-in accounts view was repeatedly shown for a set of users.
+    It is used as a counter to monitor that there are no bumps in its timeline.
+  </summary>
+</histogram>
+
 <histogram name="Signin.SignedInDurationBeforeSignout" units="minutes"
     expires_after="M77">
   <obsolete>
@@ -185089,14 +185090,6 @@
   <affected-histogram name="CompositorLatency.DroppedFrame.TouchScroll"/>
   <affected-histogram name="CompositorLatency.DroppedFrame.WheelScroll"/>
   <affected-histogram name="CompositorLatency.MainThreadAnimation"/>
-  <affected-histogram
-      name="CompositorLatency.MissedDeadlineFrame.CompositorAnimation"/>
-  <affected-histogram
-      name="CompositorLatency.MissedDeadlineFrame.MainThreadAnimation"/>
-  <affected-histogram name="CompositorLatency.MissedDeadlineFrame.PinchZoom"/>
-  <affected-histogram name="CompositorLatency.MissedDeadlineFrame.RAF"/>
-  <affected-histogram name="CompositorLatency.MissedDeadlineFrame.TouchScroll"/>
-  <affected-histogram name="CompositorLatency.MissedDeadlineFrame.WheelScroll"/>
   <affected-histogram name="CompositorLatency.MissedFrame">
     <obsolete>
       Removed on 01/2020. MissedFrame changed to DroppedFrame for more clarity.
@@ -198326,7 +198319,6 @@
   <suffix name="WheelScroll" label="Mousewheel driven interaction"/>
   <affected-histogram name="CompositorLatency"/>
   <affected-histogram name="CompositorLatency.DroppedFrame"/>
-  <affected-histogram name="CompositorLatency.MissedDeadlineFrame"/>
   <affected-histogram name="CompositorLatency.MissedFrame">
     <obsolete>
       Removed on 01/2020. MissedFrame changed to DroppedFrame for more clarity.
diff --git a/tools/msan/blacklist.txt b/tools/msan/blacklist.txt
index 6b5fa004..37df576d 100644
--- a/tools/msan/blacklist.txt
+++ b/tools/msan/blacklist.txt
@@ -14,7 +14,7 @@
 
 # False positives due to use of linux_syscall_support. http://crbug.com/394028
 src:*/third_party/breakpad/breakpad/src/*
-src:*/components/crash/content/app/breakpad_linux.cc
+src:*/components/crash/core/app/breakpad_linux.cc
 
 # Reports from sigaltstack instrumentation. https://crbug.com/1050279
 fun:NaClSignalStackRegister
diff --git a/tools/perf/process_perf_results.py b/tools/perf/process_perf_results.py
index 45dc3f60..d4f6c63 100755
--- a/tools/perf/process_perf_results.py
+++ b/tools/perf/process_perf_results.py
@@ -289,8 +289,13 @@
   return benchmark_directory_map, benchmarks_shard_map_file
 
 
-def process_perf_results(output_json, configuration_name, build_properties,
-                         task_output_dir, smoke_test_mode, output_results_dir):
+def process_perf_results(output_json,
+                         configuration_name,
+                         build_properties,
+                         task_output_dir,
+                         smoke_test_mode,
+                         output_results_dir,
+                         skip_perf=False):
   """Process perf results.
 
   Consists of merging the json-test-format output, uploading the perf test
@@ -333,7 +338,7 @@
   benchmark_enabled_map = _handle_perf_json_test_results(
       benchmark_directory_map, test_results_list)
 
-  if not smoke_test_mode:
+  if not smoke_test_mode and not skip_perf:
     try:
       build_properties = json.loads(build_properties)
       if not configuration_name:
@@ -625,6 +630,7 @@
   parser.add_argument('--task-output-dir', help=argparse.SUPPRESS)
   parser.add_argument('-o', '--output-json', required=True,
                       help=argparse.SUPPRESS)
+  parser.add_argument('--skip-perf', help=argparse.SUPPRESS)
   parser.add_argument('json_files', nargs='*', help=argparse.SUPPRESS)
   parser.add_argument('--smoke-test-mode', action='store_true',
                       help='This test should be run in smoke test mode'
@@ -636,7 +642,8 @@
   try:
     return_code, _ = process_perf_results(
         args.output_json, args.configuration_name, args.build_properties,
-        args.task_output_dir, args.smoke_test_mode, output_results_dir)
+        args.task_output_dir, args.smoke_test_mode, output_results_dir,
+        args.skip_perf)
     return return_code
   finally:
     shutil.rmtree(output_results_dir)
diff --git a/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
index 619881d..42baa9e1 100644
--- a/ui/accessibility/platform/ax_platform_node_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
@@ -100,7 +100,7 @@
 
 #define EXPECT_UIA_BOOL_EQ(node, property_id, expected)               \
   {                                                                   \
-    ScopedVariant expectedVariant(expected, VT_BOOL);                 \
+    ScopedVariant expectedVariant(expected);                          \
     ASSERT_EQ(VT_BOOL, expectedVariant.type());                       \
     ScopedVariant actual;                                             \
     ASSERT_HRESULT_SUCCEEDED(                                         \
@@ -135,7 +135,7 @@
 
 #define EXPECT_UIA_INT_EQ(node, property_id, expected)              \
   {                                                                 \
-    ScopedVariant expectedVariant(expected, VT_I4);                 \
+    ScopedVariant expectedVariant(expected);                        \
     ASSERT_EQ(VT_I4, expectedVariant.type());                       \
     ScopedVariant actual;                                           \
     ASSERT_HRESULT_SUCCEEDED(                                       \
diff --git a/ui/display/manager/apply_content_protection_task.cc b/ui/display/manager/apply_content_protection_task.cc
index 71eac64..8732888 100644
--- a/ui/display/manager/apply_content_protection_task.cc
+++ b/ui/display/manager/apply_content_protection_task.cc
@@ -53,23 +53,24 @@
   // updated the state.
   for (DisplaySnapshot* display : hdcp_capable_displays) {
     native_display_delegate_->GetHDCPState(
-        *display, base::BindOnce(&ApplyContentProtectionTask::OnGetHDCPState,
-                                 weak_ptr_factory_.GetWeakPtr(), display));
+        *display,
+        base::BindOnce(&ApplyContentProtectionTask::OnGetHDCPState,
+                       weak_ptr_factory_.GetWeakPtr(), display->display_id()));
   }
 }
 
-void ApplyContentProtectionTask::OnGetHDCPState(DisplaySnapshot* display,
+void ApplyContentProtectionTask::OnGetHDCPState(int64_t display_id,
                                                 bool success,
                                                 HDCPState state) {
   success_ &= success;
 
   bool hdcp_enabled = state != HDCP_STATE_UNDESIRED;
-  bool hdcp_requested = GetDesiredProtectionMask(display->display_id()) &
-                        CONTENT_PROTECTION_METHOD_HDCP;
+  bool hdcp_requested =
+      GetDesiredProtectionMask(display_id) & CONTENT_PROTECTION_METHOD_HDCP;
 
   if (hdcp_enabled != hdcp_requested) {
     hdcp_requests_.emplace_back(
-        display, hdcp_requested ? HDCP_STATE_DESIRED : HDCP_STATE_UNDESIRED);
+        display_id, hdcp_requested ? HDCP_STATE_DESIRED : HDCP_STATE_UNDESIRED);
   }
 
   pending_requests_--;
@@ -91,9 +92,25 @@
     return;
   }
 
+  std::vector<DisplaySnapshot*> displays = layout_manager_->GetDisplayStates();
+  std::vector<DisplaySnapshot*> hdcped_displays;
+  // Lookup the displays again since display configuration may have changed.
   for (const auto& pair : hdcp_requests_) {
+    auto it = std::find_if(displays.begin(), displays.end(),
+                           [id = pair.first](DisplaySnapshot* display) {
+                             return id == display->display_id();
+                           });
+    if (it == displays.end()) {
+      std::move(callback_).Run(Status::FAILURE);
+      return;
+    }
+
+    hdcped_displays.push_back(*it);
+  }
+
+  for (size_t i = 0; i < hdcp_requests_.size(); ++i) {
     native_display_delegate_->SetHDCPState(
-        *pair.first, pair.second,
+        *hdcped_displays[i], hdcp_requests_[i].second,
         base::BindOnce(&ApplyContentProtectionTask::OnSetHDCPState,
                        weak_ptr_factory_.GetWeakPtr()));
   }
diff --git a/ui/display/manager/apply_content_protection_task.h b/ui/display/manager/apply_content_protection_task.h
index 7be81c1..5535f8d 100644
--- a/ui/display/manager/apply_content_protection_task.h
+++ b/ui/display/manager/apply_content_protection_task.h
@@ -36,7 +36,7 @@
   void Run() override;
 
  private:
-  void OnGetHDCPState(DisplaySnapshot* display, bool success, HDCPState state);
+  void OnGetHDCPState(int64_t display_id, bool success, HDCPState state);
   void OnSetHDCPState(bool success);
 
   uint32_t GetDesiredProtectionMask(int64_t display_id) const;
@@ -47,7 +47,7 @@
   const ContentProtectionManager::ContentProtections requests_;
   ResponseCallback callback_;
 
-  std::vector<std::pair<DisplaySnapshot*, HDCPState>> hdcp_requests_;
+  std::vector<std::pair<int64_t, HDCPState>> hdcp_requests_;
 
   bool success_ = true;
   size_t pending_requests_ = 0;
diff --git a/ui/display/manager/apply_content_protection_task_unittest.cc b/ui/display/manager/apply_content_protection_task_unittest.cc
index 864c2cd..b2718c8d 100644
--- a/ui/display/manager/apply_content_protection_task_unittest.cc
+++ b/ui/display/manager/apply_content_protection_task_unittest.cc
@@ -11,6 +11,8 @@
 
 #include "base/bind.h"
 #include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/fake/fake_display_snapshot.h"
 #include "ui/display/manager/display_layout_manager.h"
@@ -46,6 +48,8 @@
   void ResponseCallback(Response response) { response_ = response; }
 
  protected:
+  base::test::SingleThreadTaskEnvironment task_environment_;
+
   Response response_ = Response::KILLED;
   ActionLogger log_;
   TestNativeDisplayDelegate display_delegate_{&log_};
@@ -167,5 +171,34 @@
   EXPECT_EQ(kNoActions, log_.GetActionsAndClear());
 }
 
+TEST_F(ApplyContentProtectionTaskTest, ApplyHdcpWhileConfiguringDisplays) {
+  // Run async so the test can simulate a display change in the middle of
+  // updating HDCP state.
+  display_delegate_.set_run_async(true);
+
+  std::vector<std::unique_ptr<DisplaySnapshot>> displays;
+  displays.push_back(CreateDisplaySnapshot(DISPLAY_CONNECTION_TYPE_HDMI));
+  TestDisplayLayoutManager layout_manager(std::move(displays),
+                                          MULTIPLE_DISPLAY_STATE_SINGLE);
+
+  ContentProtectionManager::ContentProtections request;
+  request[1] = CONTENT_PROTECTION_METHOD_HDCP;
+  ApplyContentProtectionTask task(
+      &layout_manager, &display_delegate_, request,
+      base::BindOnce(&ApplyContentProtectionTaskTest::ResponseCallback,
+                     base::Unretained(this)));
+  task.Run();
+  // Content protection task asked for HDCP state. The response is queued on the
+  // task runner. At this point clear the display state. Content protection task
+  // should re-query state and respond with failure since the display is no
+  // longer present.
+  layout_manager.set_displays({});
+
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(Response::FAILURE, response_);
+  EXPECT_EQ(kNoActions, log_.GetActionsAndClear());
+}
+
 }  // namespace test
 }  // namespace display
diff --git a/ui/ozone/platform/wayland/host/wayland_clipboard.cc b/ui/ozone/platform/wayland/host/wayland_clipboard.cc
index dd8c232..0191f14 100644
--- a/ui/ozone/platform/wayland/host/wayland_clipboard.cc
+++ b/ui/ozone/platform/wayland/host/wayland_clipboard.cc
@@ -4,6 +4,8 @@
 
 #include "ui/ozone/platform/wayland/host/wayland_clipboard.h"
 
+#include <string>
+
 #include "ui/ozone/platform/wayland/host/gtk_primary_selection_device.h"
 #include "ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h"
 #include "ui/ozone/platform/wayland/host/gtk_primary_selection_source.h"
@@ -38,7 +40,7 @@
       clipboard_data_source_ = data_device_manager_->CreateSource();
     data_source = clipboard_data_source_.get();
   } else {
-    if (!primary_selection_device_manager_) {
+    if (!IsPrimarySelectionSupported()) {
       std::move(callback).Run();
       return;
     }
@@ -66,8 +68,10 @@
     if (!data_device_->RequestSelectionData(mime_type))
       SetData({}, mime_type);
   } else {
-    if (!primary_selection_device_->RequestSelectionData(mime_type))
+    if (!IsPrimarySelectionSupported() ||
+        !primary_selection_device_->RequestSelectionData(mime_type)) {
       SetData({}, mime_type);
+    }
   }
 }
 
@@ -91,8 +95,10 @@
   if (buffer == ClipboardBuffer::kCopyPaste) {
     std::move(callback).Run(data_device_->GetAvailableMimeTypes());
   } else {
-    DCHECK(primary_selection_device_);
-    std::move(callback).Run(primary_selection_device_->GetAvailableMimeTypes());
+    std::move(callback).Run(
+        IsPrimarySelectionSupported()
+            ? primary_selection_device_->GetAvailableMimeTypes()
+            : std::vector<std::string>{});
   }
 }
 
@@ -128,4 +134,8 @@
     update_sequence_cb_.Run(buffer);
 }
 
+bool WaylandClipboard::IsPrimarySelectionSupported() const {
+  return primary_selection_device_manager_ && primary_selection_device_;
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_clipboard.h b/ui/ozone/platform/wayland/host/wayland_clipboard.h
index cd95b60..a448249 100644
--- a/ui/ozone/platform/wayland/host/wayland_clipboard.h
+++ b/ui/ozone/platform/wayland/host/wayland_clipboard.h
@@ -57,6 +57,8 @@
   void UpdateSequenceNumber(ClipboardBuffer buffer);
 
  private:
+  bool IsPrimarySelectionSupported() const;
+
   // Holds a temporary instance of the client's clipboard content
   // so that we can asynchronously write to it.
   PlatformClipboard::DataMap* data_map_ = nullptr;
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.cc b/ui/views/bubble/bubble_dialog_delegate_view.cc
index ca848c0..9ddb71cd 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -163,13 +163,6 @@
   View* const anchor_view_;
 };
 
-BubbleDialogDelegateView::~BubbleDialogDelegateView() {
-  if (GetWidget())
-    GetWidget()->RemoveObserver(this);
-  SetLayoutManager(nullptr);
-  SetAnchorView(nullptr);
-}
-
 // static
 Widget* BubbleDialogDelegateView::CreateBubble(
     BubbleDialogDelegateView* bubble_delegate) {
@@ -187,6 +180,32 @@
   return bubble_widget;
 }
 
+BubbleDialogDelegateView::BubbleDialogDelegateView()
+    : BubbleDialogDelegateView(nullptr, BubbleBorder::TOP_LEFT) {}
+
+BubbleDialogDelegateView::BubbleDialogDelegateView(View* anchor_view,
+                                                   BubbleBorder::Arrow arrow,
+                                                   BubbleBorder::Shadow shadow)
+    : shadow_(shadow) {
+  SetArrow(arrow);
+  LayoutProvider* provider = LayoutProvider::Get();
+  // An individual bubble should override these margins if its layout differs
+  // from the typical title/text/buttons.
+  set_margins(provider->GetDialogInsetsForContentType(TEXT, TEXT));
+  title_margins_ = provider->GetInsetsMetric(INSETS_DIALOG_TITLE);
+  if (anchor_view)
+    SetAnchorView(anchor_view);
+  UpdateColorsFromTheme();
+  UMA_HISTOGRAM_BOOLEAN("Dialog.BubbleDialogDelegateView.Create", true);
+}
+
+BubbleDialogDelegateView::~BubbleDialogDelegateView() {
+  if (GetWidget())
+    GetWidget()->RemoveObserver(this);
+  SetLayoutManager(nullptr);
+  SetAnchorView(nullptr);
+}
+
 BubbleDialogDelegateView* BubbleDialogDelegateView::AsBubbleDialogDelegate() {
   return this;
 }
@@ -379,31 +398,6 @@
   SizeToContents();
 }
 
-BubbleDialogDelegateView::BubbleDialogDelegateView()
-    : BubbleDialogDelegateView(nullptr, BubbleBorder::TOP_LEFT) {}
-
-BubbleDialogDelegateView::BubbleDialogDelegateView(View* anchor_view,
-                                                   BubbleBorder::Arrow arrow,
-                                                   BubbleBorder::Shadow shadow)
-    : close_on_deactivate_(true),
-      anchor_widget_(nullptr),
-      shadow_(shadow),
-      color_explicitly_set_(false),
-      accept_events_(true),
-      adjust_if_offscreen_(true),
-      parent_window_(nullptr) {
-  SetArrow(arrow);
-  LayoutProvider* provider = LayoutProvider::Get();
-  // An individual bubble should override these margins if its layout differs
-  // from the typical title/text/buttons.
-  set_margins(provider->GetDialogInsetsForContentType(TEXT, TEXT));
-  title_margins_ = provider->GetInsetsMetric(INSETS_DIALOG_TITLE);
-  if (anchor_view)
-    SetAnchorView(anchor_view);
-  UpdateColorsFromTheme();
-  UMA_HISTOGRAM_BOOLEAN("Dialog.BubbleDialogDelegateView.Create", true);
-}
-
 gfx::Rect BubbleDialogDelegateView::GetBubbleBounds() {
   // The argument rect has its origin at the bubble's arrow anchor point;
   // its size is the preferred size of the bubble's client view (this view).
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.h b/ui/views/bubble/bubble_dialog_delegate_view.h
index 62bc3d2..2235541 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.h
+++ b/ui/views/bubble/bubble_dialog_delegate_view.h
@@ -50,11 +50,20 @@
     UNKNOWN,
   };
 
-  ~BubbleDialogDelegateView() override;
-
   // Create and initialize the bubble Widget(s) with proper bounds.
   static Widget* CreateBubble(BubbleDialogDelegateView* bubble_delegate);
 
+  BubbleDialogDelegateView();
+  // |shadow| usually doesn't need to be explicitly set, just uses the default
+  // argument. Unless on Mac when the bubble needs to use Views base shadow,
+  // override it with suitable bubble border type.
+  BubbleDialogDelegateView(
+      View* anchor_view,
+      BubbleBorder::Arrow arrow,
+      BubbleBorder::Shadow shadow = BubbleBorder::DIALOG_SHADOW);
+
+  ~BubbleDialogDelegateView() override;
+
   // DialogDelegateView:
   BubbleDialogDelegateView* AsBubbleDialogDelegate() override;
   bool ShouldShowCloseButton() const override;
@@ -151,14 +160,6 @@
   void OnAnchorBoundsChanged();
 
  protected:
-  BubbleDialogDelegateView();
-  // |shadow| usually doesn't need to be explicitly set, just uses the default
-  // argument. Unless on Mac when the bubble needs to use Views base shadow,
-  // override it with suitable bubble border type.
-  BubbleDialogDelegateView(
-      View* anchor_view,
-      BubbleBorder::Arrow arrow,
-      BubbleBorder::Shadow shadow = BubbleBorder::DIALOG_SHADOW);
 
   // Returns the desired arrow post-RTL mirroring if needed.
   BubbleBorder::Arrow arrow() const { return arrow_; }
@@ -225,12 +226,12 @@
   static bool devtools_dismiss_override_;
 
   // A flag controlling bubble closure on deactivation.
-  bool close_on_deactivate_;
+  bool close_on_deactivate_ = true;
 
   // The view and widget to which this bubble is anchored. AnchorViewObserver
   // is used to observe bounds changes and view deletion.
   std::unique_ptr<AnchorViewObserver> anchor_view_observer_;
-  Widget* anchor_widget_;
+  Widget* anchor_widget_ = nullptr;
   std::unique_ptr<Widget::PaintAsActiveLock> paint_as_active_lock_;
 
   // Whether the |anchor_widget_| (or the |highlighted_button_tracker_|, when
@@ -253,7 +254,7 @@
 
   // The background color of the bubble; and flag for when it's explicitly set.
   SkColor color_;
-  bool color_explicitly_set_;
+  bool color_explicitly_set_ = false;
 
   // The margins around the title.
   // TODO(tapted): Investigate deleting this when MD is default.
@@ -263,14 +264,14 @@
   gfx::Insets anchor_view_insets_;
 
   // Specifies whether the bubble (or its border) handles mouse events, etc.
-  bool accept_events_;
+  bool accept_events_ = true;
 
   // If true (defaults to true), the arrow may be mirrored and moved to fit the
   // bubble on screen better. It would be a no-op if the bubble has no arrow.
-  bool adjust_if_offscreen_;
+  bool adjust_if_offscreen_ = true;
 
   // Parent native window of the bubble.
-  gfx::NativeView parent_window_;
+  gfx::NativeView parent_window_ = nullptr;
 
   // If true, focus can navigate to the bubble from the anchor view. This takes
   // effect only when SetAnchorView is called.
diff --git a/ui/views/controls/button/button_unittest.cc b/ui/views/controls/button/button_unittest.cc
index fa0a6fde..1f44ad0e 100644
--- a/ui/views/controls/button/button_unittest.cc
+++ b/ui/views/controls/button/button_unittest.cc
@@ -180,6 +180,10 @@
 
     button_ = std::make_unique<TestButton>(false);
     widget_->SetContentsView(button_.get());
+
+    event_generator_ =
+        std::make_unique<ui::test::EventGenerator>(GetRootWindow(widget()));
+    event_generator_->set_assume_window_at_origin(false);
   }
 
   void TearDown() override {
@@ -219,6 +223,7 @@
   Widget* widget() { return widget_.get(); }
   TestButton* button() { return button_.get(); }
   TestButtonObserver* button_observer() { return button_observer_.get(); }
+  ui::test::EventGenerator* event_generator() { return event_generator_.get(); }
   void SetDraggedView(View* dragged_view) {
     widget_->dragged_view_ = dragged_view;
   }
@@ -227,18 +232,18 @@
   std::unique_ptr<Widget> widget_;
   std::unique_ptr<TestButton> button_;
   std::unique_ptr<TestButtonObserver> button_observer_;
+  std::unique_ptr<ui::test::EventGenerator> event_generator_;
 
   DISALLOW_COPY_AND_ASSIGN(ButtonTest);
 };
 
 // Tests that hover state changes correctly when visiblity/enableness changes.
 TEST_F(ButtonTest, HoverStateOnVisibilityChange) {
-  ui::test::EventGenerator generator(GetRootWindow(widget()));
-
-  generator.PressLeftButton();
+  event_generator()->MoveMouseTo(button()->GetBoundsInScreen().CenterPoint());
+  event_generator()->PressLeftButton();
   EXPECT_EQ(Button::STATE_PRESSED, button()->state());
 
-  generator.ReleaseLeftButton();
+  event_generator()->ReleaseLeftButton();
   EXPECT_EQ(Button::STATE_HOVERED, button()->state());
 
   button()->SetEnabled(false);
@@ -305,8 +310,7 @@
 // Tests that the hover state is preserved during a view hierarchy update of a
 // button's child View.
 TEST_F(ButtonTest, HoverStatePreservedOnDescendantViewHierarchyChange) {
-  ui::test::EventGenerator generator(GetRootWindow(widget()));
-  generator.MoveMouseTo(button()->GetBoundsInScreen().CenterPoint());
+  event_generator()->MoveMouseTo(button()->GetBoundsInScreen().CenterPoint());
 
   EXPECT_EQ(Button::STATE_HOVERED, button()->state());
   Label* child = new Label(base::string16());
@@ -456,12 +460,11 @@
   TestInkDrop* ink_drop = new TestInkDrop();
   CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false);
 
-  ui::test::EventGenerator generator(GetRootWindow(widget()));
-  generator.set_current_screen_location(gfx::Point(50, 50));
-  generator.PressLeftButton();
+  event_generator()->MoveMouseTo(button()->GetBoundsInScreen().CenterPoint());
+  event_generator()->PressLeftButton();
   EXPECT_EQ(InkDropState::ACTION_PENDING, ink_drop->GetTargetInkDropState());
 
-  generator.ReleaseLeftButton();
+  event_generator()->ReleaseLeftButton();
   EXPECT_EQ(InkDropState::ACTION_PENDING, ink_drop->GetTargetInkDropState());
 }
 
@@ -471,9 +474,8 @@
   TestInkDrop* ink_drop = new TestInkDrop();
   CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false);
 
-  ui::test::EventGenerator generator(GetRootWindow(widget()));
-  generator.set_current_screen_location(gfx::Point(50, 50));
-  generator.PressLeftButton();
+  event_generator()->MoveMouseTo(button()->GetBoundsInScreen().CenterPoint());
+  event_generator()->PressLeftButton();
   EXPECT_EQ(InkDropState::ACTION_PENDING, ink_drop->GetTargetInkDropState());
 
   EXPECT_EQ(Button::ButtonState::STATE_PRESSED, button()->state());
@@ -543,8 +545,7 @@
   TestInkDrop* ink_drop = new TestInkDrop();
   CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false);
 
-  ui::test::EventGenerator generator(GetRootWindow(widget()));
-  generator.MoveMouseToInHost(10, 10);
+  event_generator()->MoveMouseTo(button()->GetBoundsInScreen().CenterPoint());
   EXPECT_TRUE(ink_drop->is_hovered());
   button()->SetEnabled(false);
   EXPECT_FALSE(ink_drop->is_hovered());
@@ -578,8 +579,8 @@
   // Make sure that the button ink drop is hidden after the button gets removed.
   widget()->SetContentsView(&test_container);
   test_container.AddChildView(button());
-  ui::test::EventGenerator generator(GetRootWindow(widget()));
-  generator.MoveMouseToInHost(2, 2);
+  event_generator()->MoveMouseTo(button()->GetBoundsInScreen().origin());
+  event_generator()->MoveMouseBy(2, 2);
   EXPECT_TRUE(ink_drop->is_hovered());
   // Set ink-drop state to ACTIVATED to make sure that removing the container
   // sets it back to HIDDEN.
@@ -599,7 +600,7 @@
   // Trigger hovering and then remove from the indirect parent. This should
   // propagate down to Button which should remove the highlight effect.
   EXPECT_FALSE(ink_drop->is_hovered());
-  generator.MoveMouseToInHost(10, 10);
+  event_generator()->MoveMouseBy(8, 8);
   EXPECT_TRUE(ink_drop->is_hovered());
   // Set ink-drop state to ACTIVATED to make sure that removing the container
   // sets it back to HIDDEN.
@@ -864,9 +865,9 @@
 // and that the |observed_button| is passed to observer correctly.
 TEST_F(ButtonTest, ClickingButtonNotifiesObserverOfStateChanges) {
   CreateButtonWithObserver();
-  ui::test::EventGenerator generator(GetRootWindow(widget()));
 
-  generator.PressLeftButton();
+  event_generator()->MoveMouseTo(button()->GetBoundsInScreen().CenterPoint());
+  event_generator()->PressLeftButton();
   EXPECT_EQ(button_observer()->observed_button(), button());
   EXPECT_TRUE(button_observer()->state_changed());
 
@@ -874,7 +875,7 @@
   EXPECT_EQ(button_observer()->observed_button(), nullptr);
   EXPECT_FALSE(button_observer()->state_changed());
 
-  generator.ReleaseLeftButton();
+  event_generator()->ReleaseLeftButton();
   EXPECT_EQ(button_observer()->observed_button(), button());
   EXPECT_TRUE(button_observer()->state_changed());
 }
diff --git a/weblayer/BUILD.gn b/weblayer/BUILD.gn
index 2152e8215..bd87318 100644
--- a/weblayer/BUILD.gn
+++ b/weblayer/BUILD.gn
@@ -220,8 +220,8 @@
     "//components/captive_portal/core:buildflags",
     "//components/cdm/renderer",
     "//components/content_settings/core/browser",
-    "//components/crash/content/app",
     "//components/crash/content/browser",
+    "//components/crash/core/app",
     "//components/crash/core/common",
     "//components/embedder_support",
     "//components/find_in_page",
diff --git a/weblayer/browser/autofill_client_impl.cc b/weblayer/browser/autofill_client_impl.cc
index 8be320a..6b97b77 100644
--- a/weblayer/browser/autofill_client_impl.cc
+++ b/weblayer/browser/autofill_client_impl.cc
@@ -240,7 +240,7 @@
   return base::span<const autofill::Suggestion>();
 }
 
-void AutofillClientImpl::PinPopupViewUntilUpdate() {
+void AutofillClientImpl::PinPopupView() {
   NOTIMPLEMENTED();
 }
 
diff --git a/weblayer/browser/autofill_client_impl.h b/weblayer/browser/autofill_client_impl.h
index 3e14f74..fd96d32 100644
--- a/weblayer/browser/autofill_client_impl.h
+++ b/weblayer/browser/autofill_client_impl.h
@@ -113,7 +113,7 @@
       const std::vector<base::string16>& values,
       const std::vector<base::string16>& labels) override;
   base::span<const autofill::Suggestion> GetPopupSuggestions() const override;
-  void PinPopupViewUntilUpdate() override;
+  void PinPopupView() override;
   void UpdatePopup(const std::vector<autofill::Suggestion>& suggestions,
                    autofill::PopupType popup_type) override;
   void HideAutofillPopup(autofill::PopupHidingReason reason) override;
diff --git a/weblayer/common/crash_reporter/crash_reporter_client.cc b/weblayer/common/crash_reporter/crash_reporter_client.cc
index 3ae4fbd5..b2d6318 100644
--- a/weblayer/common/crash_reporter/crash_reporter_client.cc
+++ b/weblayer/common/crash_reporter/crash_reporter_client.cc
@@ -13,8 +13,8 @@
 #include "base/no_destructor.h"
 #include "base/path_service.h"
 #include "build/build_config.h"
-#include "components/crash/content/app/crash_reporter_client.h"
-#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/app/crash_reporter_client.h"
+#include "components/crash/core/app/crashpad.h"
 #include "components/version_info/android/channel_getter.h"
 #include "components/version_info/version_info.h"
 #include "components/version_info/version_info_values.h"