diff --git a/DEPS b/DEPS
index 4b077f7cb..490d5e1 100644
--- a/DEPS
+++ b/DEPS
@@ -204,7 +204,7 @@
   # 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': 'bc3c41b8742aa3430b5da0ac97de42f0fe25fe85',
+  'skia_revision': 'f326e4faef66d529904a7b53eb931fd576c82887',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -216,15 +216,15 @@
   # 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': '51a0e95d78eb22b1d87dfe7cb10a576690931986',
+  'angle_revision': '251ba5cb119ff2fed0e861cbc9b096c45004c1fa',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': 'bae138de13ac876cbca0e30a40b89dc808e2b29d',
+  'swiftshader_revision': '7c84426d2abe231f3087a2942fd831137eb3b0aa',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '30f45a6f043fc1bbd19eb4820261c45ad68cf1cc',
+  'pdfium_revision': 'e05f7b91476248cd3fd7d4b8903d2b0e06f86152',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -279,7 +279,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': 'd5886280043bd2cd11f348b3b12d80bda52590c9',
+  'devtools_frontend_revision': '48576261660b05a86df4b9b079ad81e474b8786c',
   # 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.
@@ -331,11 +331,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'f31b78e90e1ea17bf012edd22d2f35e9d89adb15',
+  'dawn_revision': 'd63d562d1b26e01b369d9a58eddeaccfaa688a02',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': 'd637672ffd93517a4e413b55dfb92121f25bf733',
+  'quiche_revision': '330a1eec8132c5a11f43b3f1a09ee48540dfa425',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -567,7 +567,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '62ccb5c72fb6822c7199abbeeafcba8dd61e34e0',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '0118f4a52f8cd03bb95f888a57e064f01096ff2e',
       'condition': 'checkout_ios',
   },
 
@@ -903,7 +903,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'aa250cf881dec006e0137dd40f079d2ce7092067',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '8295f4267ab3791e083692f4781be04ce54e41ca',
       'condition': 'checkout_chromeos',
   },
 
@@ -923,7 +923,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b28382109909606b061f2a9a04adf8196a47ce3b',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'e602c60a2b59e4a74033fe7580e50d9de9f5753e',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1373,7 +1373,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/aemu/linux-amd64',
-              'version': 'FMtthcz6qGrAgLXdxXJ6pP0iueMVqTXO6-6_LOrvTZUC'
+              'version': 'ZgEt2vZQcebeMBnTVKemE7gxtLtigVkD5iHfrp1QkDsC'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1617,7 +1617,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ff8800deaa33f678b4f53b3030e5deaf46716da5',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@be1d125a8b0666e93878cd2501f6f7a9f5f4f523',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 49635f3..c9abbc1 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1777,6 +1777,7 @@
     "//chromeos/components/phonehub",
     "//chromeos/components/quick_answers",
     "//chromeos/components/quick_answers/public/cpp:prefs",
+    "//chromeos/services/nearby/public/cpp",
     "//chromeos/ui/base",
     "//chromeos/ui/frame",
     "//services/viz/public/mojom",
@@ -2412,6 +2413,7 @@
     "//chromeos/services/assistant/public/mojom",
     "//chromeos/services/multidevice_setup/public/cpp:test_support",
     "//chromeos/services/multidevice_setup/public/mojom",
+    "//chromeos/services/nearby/public/cpp",
     "//chromeos/services/network_config/public/mojom",
     "//chromeos/system",
     "//chromeos/ui/frame:test_support",
diff --git a/ash/DEPS b/ash/DEPS
index 425f400a..2e6ba53fb 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -80,6 +80,7 @@
   "+chromeos/services/assistant/public" ,
   "+chromeos/services/assistant/test_support",
   "+chromeos/services/multidevice_setup/public",
+  "+chromeos/services/nearby/public",
   "+chromeos/services/network_config/public",
   "+chromeos/services/power/public",
   # TODO(https://crbug.com/644361): Eliminate this.
diff --git a/ash/ambient/ui/media_string_view.cc b/ash/ambient/ui/media_string_view.cc
index 03d5ce06..6f055bad 100644
--- a/ash/ambient/ui/media_string_view.cc
+++ b/ash/ambient/ui/media_string_view.cc
@@ -128,10 +128,6 @@
   return "MediaStringView";
 }
 
-void MediaStringView::VisibilityChanged(View* starting_from, bool is_visible) {
-  media_text_->layer()->GetAnimator()->StopAnimating();
-}
-
 void MediaStringView::OnViewBoundsChanged(views::View* observed_view) {
   UpdateMaskLayer();
 }
diff --git a/ash/ambient/ui/media_string_view.h b/ash/ambient/ui/media_string_view.h
index feac0d9..fe71978 100644
--- a/ash/ambient/ui/media_string_view.h
+++ b/ash/ambient/ui/media_string_view.h
@@ -41,7 +41,6 @@
 
   // views::View:
   const char* GetClassName() const override;
-  void VisibilityChanged(View* starting_from, bool is_visible) override;
 
   // views::ViewObserver:
   void OnViewBoundsChanged(views::View* observed_view) override;
diff --git a/ash/ambient/ui/media_string_view_unittest.cc b/ash/ambient/ui/media_string_view_unittest.cc
index ff9a3b5..e78d437 100644
--- a/ash/ambient/ui/media_string_view_unittest.cc
+++ b/ash/ambient/ui/media_string_view_unittest.cc
@@ -226,7 +226,7 @@
       GetMediaStringViewTextLabel()->layer()->GetAnimator()->is_animating());
 }
 
-TEST_F(MediaStringViewTest, PauseMediaWillStopAnimationWithLongText) {
+TEST_F(MediaStringViewTest, PauseMediaWillNotStopAnimationWithLongText) {
   ui::ScopedAnimationDurationScaleMode test_duration_mode(
       ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
 
@@ -253,7 +253,7 @@
   SimulateMediaPlaybackStateChanged(
       media_session::mojom::MediaPlaybackState::kPaused);
   EXPECT_FALSE(GetMediaStringView()->GetVisible());
-  EXPECT_FALSE(
+  EXPECT_TRUE(
       GetMediaStringViewTextLabel()->layer()->GetAnimator()->is_animating());
 }
 
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 70df28a..01210f10 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -1201,6 +1201,9 @@
       <message name="IDS_ASH_PHONE_HUB_CONTINUE_BROWSING_TAB_LABEL" desc="Label inside a Phone Hub continue browsing card that describes the index of the card, title of the webpage, and webpage url.">
         Browser tab <ph name="INDEX">$1<ex>1</ex></ph> of <ph name="TOTAL_COUNT">$2<ex>2</ex></ph>. <ph name="SITE_TITLE">$3<ex>Google</ex></ph>, <ph name="SITE_URL">$4<ex>https://google.com</ex></ph>
       </message>
+      <message name="IDS_ASH_PHONE_HUB_CONNECTED_DEVICE_SETTINGS_LABEL" desc="Label for the settings button in the Phone Hub UI that lets users know that the button routes to the Chrome OS Settings app page for connected devices (e.g. to disable Phone Hub features, enable/disable Smartlock, etc)">
+        Connected devices settings
+      </message>
 
       <message name="IDS_ASH_STYLUS_TOOLS_CAPTURE_REGION_ACTION" desc="Title of the capture region action in the stylus tools (a pop-up panel next to the status tray). This causes a partial screenshot to be taken (the user selects an area of the screen to take a screenshot of).">
         Capture region
@@ -1342,6 +1345,18 @@
       <message name="IDS_ASH_DESKS_DESK_4_MINI_VIEW_TITLE" desc="The label of the fourth virtual desk thumbnail.">
         Desk 4
       </message>
+      <message name="IDS_ASH_DESKS_DESK_5_MINI_VIEW_TITLE" desc="The label of the fourth virtual desk thumbnail.">
+        Desk 5
+      </message>
+      <message name="IDS_ASH_DESKS_DESK_6_MINI_VIEW_TITLE" desc="The label of the fourth virtual desk thumbnail.">
+        Desk 6
+      </message>
+      <message name="IDS_ASH_DESKS_DESK_7_MINI_VIEW_TITLE" desc="The label of the fourth virtual desk thumbnail.">
+        Desk 7
+      </message>
+      <message name="IDS_ASH_DESKS_DESK_8_MINI_VIEW_TITLE" desc="The label of the fourth virtual desk thumbnail.">
+        Desk 8
+      </message>
       <message name="IDS_ASH_DESKS_MAX_NUM_REACHED" desc="Message shown to users when they attempt to add a new virtual desk when the maximum number of desks has been reached.">
         Maximum number of desks reached.
       </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_DESKS_DESK_5_MINI_VIEW_TITLE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DESKS_DESK_5_MINI_VIEW_TITLE.png.sha1
new file mode 100644
index 0000000..4d8b722d
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_DESKS_DESK_5_MINI_VIEW_TITLE.png.sha1
@@ -0,0 +1 @@
+3cae01c90171ce6c26b47d9801a484112244c7e7
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_DESKS_DESK_6_MINI_VIEW_TITLE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DESKS_DESK_6_MINI_VIEW_TITLE.png.sha1
new file mode 100644
index 0000000..2370f53
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_DESKS_DESK_6_MINI_VIEW_TITLE.png.sha1
@@ -0,0 +1 @@
+742dbc05af4f811b58b920366be68bc3326a0fd1
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_DESKS_DESK_7_MINI_VIEW_TITLE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DESKS_DESK_7_MINI_VIEW_TITLE.png.sha1
new file mode 100644
index 0000000..118f97fe
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_DESKS_DESK_7_MINI_VIEW_TITLE.png.sha1
@@ -0,0 +1 @@
+9f0212b564680a499a3c8dbe4cd3269f4da627fd
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_DESKS_DESK_8_MINI_VIEW_TITLE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DESKS_DESK_8_MINI_VIEW_TITLE.png.sha1
new file mode 100644
index 0000000..367b67c3
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_DESKS_DESK_8_MINI_VIEW_TITLE.png.sha1
@@ -0,0 +1 @@
+7aa3b5c7a953290beb1b0188da4ef95f2114348d
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_CONNECTED_DEVICE_SETTINGS_LABEL.png.sha1 b/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_CONNECTED_DEVICE_SETTINGS_LABEL.png.sha1
new file mode 100644
index 0000000..4fa325c2
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_CONNECTED_DEVICE_SETTINGS_LABEL.png.sha1
@@ -0,0 +1 @@
+ad7a89eaab68906ed5e04fc18cf4f6cacd1ef94c
\ No newline at end of file
diff --git a/ash/public/cpp/ambient/ambient_metrics.cc b/ash/public/cpp/ambient/ambient_metrics.cc
index 54685f12..e7e1f9a 100644
--- a/ash/public/cpp/ambient/ambient_metrics.cc
+++ b/ash/public/cpp/ambient/ambient_metrics.cc
@@ -69,5 +69,15 @@
       /*buckets=*/kAmbientModeElapsedTimeHistogramBuckets);
 }
 
+void RecordAmbientModeTotalNumberOfAlbums(int num_albums) {
+  base::UmaHistogramCounts100("Ash.AmbientMode.TotalNumberOfAlbums",
+                              num_albums);
+}
+
+void RecordAmbientModeSelectedNumberOfAlbums(int num_albums) {
+  base::UmaHistogramCounts100("Ash.AmbientMode.SelectedNumberOfAlbums",
+                              num_albums);
+}
+
 }  // namespace ambient
 }  // namespace ash
diff --git a/ash/public/cpp/ambient/ambient_metrics.h b/ash/public/cpp/ambient/ambient_metrics.h
index 541ae3a..f9624e7 100644
--- a/ash/public/cpp/ambient/ambient_metrics.h
+++ b/ash/public/cpp/ambient/ambient_metrics.h
@@ -36,6 +36,10 @@
 ASH_PUBLIC_EXPORT void RecordAmbientModeTimeElapsed(base::TimeDelta time_delta,
                                                     bool tablet_mode);
 
+ASH_PUBLIC_EXPORT void RecordAmbientModeTotalNumberOfAlbums(int num_albums);
+
+ASH_PUBLIC_EXPORT void RecordAmbientModeSelectedNumberOfAlbums(int num_albums);
+
 }  // namespace ambient
 }  // namespace ash
 
diff --git a/ash/public/cpp/nearby_share_delegate.h b/ash/public/cpp/nearby_share_delegate.h
index 7eb7016..9a25570 100644
--- a/ash/public/cpp/nearby_share_delegate.h
+++ b/ash/public/cpp/nearby_share_delegate.h
@@ -27,6 +27,11 @@
   // Gets the current high visibility state from the NearbySharingService.
   virtual bool IsHighVisibilityOn() = 0;
 
+  // Returns true if EnableHighVisibility() has been called but
+  // NearbyShareDelegate has not yet been informed that the request has
+  // concluded.
+  virtual bool IsEnableHighVisibilityRequestActive() const = 0;
+
   // If high visibility is on, returns the time when the delegate
   // will turn it off. May return any value if high visibility is off.
   virtual base::TimeTicks HighVisibilityShutoffTime() const = 0;
diff --git a/ash/public/cpp/shell_window_ids.cc b/ash/public/cpp/shell_window_ids.cc
index 2dfcfd6..d37acf37 100644
--- a/ash/public/cpp/shell_window_ids.cc
+++ b/ash/public/cpp/shell_window_ids.cc
@@ -4,6 +4,8 @@
 
 #include "ash/public/cpp/shell_window_ids.h"
 
+#include <array>
+
 #include "ash/public/cpp/ash_features.h"
 #include "base/stl_util.h"
 
@@ -11,7 +13,11 @@
 
 namespace {
 
-constexpr std::array<int, 19> kActivatableContainersIds = {
+// TODO(minch): Consolidate the below lists when we launch Bento.
+
+// List of IDs of the containers whose windows are actiavated *before* windows
+// in the desks containers.
+constexpr std::array<int, 11> kPreDesksActivatableContainersIds = {
     kShellWindowId_OverlayContainer,
     kShellWindowId_LockSystemModalContainer,
     kShellWindowId_AccessibilityBubbleContainer,
@@ -23,10 +29,11 @@
     kShellWindowId_SystemModalContainer,
     kShellWindowId_AlwaysOnTopContainer,
     kShellWindowId_AppListContainer,
-    kShellWindowId_DefaultContainerDeprecated,
-    kShellWindowId_DeskContainerB,
-    kShellWindowId_DeskContainerC,
-    kShellWindowId_DeskContainerD,
+};
+
+// List of IDs of the containers whose windows are actiavated *after* windows in
+// the desks containers.
+constexpr std::array<int, 4> kPostDesksActivatableContainersIds = {
     kShellWindowId_HomeScreenContainer,
 
     // Launcher and status are intentionally checked after other containers
@@ -39,14 +46,30 @@
 
 }  // namespace
 
-// Note: this function avoids having a copy of |kActivatableContainersIds| in
-// each translation unit that references it.
-const std::array<int, 19>& GetActivatableShellWindowIds() {
-  return kActivatableContainersIds;
+std::vector<int> GetActivatableShellWindowIds() {
+  std::vector<int> ids(kPreDesksActivatableContainersIds.begin(),
+                       kPreDesksActivatableContainersIds.end());
+
+  // Add the desks containers IDs. Can't use desks_util since we're in
+  // ash/public here.
+  ids.emplace_back(kShellWindowId_DefaultContainerDeprecated);
+  ids.emplace_back(kShellWindowId_DeskContainerB);
+  ids.emplace_back(kShellWindowId_DeskContainerC);
+  ids.emplace_back(kShellWindowId_DeskContainerD);
+  if (features::IsBentoEnabled()) {
+    ids.emplace_back(kShellWindowId_DeskContainerE);
+    ids.emplace_back(kShellWindowId_DeskContainerF);
+    ids.emplace_back(kShellWindowId_DeskContainerG);
+    ids.emplace_back(kShellWindowId_DeskContainerH);
+  }
+
+  ids.insert(ids.end(), kPostDesksActivatableContainersIds.begin(),
+             kPostDesksActivatableContainersIds.end());
+  return ids;
 }
 
 bool IsActivatableShellWindowId(int id) {
-  return base::Contains(kActivatableContainersIds, id);
+  return base::Contains(GetActivatableShellWindowIds(), id);
 }
 
 }  // namespace ash
diff --git a/ash/public/cpp/shell_window_ids.h b/ash/public/cpp/shell_window_ids.h
index d0b6275..6b1e21d 100644
--- a/ash/public/cpp/shell_window_ids.h
+++ b/ash/public/cpp/shell_window_ids.h
@@ -5,7 +5,6 @@
 #ifndef ASH_PUBLIC_CPP_SHELL_WINDOW_IDS_H_
 #define ASH_PUBLIC_CPP_SHELL_WINDOW_IDS_H_
 
-#include <array>
 #include <vector>
 
 #include "ash/public/cpp/ash_public_export.h"
@@ -69,6 +68,10 @@
   kShellWindowId_DeskContainerB,
   kShellWindowId_DeskContainerC,
   kShellWindowId_DeskContainerD,
+  kShellWindowId_DeskContainerE,
+  kShellWindowId_DeskContainerF,
+  kShellWindowId_DeskContainerG,
+  kShellWindowId_DeskContainerH,
 
   // The container for top-level windows with the 'always-on-top' flag set.
   kShellWindowId_AlwaysOnTopContainer,
@@ -206,9 +209,9 @@
 // windows in containers appearing later in the list. This list is used by
 // AshFocusRules to determine which container to start the search from when
 // looking for the next activatable window.
-ASH_PUBLIC_EXPORT const std::array<int, 19>& GetActivatableShellWindowIds();
+ASH_PUBLIC_EXPORT std::vector<int> GetActivatableShellWindowIds();
 
-// Returns true if |id| is in |kActivatableContainersIds|.
+// Returns true if |id| is in GetActivatableShellWindowIds.
 ASH_PUBLIC_EXPORT bool IsActivatableShellWindowId(int id);
 
 }  // namespace ash
diff --git a/ash/public/cpp/test/test_nearby_share_delegate.cc b/ash/public/cpp/test/test_nearby_share_delegate.cc
index 98ea92c9..fa39818 100644
--- a/ash/public/cpp/test/test_nearby_share_delegate.cc
+++ b/ash/public/cpp/test/test_nearby_share_delegate.cc
@@ -20,6 +20,10 @@
   return is_high_visibility_on_;
 }
 
+bool TestNearbyShareDelegate::IsEnableHighVisibilityRequestActive() const {
+  return is_enable_high_visibility_request_active_;
+}
+
 base::TimeTicks TestNearbyShareDelegate::HighVisibilityShutoffTime() const {
   return high_visibility_shutoff_time_;
 }
diff --git a/ash/public/cpp/test/test_nearby_share_delegate.h b/ash/public/cpp/test/test_nearby_share_delegate.h
index ba744e3..9d936143 100644
--- a/ash/public/cpp/test/test_nearby_share_delegate.h
+++ b/ash/public/cpp/test/test_nearby_share_delegate.h
@@ -28,6 +28,7 @@
   // NearbyShareDelegate
   bool IsPodButtonVisible() override;
   bool IsHighVisibilityOn() override;
+  bool IsEnableHighVisibilityRequestActive() const override;
   base::TimeTicks HighVisibilityShutoffTime() const override;
   void EnableHighVisibility() override;
   void DisableHighVisibility() override;
@@ -37,6 +38,12 @@
     is_pod_button_visible_ = visible;
   }
 
+  void set_is_enable_high_visibility_request_active(
+      bool is_enable_high_visibility_request_active) {
+    is_enable_high_visibility_request_active_ =
+        is_enable_high_visibility_request_active;
+  }
+
   void set_is_high_visibility_on(bool on) { is_high_visibility_on_ = on; }
 
   void set_high_visibility_shutoff_time(base::TimeTicks time) {
@@ -47,6 +54,7 @@
 
  private:
   bool is_pod_button_visible_ = false;
+  bool is_enable_high_visibility_request_active_ = false;
   bool is_high_visibility_on_ = false;
   base::TimeTicks high_visibility_shutoff_time_;
   std::vector<Method> method_calls_;
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index 80d7b16..ea9de54 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -224,10 +224,6 @@
 void ReparentAllWindows(aura::Window* src, aura::Window* dst) {
   // Set of windows to move.
   constexpr int kContainerIdsToMove[] = {
-      kShellWindowId_DefaultContainerDeprecated,
-      kShellWindowId_DeskContainerB,
-      kShellWindowId_DeskContainerC,
-      kShellWindowId_DeskContainerD,
       kShellWindowId_AlwaysOnTopContainer,
       kShellWindowId_PipContainer,
       kShellWindowId_SystemModalContainer,
@@ -241,8 +237,11 @@
       kShellWindowId_LockScreenContainer,
   };
 
-  std::vector<int> container_ids{std::begin(kContainerIdsToMove),
-                                 std::end(kContainerIdsToMove)};
+  // Desk container ids are different depends on whether Bento feature is
+  // enabled or not.
+  std::vector<int> container_ids = desks_util::GetDesksContainersIds();
+  for (const int id : kContainerIdsToMove)
+    container_ids.emplace_back(id);
 
   // Check the display mode as this is also necessary when trasitioning between
   // mirror and unified mode.
diff --git a/ash/system/bluetooth/bluetooth_notification_controller.cc b/ash/system/bluetooth/bluetooth_notification_controller.cc
index 4c118483..b4b160f2 100644
--- a/ash/system/bluetooth/bluetooth_notification_controller.cc
+++ b/ash/system/bluetooth/bluetooth_notification_controller.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/public/cpp/nearby_share_delegate.h"
 #include "ash/public/cpp/notification_utils.h"
 #include "ash/public/cpp/system_tray_client.h"
 #include "ash/resources/vector_icons/vector_icons.h"
@@ -20,6 +21,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chromeos/services/nearby/public/cpp/nearby_client_uuids.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -296,8 +298,18 @@
 }
 
 void BluetoothNotificationController::NotifyAdapterDiscoverable() {
-  message_center::RichNotificationData optional;
+  // If Nearby Share has made the local device discoverable, do not
+  // unnecessarily display this notification.
+  // TODO(crbug.com/1155669): Generalize this logic to prevent leaking Nearby
+  // implementation details.
+  auto* nearby_share_delegate = Shell::Get()->nearby_share_delegate();
+  if (nearby_share_delegate &&
+      (nearby_share_delegate->IsEnableHighVisibilityRequestActive() ||
+       nearby_share_delegate->IsHighVisibilityOn())) {
+    return;
+  }
 
+  message_center::RichNotificationData optional;
   std::unique_ptr<Notification> notification = CreateSystemNotification(
       message_center::NOTIFICATION_TYPE_SIMPLE,
       kBluetoothDeviceDiscoverableNotificationId, base::string16() /* title */,
@@ -350,6 +362,16 @@
                                         false /* by_user */);
   }
 
+  // If the newly paired device is connected via a Nearby Connections client
+  // (e.g., Nearby Share), do not display this notification.
+  // TODO(crbug.com/1155669): Generalize this logic to prevent leaking Nearby
+  // implementation details.
+  for (const auto& uuid : device->GetUUIDs()) {
+    if (chromeos::nearby::IsNearbyClientUuid(uuid)) {
+      return;
+    }
+  }
+
   std::unique_ptr<Notification> notification = CreateSystemNotification(
       message_center::NOTIFICATION_TYPE_SIMPLE, GetPairedNotificationId(device),
       base::string16() /* title */,
diff --git a/ash/system/bluetooth/bluetooth_notification_controller_unittest.cc b/ash/system/bluetooth/bluetooth_notification_controller_unittest.cc
index c15f72d..446b86a 100644
--- a/ash/system/bluetooth/bluetooth_notification_controller_unittest.cc
+++ b/ash/system/bluetooth/bluetooth_notification_controller_unittest.cc
@@ -7,8 +7,10 @@
 #include <memory>
 #include <utility>
 
+#include "ash/public/cpp/test/test_nearby_share_delegate.h"
 #include "ash/public/cpp/test/test_system_tray_client.h"
 #include "ash/session/test_session_controller_client.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/tray/tray_popup_utils.h"
 #include "ash/test/ash_test_base.h"
@@ -16,17 +18,24 @@
 #include "base/containers/flat_map.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chromeos/services/nearby/public/cpp/nearby_client_uuids.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
 #include "device/bluetooth/test/mock_bluetooth_device.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/message_center/fake_message_center.h"
 
+using testing::NiceMock;
 using testing::Return;
 
 namespace ash {
 namespace {
 
+const char kTestAdapterName[] = "Chromebook";
+const char kTestAdapterAddress[] = "01:23:45:67:89:AB";
+
 class TestMessageCenter : public message_center::FakeMessageCenter {
  public:
   TestMessageCenter() = default;
@@ -51,19 +60,29 @@
 
   void SetUp() override {
     AshTestBase::SetUp();
+
+    mock_adapter_ =
+        base::MakeRefCounted<NiceMock<device::MockBluetoothAdapter>>();
+    ON_CALL(*mock_adapter_, IsPresent()).WillByDefault(Return(true));
+    ON_CALL(*mock_adapter_, IsPowered()).WillByDefault(Return(true));
+    ON_CALL(*mock_adapter_, GetName()).WillByDefault(Return(kTestAdapterName));
+    ON_CALL(*mock_adapter_, GetAddress())
+        .WillByDefault(Return(kTestAdapterAddress));
+    device::BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);
+
     notification_controller_ =
         std::make_unique<BluetoothNotificationController>(
             &test_message_center_);
     system_tray_client_ = GetSystemTrayClient();
 
     bluetooth_device_1_ =
-        std::make_unique<testing::NiceMock<device::MockBluetoothDevice>>(
-            nullptr /* adapter */, 0 /* bluetooth_class */, "name_1",
-            "address_1", false /* paired */, false /* connected */);
+        std::make_unique<NiceMock<device::MockBluetoothDevice>>(
+            mock_adapter_.get(), 0 /* bluetooth_class */, "name_1", "address_1",
+            false /* paired */, false /* connected */);
     bluetooth_device_2_ =
-        std::make_unique<testing::NiceMock<device::MockBluetoothDevice>>(
-            nullptr /* adapter */, 0 /* bluetooth_class */, "name_2",
-            "address_2", false /* paired */, false /* connected */);
+        std::make_unique<NiceMock<device::MockBluetoothDevice>>(
+            mock_adapter_.get(), 0 /* bluetooth_class */, "name_2", "address_2",
+            false /* paired */, false /* connected */);
   }
 
   void ClickPairedNotification(const device::BluetoothDevice* device) {
@@ -78,6 +97,26 @@
         by_user);
   }
 
+  void VerifyDiscoverableNotificationIsNotVisible() {
+    EXPECT_FALSE(test_message_center_.FindVisibleNotificationById(
+        BluetoothNotificationController::
+            kBluetoothDeviceDiscoverableNotificationId));
+  }
+
+  void VerifyDiscoverableNotificationIsVisible() {
+    message_center::Notification* visible_notification =
+        test_message_center_.FindVisibleNotificationById(
+            BluetoothNotificationController::
+                kBluetoothDeviceDiscoverableNotificationId);
+    EXPECT_TRUE(visible_notification);
+    EXPECT_EQ(base::string16(), visible_notification->title());
+    EXPECT_EQ(
+        l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERABLE,
+                                   base::UTF8ToUTF16(kTestAdapterName),
+                                   base::UTF8ToUTF16(kTestAdapterAddress)),
+        visible_notification->message());
+  }
+
   void VerifyPairedNotificationIsNotVisible(
       const device::BluetoothDevice* device) {
     EXPECT_FALSE(test_message_center_.FindVisibleNotificationById(
@@ -98,6 +137,11 @@
 
   // Run the notification controller to simulate showing a notification by
   // adding it to the TestMessageCenter.
+  void ShowDiscoverableNotification(
+      BluetoothNotificationController* notification_controller) {
+    notification_controller->NotifyAdapterDiscoverable();
+  }
+
   void ShowPairedNotification(
       BluetoothNotificationController* notification_controller,
       device::MockBluetoothDevice* bluetooth_device) {
@@ -105,6 +149,7 @@
   }
 
   TestMessageCenter test_message_center_;
+  scoped_refptr<device::MockBluetoothAdapter> mock_adapter_;
   std::unique_ptr<BluetoothNotificationController> notification_controller_;
   TestSystemTrayClient* system_tray_client_;
   std::unique_ptr<device::MockBluetoothDevice> bluetooth_device_1_;
@@ -113,6 +158,40 @@
   DISALLOW_COPY_AND_ASSIGN(BluetoothNotificationControllerTest);
 };
 
+TEST_F(BluetoothNotificationControllerTest, DiscoverableNotification) {
+  VerifyDiscoverableNotificationIsNotVisible();
+
+  ShowDiscoverableNotification(notification_controller_.get());
+
+  VerifyDiscoverableNotificationIsVisible();
+}
+
+TEST_F(BluetoothNotificationControllerTest,
+       DiscoverableNotification_NearbyShareEnableHighVisibilityRequestActive) {
+  VerifyDiscoverableNotificationIsNotVisible();
+
+  auto* nearby_share_delegate_ = static_cast<TestNearbyShareDelegate*>(
+      Shell::Get()->nearby_share_delegate());
+  nearby_share_delegate_->set_is_enable_high_visibility_request_active(true);
+
+  ShowDiscoverableNotification(notification_controller_.get());
+
+  VerifyDiscoverableNotificationIsNotVisible();
+}
+
+TEST_F(BluetoothNotificationControllerTest,
+       DiscoverableNotification_NearbyShareHighVisibilityOn) {
+  VerifyDiscoverableNotificationIsNotVisible();
+
+  auto* nearby_share_delegate_ = static_cast<TestNearbyShareDelegate*>(
+      Shell::Get()->nearby_share_delegate());
+  nearby_share_delegate_->set_is_high_visibility_on(true);
+
+  ShowDiscoverableNotification(notification_controller_.get());
+
+  VerifyDiscoverableNotificationIsNotVisible();
+}
+
 TEST_F(BluetoothNotificationControllerTest,
        PairedDeviceNotification_TapNotification) {
   // Show the notification to the user.
@@ -177,4 +256,18 @@
   EXPECT_EQ(0, system_tray_client_->show_bluetooth_settings_count());
 }
 
+TEST_F(BluetoothNotificationControllerTest,
+       PairedDeviceNotification_DeviceConnectionInitiatedByNearbyClient) {
+  VerifyPairedNotificationIsNotVisible(bluetooth_device_1_.get());
+
+  base::flat_set<device::BluetoothUUID> uuid_set;
+  uuid_set.insert(chromeos::nearby::GetNearbyClientUuids()[0]);
+  ON_CALL(*bluetooth_device_1_, GetUUIDs()).WillByDefault(Return(uuid_set));
+
+  ShowPairedNotification(notification_controller_.get(),
+                         bluetooth_device_1_.get());
+
+  VerifyPairedNotificationIsNotVisible(bluetooth_device_1_.get());
+}
+
 }  // namespace ash
diff --git a/ash/system/phonehub/phone_status_view.cc b/ash/system/phonehub/phone_status_view.cc
index 76ada20..e44fd36 100644
--- a/ash/system/phonehub/phone_status_view.cc
+++ b/ash/system/phonehub/phone_status_view.cc
@@ -162,7 +162,8 @@
   settings_button_ = new TopShortcutButton(
       base::BindRepeating(&Delegate::OpenConnectedDevicesSettings,
                           base::Unretained(delegate)),
-      kSystemMenuSettingsIcon, IDS_ASH_STATUS_TRAY_SETTINGS);
+      kSystemMenuSettingsIcon,
+      IDS_ASH_PHONE_HUB_CONNECTED_DEVICE_SETTINGS_LABEL);
   AddView(TriView::Container::END, settings_button_);
 
   separator_->SetVisible(delegate->CanOpenConnectedDeviceSettings());
diff --git a/ash/system/phonehub/quick_actions_view_unittest.cc b/ash/system/phonehub/quick_actions_view_unittest.cc
index 3502469..fe594cd 100644
--- a/ash/system/phonehub/quick_actions_view_unittest.cc
+++ b/ash/system/phonehub/quick_actions_view_unittest.cc
@@ -98,6 +98,11 @@
 }
 
 TEST_F(QuickActionsViewTest, SilencePhoneToggle) {
+  // Allow silence phone to be toggle-able.
+  dnd_controller()->SetDoNotDisturbStateInternal(
+      /*is_dnd_enabled=*/false,
+      /*can_request_new_dnd_state=*/true);
+
   // Initially, silence phone is not enabled.
   EXPECT_FALSE(dnd_controller()->IsDndEnabled());
 
diff --git a/ash/system/phonehub/silence_phone_quick_action_controller.cc b/ash/system/phonehub/silence_phone_quick_action_controller.cc
index 0cd58c14..2d1facb 100644
--- a/ash/system/phonehub/silence_phone_quick_action_controller.cc
+++ b/ash/system/phonehub/silence_phone_quick_action_controller.cc
@@ -58,6 +58,9 @@
 }
 
 void SilencePhoneQuickActionController::OnButtonPressed(bool is_now_enabled) {
+  // Button should not be pressed if it is disabled.
+  DCHECK(state_ != ActionState::kDisabled);
+
   LogQuickActionClick(is_now_enabled ? QuickAction::kToggleQuietModeOff
                                      : QuickAction::kToggleQuietModeOn);
 
@@ -74,13 +77,19 @@
 }
 
 void SilencePhoneQuickActionController::OnDndStateChanged() {
-  state_ =
-      dnd_controller_->IsDndEnabled() ? ActionState::kOn : ActionState::kOff;
-  SetItemState(state_);
+  if (!dnd_controller_->CanRequestNewDndState()) {
+    state_ = ActionState::kDisabled;
+  } else if (dnd_controller_->IsDndEnabled()) {
+    state_ = ActionState::kOn;
+  } else {
+    state_ = ActionState::kOff;
+  }
 
+  SetItemState(state_);
   // If |requested_state_| correctly resembles the current state, reset it and
-  // the timer.
-  if (state_ == requested_state_) {
+  // the timer. Reset also if the state is |kDisabled| since we are not
+  // requesting a state change.
+  if (state_ == requested_state_ || state_ == ActionState::kDisabled) {
     check_requested_state_timer_.reset();
     requested_state_.reset();
   }
@@ -88,21 +97,31 @@
 
 void SilencePhoneQuickActionController::SetItemState(ActionState state) {
   bool icon_enabled;
+  bool button_enabled;
   int state_text_id;
   int sub_label_text;
   switch (state) {
     case ActionState::kOff:
       icon_enabled = false;
+      button_enabled = true;
       state_text_id = IDS_ASH_PHONE_HUB_QUICK_ACTIONS_DISABLED_STATE_TOOLTIP;
       sub_label_text = IDS_ASH_PHONE_HUB_QUICK_ACTIONS_OFF_STATE;
       break;
     case ActionState::kOn:
       icon_enabled = true;
+      button_enabled = true;
       state_text_id = IDS_ASH_PHONE_HUB_QUICK_ACTIONS_ENABLED_STATE_TOOLTIP;
       sub_label_text = IDS_ASH_PHONE_HUB_QUICK_ACTIONS_ON_STATE;
       break;
+    case ActionState::kDisabled:
+      // TODO(1155784): Update disabled view with matching toggle-state colors.
+      icon_enabled = dnd_controller_->IsDndEnabled();
+      button_enabled = false;
+      state_text_id = IDS_ASH_PHONE_HUB_QUICK_ACTIONS_DISABLED_STATE_TOOLTIP;
+      sub_label_text = IDS_ASH_PHONE_HUB_QUICK_ACTIONS_NOT_AVAILABLE_STATE;
   }
 
+  item_->SetEnabled(button_enabled);
   item_->SetToggled(icon_enabled);
   item_->SetSubLabel(l10n_util::GetStringUTF16(sub_label_text));
   base::string16 tooltip_state =
@@ -125,4 +144,9 @@
   requested_state_.reset();
 }
 
+SilencePhoneQuickActionController::ActionState
+SilencePhoneQuickActionController::GetItemState() {
+  return state_;
+}
+
 }  // namespace ash
diff --git a/ash/system/phonehub/silence_phone_quick_action_controller.h b/ash/system/phonehub/silence_phone_quick_action_controller.h
index c44cd2fd..7ded968 100644
--- a/ash/system/phonehub/silence_phone_quick_action_controller.h
+++ b/ash/system/phonehub/silence_phone_quick_action_controller.h
@@ -51,13 +51,18 @@
   void OnDndStateChanged() override;
 
  private:
+  friend class SilencePhoneQuickActionControllerTest;
+
   // All the possible states that the silence phone button can be viewed. Each
   // state has a corresponding icon, labels and tooltip view.
-  enum class ActionState { kOff, kOn };
+  enum class ActionState { kOff, kOn, kDisabled };
 
   // Set the item (including icon, label and tooltips) to a certain state.
   void SetItemState(ActionState state);
 
+  // Retrieves the current state of the QuickActionItem. Used only for testing.
+  ActionState GetItemState();
+
   // Check to see if the requested state is similar to current state of the
   // phone. Make changes to item's state if necessary.
   void CheckRequestedState();
diff --git a/ash/system/phonehub/silence_phone_quick_action_controller_unittest.cc b/ash/system/phonehub/silence_phone_quick_action_controller_unittest.cc
index 0dae8d45..ca9a58a 100644
--- a/ash/system/phonehub/silence_phone_quick_action_controller_unittest.cc
+++ b/ash/system/phonehub/silence_phone_quick_action_controller_unittest.cc
@@ -47,6 +47,11 @@
     return dnd_controller_.get();
   }
 
+  bool IsButtonDisabled() {
+    return SilencePhoneQuickActionController::ActionState::kDisabled ==
+           controller_->GetItemState();
+  }
+
   size_t GetNumObserverCalls() { return num_calls_; }
 
  private:
@@ -63,16 +68,21 @@
   // Initially, there's one observer call during initiation.
   EXPECT_EQ(1u, GetNumObserverCalls());
 
+  // Allow the button to be enabled.
+  dnd_controller()->SetDoNotDisturbStateInternal(
+      /*is_dnd_enabled=*/false, /*can_request_new_dnd_state=*/true);
+  EXPECT_EQ(2u, GetNumObserverCalls());
+
   // Press the button to enabled state will trigger observer.
   controller()->OnButtonPressed(false /* is_now_enabled */);
-  EXPECT_EQ(2u, GetNumObserverCalls());
+  EXPECT_EQ(3u, GetNumObserverCalls());
 
   // Item state changed to enabled.
   EXPECT_TRUE(controller()->IsItemEnabled());
 
   // Press the button to disabled state will trigger observer.
   controller()->OnButtonPressed(true /* is_now_enabled */);
-  EXPECT_EQ(3u, GetNumObserverCalls());
+  EXPECT_EQ(4u, GetNumObserverCalls());
 
   // Item state changed to disabled.
   EXPECT_FALSE(controller()->IsItemEnabled());
@@ -80,4 +90,31 @@
   dnd_controller()->SetShouldRequestFail(false);
 }
 
+TEST_F(SilencePhoneQuickActionControllerTest, CanRequestNewDndState) {
+  // Set DoNotDisturbState to not allow any new request.
+  dnd_controller()->SetDoNotDisturbStateInternal(
+      /*is_dnd_enabled=*/false, /*can_request_new_dnd_state=*/false);
+  EXPECT_EQ(1u, GetNumObserverCalls());
+  // Since no new state requests are allowed, expect the button to be disabled.
+  EXPECT_FALSE(controller()->IsItemEnabled());
+  EXPECT_TRUE(IsButtonDisabled());
+
+  // Simulate that the phone updated its DoNotDisturb state, but is still in a
+  // work profile.
+  dnd_controller()->SetDoNotDisturbStateInternal(
+      /*is_dnd_enabled=*/true, /*can_request_new_dnd_state=*/false);
+  EXPECT_EQ(2u, GetNumObserverCalls());
+  // The button should still be disabled despite the phone has DoNotDisturb mode
+  // enabled. However, the underlying toggle has been flipped to enabled.
+  EXPECT_TRUE(controller()->IsItemEnabled());
+  EXPECT_TRUE(IsButtonDisabled());
+
+  // Flip toggle state back to enabled, but still in a work profile.
+  dnd_controller()->SetDoNotDisturbStateInternal(
+      /*is_dnd_enabled=*/false, /*can_request_new_dnd_state=*/false);
+  EXPECT_EQ(3u, GetNumObserverCalls());
+  EXPECT_FALSE(controller()->IsItemEnabled());
+  EXPECT_TRUE(IsButtonDisabled());
+}
+
 }  // namespace ash
diff --git a/ash/wm/ash_focus_rules.cc b/ash/wm/ash_focus_rules.cc
index 95c544a9..e0e8d7b 100644
--- a/ash/wm/ash_focus_rules.cc
+++ b/ash/wm/ash_focus_rules.cc
@@ -48,7 +48,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 // AshFocusRules, public:
 
-AshFocusRules::AshFocusRules() = default;
+AshFocusRules::AshFocusRules()
+    : activatable_container_ids_(GetActivatableShellWindowIds()) {}
 
 AshFocusRules::~AshFocusRules() = default;
 
@@ -63,11 +64,11 @@
 
   // The window must exist within a container that supports activation.
   // The window cannot be blocked by a modal transient.
-  return IsActivatableShellWindowId(window->parent()->id());
+  return base::Contains(activatable_container_ids_, window->parent()->id());
 }
 
 bool AshFocusRules::SupportsChildActivation(const aura::Window* window) const {
-  return IsActivatableShellWindowId(window->id());
+  return base::Contains(activatable_container_ids_, window->id());
 }
 
 bool AshFocusRules::IsWindowConsideredVisibleForActivation(
@@ -157,10 +158,10 @@
   aura::Window* root = starting_window->GetRootWindow();
   if (!root)
     root = Shell::GetRootWindowForNewWindows();
-  const auto& container_ids = GetActivatableShellWindowIds();
-  const int container_count = container_ids.size();
+  const int container_count = activatable_container_ids_.size();
   for (int i = 0; i < container_count; i++) {
-    aura::Window* container = Shell::GetContainer(root, container_ids[i]);
+    aura::Window* container =
+        Shell::GetContainer(root, activatable_container_ids_[i]);
     if (container && container->Contains(starting_window)) {
       starting_container_index = i;
       break;
@@ -183,7 +184,7 @@
 aura::Window* AshFocusRules::GetTopmostWindowToActivateForContainerIndex(
     int index,
     aura::Window* ignore) const {
-  const int container_id = GetActivatableShellWindowIds()[index];
+  const int container_id = activatable_container_ids_[index];
   // Inactive desk containers should be ignored, since windows in them should
   // never be returned as a next activatable window.
   if (IsInactiveDeskContainerId(container_id))
diff --git a/ash/wm/ash_focus_rules.h b/ash/wm/ash_focus_rules.h
index 1a9cc04b..6d28e713 100644
--- a/ash/wm/ash_focus_rules.h
+++ b/ash/wm/ash_focus_rules.h
@@ -36,6 +36,10 @@
       aura::Window* container,
       aura::Window* ignore) const;
 
+  // List of container IDs in the order of actiavation. This list doesn't change
+  // for the lifetime of this object.
+  const std::vector<int> activatable_container_ids_;
+
   DISALLOW_COPY_AND_ASSIGN(AshFocusRules);
 };
 
diff --git a/ash/wm/desks/autotest_desks_api.cc b/ash/wm/desks/autotest_desks_api.cc
index 091f195..ef0cc64 100644
--- a/ash/wm/desks/autotest_desks_api.cc
+++ b/ash/wm/desks/autotest_desks_api.cc
@@ -112,7 +112,7 @@
 
     auto* animation = DesksController::Get()->animation();
     DCHECK(animation);
-    animation_layer_ = animation->GetFirstDeskSwitchAnimatorForTesting()
+    animation_layer_ = animation->GetDeskSwitchAnimatorAtIndexForTesting(0)
                            ->GetAnimationLayerForTesting();
     animation_layer_->GetAnimator()->AddObserver(this);
   }
diff --git a/ash/wm/desks/desk_animation_base.cc b/ash/wm/desks/desk_animation_base.cc
index f99eb82..479e4d2 100644
--- a/ash/wm/desks/desk_animation_base.cc
+++ b/ash/wm/desks/desk_animation_base.cc
@@ -143,9 +143,9 @@
 }
 
 RootWindowDeskSwitchAnimator*
-DeskAnimationBase::GetFirstDeskSwitchAnimatorForTesting() const {
-  DCHECK(!desk_switch_animators_.empty());
-  return desk_switch_animators_.front().get();
+DeskAnimationBase::GetDeskSwitchAnimatorAtIndexForTesting(size_t index) const {
+  DCHECK_LT(index, desk_switch_animators_.size());
+  return desk_switch_animators_[index].get();
 }
 
 }  // namespace ash
diff --git a/ash/wm/desks/desk_animation_base.h b/ash/wm/desks/desk_animation_base.h
index 684d193..2017549 100644
--- a/ash/wm/desks/desk_animation_base.h
+++ b/ash/wm/desks/desk_animation_base.h
@@ -62,7 +62,8 @@
     skip_notify_controller_on_animation_finished_for_testing_ = val;
   }
 
-  RootWindowDeskSwitchAnimator* GetFirstDeskSwitchAnimatorForTesting() const;
+  RootWindowDeskSwitchAnimator* GetDeskSwitchAnimatorAtIndexForTesting(
+      size_t index) const;
 
  protected:
   // Abstract functions that can be overridden by child classes to do different
diff --git a/ash/wm/desks/desk_animation_impl.cc b/ash/wm/desks/desk_animation_impl.cc
index 75d82655a..ca00c9e 100644
--- a/ash/wm/desks/desk_animation_impl.cc
+++ b/ash/wm/desks/desk_animation_impl.cc
@@ -129,7 +129,8 @@
   }
 
   // Activate the target desk and take a screenshot.
-  DCHECK_EQ(pending_animators.size(), desk_switch_animators_.size());
+  // TODO(crbug.com/1134390): Convert back to DCHECK when the issue is fixed.
+  CHECK_EQ(pending_animators.size(), desk_switch_animators_.size());
   PrepareDeskForScreenshot(new_ending_desk_index);
   for (auto* animator : pending_animators)
     animator->TakeEndingDeskScreenshot();
@@ -142,28 +143,27 @@
 
   presentation_time_recorder_->RequestNext();
 
-  // List of animators that need a screenshot. It should be either empty or
-  // match the size of |desk_switch_animators_| as all the animations should be
-  // in sync.
-  std::vector<RootWindowDeskSwitchAnimator*> pending_animators;
+  // If any of the displays need a new screenshot while scrolling, take the
+  // ending desk screenshot for all of them to keep them in sync.
+  base::Optional<int> ending_desk_index;
   for (const auto& animator : desk_switch_animators_) {
-    if (animator->UpdateSwipeAnimation(scroll_delta_x))
-      pending_animators.push_back(animator.get());
+    if (!ending_desk_index)
+      ending_desk_index = animator->UpdateSwipeAnimation(scroll_delta_x);
+    else
+      animator->UpdateSwipeAnimation(scroll_delta_x);
   }
 
   // No screenshot needed.
-  if (pending_animators.empty()) {
-    OnEndingDeskScreenshotTaken();
+  if (!ending_desk_index)
     return true;
-  }
 
   // Activate the target desk and take a screenshot.
-  // TODO(crbug.com/1134390): Convert back to DCHECK when the issue is fixed.
-  CHECK_EQ(pending_animators.size(), desk_switch_animators_.size());
-  ending_desk_index_ = desk_switch_animators_[0]->ending_desk_index();
+  ending_desk_index_ = *ending_desk_index;
   PrepareDeskForScreenshot(ending_desk_index_);
-  for (auto* animator : pending_animators)
+  for (const auto& animator : desk_switch_animators_) {
+    animator->PrepareForEndingDeskScreenshot(ending_desk_index_);
     animator->TakeEndingDeskScreenshot();
+  }
   return true;
 }
 
diff --git a/ash/wm/desks/desk_animation_impl_unittest.cc b/ash/wm/desks/desk_animation_impl_unittest.cc
index e8f9206..b9267a3 100644
--- a/ash/wm/desks/desk_animation_impl_unittest.cc
+++ b/ash/wm/desks/desk_animation_impl_unittest.cc
@@ -8,6 +8,8 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/desks/desks_controller.h"
 #include "ash/wm/desks/desks_histogram_enums.h"
+#include "ash/wm/desks/root_window_desk_switch_animator_test_api.h"
+#include "base/barrier_closure.h"
 #include "base/test/scoped_feature_list.h"
 
 namespace ash {
@@ -33,4 +35,45 @@
   animation.EndSwipeAnimation();
 }
 
+// Tests that there is no crash when swiping with external displays. Regression
+// test for https://crbug.com/1154868.
+TEST_F(DeskActivationAnimationTest, UpdateSwipeNewScreenshotCrash) {
+  // Crash is only reproducible on different resolution widths and easier to
+  // repro when the widths differ by a lot.
+  UpdateDisplay("600x600,601+0-2000x600");
+
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kEnhancedDeskAnimations);
+
+  // Crash repro requires three desks.
+  auto* desks_controller = DesksController::Get();
+  desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
+  desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
+
+  DeskActivationAnimation animation(desks_controller, 1, 2,
+                                    DesksSwitchSource::kDeskSwitchTouchpad,
+                                    /*update_window_activation=*/false);
+  animation.set_skip_notify_controller_on_animation_finished_for_testing(true);
+  animation.Launch();
+
+  // Wait until all ending screenshots have been taken before swiping.
+  size_t num_animators = 2u;
+  base::RunLoop run_loop;
+  base::RepeatingClosure end_screenshot_callback =
+      base::BarrierClosure(num_animators, run_loop.QuitClosure());
+  for (size_t i = 0; i < num_animators; ++i) {
+    auto* desk_switch_animator =
+        animation.GetDeskSwitchAnimatorAtIndexForTesting(i);
+    RootWindowDeskSwitchAnimatorTestApi(desk_switch_animator)
+        .SetOnEndingScreenshotTakenCallback(end_screenshot_callback);
+  }
+  run_loop.Run();
+
+  // Swipe in a way which would have caused a crash using the old algorithm. See
+  // bug for more details.
+  animation.UpdateSwipeAnimation(-20);
+  animation.UpdateSwipeAnimation(10);
+  animation.EndSwipeAnimation();
+}
+
 }  // namespace ash
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index 368f71e..27d5d5721 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -60,6 +60,14 @@
     "Ash.Desks.NumberOfWindowsOnDesk_3";
 constexpr char kNumberOfWindowsOnDesk_4_HistogramName[] =
     "Ash.Desks.NumberOfWindowsOnDesk_4";
+constexpr char kNumberOfWindowsOnDesk_5_HistogramName[] =
+    "Ash.Desks.NumberOfWindowsOnDesk_5";
+constexpr char kNumberOfWindowsOnDesk_6_HistogramName[] =
+    "Ash.Desks.NumberOfWindowsOnDesk_6";
+constexpr char kNumberOfWindowsOnDesk_7_HistogramName[] =
+    "Ash.Desks.NumberOfWindowsOnDesk_7";
+constexpr char kNumberOfWindowsOnDesk_8_HistogramName[] =
+    "Ash.Desks.NumberOfWindowsOnDesk_8";
 
 constexpr char kNumberOfDeskTraversalsHistogramName[] =
     "Ash.Desks.NumberOfDeskTraversals";
@@ -71,6 +79,12 @@
 constexpr base::TimeDelta kDeskTraversalsTimeout =
     base::TimeDelta::FromSeconds(5);
 
+constexpr int kDeskDefaultNameIds[] = {
+    IDS_ASH_DESKS_DESK_1_MINI_VIEW_TITLE, IDS_ASH_DESKS_DESK_2_MINI_VIEW_TITLE,
+    IDS_ASH_DESKS_DESK_3_MINI_VIEW_TITLE, IDS_ASH_DESKS_DESK_4_MINI_VIEW_TITLE,
+    IDS_ASH_DESKS_DESK_5_MINI_VIEW_TITLE, IDS_ASH_DESKS_DESK_6_MINI_VIEW_TITLE,
+    IDS_ASH_DESKS_DESK_7_MINI_VIEW_TITLE, IDS_ASH_DESKS_DESK_8_MINI_VIEW_TITLE};
+
 // Appends the given |windows| to the end of the currently active overview mode
 // session such that the most-recently used window is added first. If
 // The windows will animate to their positions in the overview grid.
@@ -232,15 +246,8 @@
 
 // static
 base::string16 DesksController::GetDeskDefaultName(size_t desk_index) {
-  DCHECK_LT(desk_index, desks_util::kMaxNumberOfDesks);
-  constexpr int kStringIds[] = {IDS_ASH_DESKS_DESK_1_MINI_VIEW_TITLE,
-                                IDS_ASH_DESKS_DESK_2_MINI_VIEW_TITLE,
-                                IDS_ASH_DESKS_DESK_3_MINI_VIEW_TITLE,
-                                IDS_ASH_DESKS_DESK_4_MINI_VIEW_TITLE};
-  static_assert(desks_util::kMaxNumberOfDesks == base::size(kStringIds),
-                "Wrong default desks' names.");
-
-  return l10n_util::GetStringUTF16(kStringIds[desk_index]);
+  DCHECK_LT(desk_index, desks_util::GetMaxNumberOfDesks());
+  return l10n_util::GetStringUTF16(kDeskDefaultNameIds[desk_index]);
 }
 
 const Desk* DesksController::GetTargetActiveDesk() const {
@@ -280,7 +287,7 @@
 }
 
 bool DesksController::CanCreateDesks() const {
-  return desks_.size() < desks_util::kMaxNumberOfDesks;
+  return desks_.size() < desks_util::GetMaxNumberOfDesks();
 }
 
 Desk* DesksController::GetNextDesk() const {
@@ -812,7 +819,7 @@
 
   desks_restore_util::UpdatePrimaryUserDesksPrefs();
 
-  DCHECK_LE(available_container_ids_.size(), desks_util::kMaxNumberOfDesks);
+  DCHECK_LE(available_container_ids_.size(), desks_util::GetMaxNumberOfDesks());
 }
 
 const Desk* DesksController::FindDeskOfWindow(aura::Window* window) const {
@@ -850,6 +857,26 @@
                                  windows_count);
         break;
 
+      case 4:
+        UMA_HISTOGRAM_COUNTS_100(kNumberOfWindowsOnDesk_5_HistogramName,
+                                 windows_count);
+        break;
+
+      case 5:
+        UMA_HISTOGRAM_COUNTS_100(kNumberOfWindowsOnDesk_6_HistogramName,
+                                 windows_count);
+        break;
+
+      case 6:
+        UMA_HISTOGRAM_COUNTS_100(kNumberOfWindowsOnDesk_7_HistogramName,
+                                 windows_count);
+        break;
+
+      case 7:
+        UMA_HISTOGRAM_COUNTS_100(kNumberOfWindowsOnDesk_8_HistogramName,
+                                 windows_count);
+        break;
+
       default:
         NOTREACHED();
         break;
@@ -858,9 +885,9 @@
 }
 
 void DesksController::ReportDesksCountHistogram() const {
-  DCHECK_LE(desks_.size(), desks_util::kMaxNumberOfDesks);
+  DCHECK_LE(desks_.size(), desks_util::GetMaxNumberOfDesks());
   UMA_HISTOGRAM_EXACT_LINEAR(kDesksCountHistogramName, desks_.size(),
-                             desks_util::kMaxNumberOfDesks);
+                             desks_util::GetMaxNumberOfDesks());
 }
 
 void DesksController::UpdateDesksDefaultNames() {
diff --git a/ash/wm/desks/desks_restore_util.cc b/ash/wm/desks/desks_restore_util.cc
index 4488b00..c18fe07 100644
--- a/ash/wm/desks/desks_restore_util.cc
+++ b/ash/wm/desks/desks_restore_util.cc
@@ -38,7 +38,7 @@
 bool IsValidDeskIndex(int desk_index) {
   return desk_index >= 0 &&
          desk_index < int{DesksController::Get()->desks().size()} &&
-         desk_index < int{desks_util::kMaxNumberOfDesks};
+         desk_index < int{desks_util::GetMaxNumberOfDesks()};
 }
 
 }  // namespace
@@ -69,7 +69,7 @@
 
   // If we don't have any restore data, or the list is corrupt for some reason,
   // abort.
-  if (!restore_size || restore_size > desks_util::kMaxNumberOfDesks)
+  if (!restore_size || restore_size > desks_util::GetMaxNumberOfDesks())
     return;
 
   auto* desks_controller = DesksController::Get();
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 110b4c93..5082c60b 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -373,8 +373,8 @@
 
   // Expect we've reached the max number of desks, and we've been notified only
   // with the newly created desks.
-  EXPECT_EQ(desks_util::kMaxNumberOfDesks, controller->desks().size());
-  EXPECT_EQ(desks_util::kMaxNumberOfDesks - 1, observer.desks().size());
+  EXPECT_EQ(desks_util::GetMaxNumberOfDesks(), controller->desks().size());
+  EXPECT_EQ(desks_util::GetMaxNumberOfDesks() - 1, observer.desks().size());
   EXPECT_TRUE(controller->CanRemoveDesks());
 
   // Remove all desks until no longer possible, and expect that there's always
@@ -420,11 +420,11 @@
 
   auto* event_generator = GetEventGenerator();
   event_generator->MoveMouseTo(button_center);
-  for (size_t i = 0; i < desks_util::kMaxNumberOfDesks + 2; ++i)
+  for (size_t i = 0; i < desks_util::GetMaxNumberOfDesks() + 2; ++i)
     event_generator->ClickLeftButton();
 
   EXPECT_TRUE(overview_grid->IsDesksBarViewActive());
-  EXPECT_EQ(desks_util::kMaxNumberOfDesks, controller->desks().size());
+  EXPECT_EQ(desks_util::GetMaxNumberOfDesks(), controller->desks().size());
   EXPECT_EQ(controller->desks().size(), desks_bar_view->mini_views().size());
   EXPECT_FALSE(controller->CanCreateDesks());
   EXPECT_TRUE(controller->CanRemoveDesks());
@@ -446,7 +446,7 @@
   event_generator->ClickLeftButton();
 
   // The new desk button is now enabled again.
-  EXPECT_EQ(desks_util::kMaxNumberOfDesks - 1, controller->desks().size());
+  EXPECT_EQ(desks_util::GetMaxNumberOfDesks() - 1, controller->desks().size());
   EXPECT_EQ(controller->desks().size(), desks_bar_view->mini_views().size());
   EXPECT_TRUE(controller->CanCreateDesks());
   EXPECT_TRUE(new_desk_button->GetEnabled());
@@ -490,7 +490,7 @@
   // Gesture tap multiple times on the new desk button until it's disabled, and
   // verify the button state.
   auto* event_generator = GetEventGenerator();
-  for (size_t i = 0; i < desks_util::kMaxNumberOfDesks + 2; ++i)
+  for (size_t i = 0; i < desks_util::GetMaxNumberOfDesks() + 2; ++i)
     GestureTapOnView(new_desk_button, event_generator);
 
   EXPECT_FALSE(new_desk_button->GetEnabled());
@@ -2206,7 +2206,7 @@
   // containers are reused for new desks, their backdrop state are always
   // correct, and there are no crashes as desks are removed.
   auto* desks_controller = DesksController::Get();
-  for (size_t i = 0; i < 2 * desks_util::kMaxNumberOfDesks; ++i) {
+  for (size_t i = 0; i < 2 * desks_util::GetMaxNumberOfDesks(); ++i) {
     NewDesk();
     ASSERT_EQ(2u, desks_controller->desks().size());
     const Desk* desk_1 = desks_controller->desks()[0].get();
@@ -3277,9 +3277,10 @@
 
 TEST_F(DesksAcceleratorsTest, NewDesk) {
   auto* controller = DesksController::Get();
-  // It's possible to add up to `kMaxNumberOfDesks` desks using the shortcut.
+  // It's possible to add up to `GetMaxNumberOfDesks()` desks using the
+  // shortcut.
   const int flags = ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN;
-  for (size_t num_desks = 1; num_desks < desks_util::kMaxNumberOfDesks;
+  for (size_t num_desks = 1; num_desks < desks_util::GetMaxNumberOfDesks();
        ++num_desks) {
     DeskSwitchAnimationWaiter waiter;
     SendAccelerator(ui::VKEY_OEM_PLUS, flags);
@@ -3290,9 +3291,9 @@
   }
 
   // When we reach the limit, the shortcut does nothing.
-  EXPECT_EQ(desks_util::kMaxNumberOfDesks, controller->desks().size());
+  EXPECT_EQ(desks_util::GetMaxNumberOfDesks(), controller->desks().size());
   SendAccelerator(ui::VKEY_OEM_PLUS, flags);
-  EXPECT_EQ(desks_util::kMaxNumberOfDesks, controller->desks().size());
+  EXPECT_EQ(desks_util::GetMaxNumberOfDesks(), controller->desks().size());
 }
 
 TEST_F(DesksAcceleratorsTest, CannotRemoveLastDesk) {
@@ -3784,6 +3785,8 @@
 // Tests desks name nudges, i.e. when a user creates a new desk, focus + clear
 // the new desk's renaming textfield.
 TEST_F(DesksBentoTest, NameNudges) {
+  // Make sure the display is large enough to hold the max number of desks.
+  UpdateDisplay("1200x800");
   auto* controller = DesksController::Get();
 
   // Start overview.
@@ -3805,7 +3808,7 @@
   // time a new desk is created the new desk's name view should have focus, be
   // empty and have its accessible name set to the default desk name. Also, the
   // previous desk should be left with a default name.
-  for (size_t i = 1; i < desks_util::kMaxNumberOfDesks; ++i) {
+  for (size_t i = 1; i < desks_util::GetMaxNumberOfDesks(); ++i) {
     event_generator->ClickLeftButton();
     auto* desk_name_view = desks_bar_view->mini_views()[i]->desk_name_view();
     EXPECT_TRUE(desk_name_view->HasFocus());
diff --git a/ash/wm/desks/desks_util.cc b/ash/wm/desks/desks_util.cc
index 370fdcd..b89505e 100644
--- a/ash/wm/desks/desks_util.cc
+++ b/ash/wm/desks/desks_util.cc
@@ -4,6 +4,9 @@
 
 #include "ash/wm/desks/desks_util.h"
 
+#include <array>
+
+#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/tablet_mode.h"
 #include "ash/shell.h"
 #include "ash/wm/desks/desk.h"
@@ -17,19 +20,34 @@
 
 namespace {
 
-constexpr std::array<int, kMaxNumberOfDesks> kDesksContainersIds = {
+constexpr size_t kMaxNumberOfDesks = 4;
+constexpr size_t kBentoMaxNumberOfDesks = 8;
+
+constexpr std::array<int, kBentoMaxNumberOfDesks> kDesksContainersIds = {
     kShellWindowId_DefaultContainerDeprecated,
     kShellWindowId_DeskContainerB,
     kShellWindowId_DeskContainerC,
     kShellWindowId_DeskContainerD,
+    kShellWindowId_DeskContainerE,
+    kShellWindowId_DeskContainerF,
+    kShellWindowId_DeskContainerG,
+    kShellWindowId_DeskContainerH,
 };
 
 }  // namespace
 
-// Note: this function avoids having a copy of |kDesksContainersIds| in each
-// translation unit that references it.
-const std::array<int, kMaxNumberOfDesks>& GetDesksContainersIds() {
-  return kDesksContainersIds;
+size_t GetMaxNumberOfDesks() {
+  return features::IsBentoEnabled() ? kBentoMaxNumberOfDesks
+                                    : kMaxNumberOfDesks;
+}
+
+std::vector<int> GetDesksContainersIds() {
+  if (!features::IsBentoEnabled()) {
+    return std::vector<int>(kDesksContainersIds.begin(),
+                            kDesksContainersIds.begin() + kMaxNumberOfDesks);
+  }
+  return std::vector<int>(kDesksContainersIds.begin(),
+                          kDesksContainersIds.end());
 }
 
 std::vector<aura::Window*> GetDesksContainers(aura::Window* root) {
@@ -37,8 +55,7 @@
   DCHECK(root->IsRootWindow());
 
   std::vector<aura::Window*> containers;
-  containers.reserve(kMaxNumberOfDesks);
-  for (const auto& id : kDesksContainersIds) {
+  for (const auto& id : GetDesksContainersIds()) {
     auto* container = root->GetChildById(id);
     DCHECK(container);
     containers.push_back(container);
@@ -63,6 +80,18 @@
     case kShellWindowId_DeskContainerD:
       return "Desk_Container_D";
 
+    case kShellWindowId_DeskContainerE:
+      return "Desk_Container_E";
+
+    case kShellWindowId_DeskContainerF:
+      return "Desk_Container_F";
+
+    case kShellWindowId_DeskContainerG:
+      return "Desk_Container_G";
+
+    case kShellWindowId_DeskContainerH:
+      return "Desk_Container_H";
+
     default:
       NOTREACHED();
       return "";
@@ -78,7 +107,11 @@
   return id == kShellWindowId_DefaultContainerDeprecated ||
          id == kShellWindowId_DeskContainerB ||
          id == kShellWindowId_DeskContainerC ||
-         id == kShellWindowId_DeskContainerD;
+         id == kShellWindowId_DeskContainerD ||
+         id == kShellWindowId_DeskContainerE ||
+         id == kShellWindowId_DeskContainerF ||
+         id == kShellWindowId_DeskContainerG ||
+         id == kShellWindowId_DeskContainerH;
 }
 
 int GetActiveDeskContainerId() {
diff --git a/ash/wm/desks/desks_util.h b/ash/wm/desks/desks_util.h
index 6fc79be3..1e575c1 100644
--- a/ash/wm/desks/desks_util.h
+++ b/ash/wm/desks/desks_util.h
@@ -5,7 +5,6 @@
 #ifndef ASH_WM_DESKS_DESKS_UTIL_H_
 #define ASH_WM_DESKS_DESKS_UTIL_H_
 
-#include <array>
 #include <vector>
 
 #include "ash/ash_export.h"
@@ -24,9 +23,9 @@
 
 namespace desks_util {
 
-constexpr size_t kMaxNumberOfDesks = 4;
+ASH_EXPORT size_t GetMaxNumberOfDesks();
 
-ASH_EXPORT const std::array<int, kMaxNumberOfDesks>& GetDesksContainersIds();
+ASH_EXPORT std::vector<int> GetDesksContainersIds();
 
 ASH_EXPORT std::vector<aura::Window*> GetDesksContainers(aura::Window* root);
 
diff --git a/ash/wm/desks/root_window_desk_switch_animator.cc b/ash/wm/desks/root_window_desk_switch_animator.cc
index 10083d7..fbb932e35 100644
--- a/ash/wm/desks/root_window_desk_switch_animator.cc
+++ b/ash/wm/desks/root_window_desk_switch_animator.cc
@@ -145,7 +145,7 @@
   DCHECK_NE(starting_desk_index_, ending_desk_index_);
   DCHECK(delegate_);
 
-  screenshot_layers_.resize(desks_util::kMaxNumberOfDesks);
+  screenshot_layers_.resize(desks_util::GetMaxNumberOfDesks());
 }
 
 RootWindowDeskSwitchAnimator::~RootWindowDeskSwitchAnimator() {
@@ -248,9 +248,10 @@
   return true;
 }
 
-bool RootWindowDeskSwitchAnimator::UpdateSwipeAnimation(float scroll_delta_x) {
+base::Optional<int> RootWindowDeskSwitchAnimator::UpdateSwipeAnimation(
+    float scroll_delta_x) {
   if (!starting_desk_screenshot_taken_ || !ending_desk_screenshot_taken_)
-    return false;
+    return base::nullopt;
 
   const float translation_delta_x =
       TouchpadToXTranslation(scroll_delta_x, x_translation_offset_);
@@ -304,13 +305,16 @@
           : transformed_animation_layer_bounds.x() >
                 -kMinDistanceBeforeScreenshotDp;
 
+  // TODO(sammiequon): Make GetIndexOfMostVisibleDeskScreenshot() public and
+  // have DeskActivationAnimation keep track of |visible_desk_index_|. Right now
+  // OnVisibleDeskChanged will get called once for each display.
   const int old_visible_desk_index = visible_desk_index_;
   visible_desk_index_ = GetIndexOfMostVisibleDeskScreenshot();
   if (old_visible_desk_index != visible_desk_index_)
     delegate_->OnVisibleDeskChanged();
 
   if (!going_out_of_bounds)
-    return false;
+    return base::nullopt;
 
   // The upcoming desk we need to show will be an adjacent desk to the desk at
   // |visible_desk_index_| based on |moving_left|.
@@ -318,13 +322,17 @@
 
   if (new_desk_index < 0 ||
       new_desk_index >= int{DesksController::Get()->desks().size()}) {
-    return false;
+    return base::nullopt;
   }
 
-  ending_desk_index_ = new_desk_index;
+  return new_desk_index;
+}
+
+void RootWindowDeskSwitchAnimator::PrepareForEndingDeskScreenshot(
+    int new_ending_desk_index) {
+  ending_desk_index_ = new_ending_desk_index;
   ending_desk_screenshot_retries_ = 0;
   ending_desk_screenshot_taken_ = false;
-  return true;
 }
 
 int RootWindowDeskSwitchAnimator::EndSwipeAnimation() {
diff --git a/ash/wm/desks/root_window_desk_switch_animator.h b/ash/wm/desks/root_window_desk_switch_animator.h
index 9bb431ba..50dd8d3 100644
--- a/ash/wm/desks/root_window_desk_switch_animator.h
+++ b/ash/wm/desks/root_window_desk_switch_animator.h
@@ -263,8 +263,17 @@
   // Called as a user is performing a touchpad swipe. Requests a new screenshot
   // if necessary based on the last direction as specified in |scroll_delta_x|.
   // |scroll_delta_x| is in touchpad units, it will be converted to display
-  // units and then used to shift the animation layer.
-  bool UpdateSwipeAnimation(float scroll_delta_x);
+  // units and then used to shift the animation layer. If the animation layer is
+  // near its boundaries, this will return an index for the desk we should take
+  // a screenshot for. If we are not near the boundaries, or if there is no next
+  // adjacent desk in the direction we are heading, return base::nullopt. The
+  // delegate is responsible for requesting the screenshot.
+  base::Optional<int> UpdateSwipeAnimation(float scroll_delta_x);
+
+  // Maybe called after UpdateSwipeAnimation() if we need a new screenshot.
+  // Updates |ending_desk_index_| and resets some other internal state related
+  // to the ending desk screenshot.
+  void PrepareForEndingDeskScreenshot(int new_ending_desk_index);
 
   // Called when a user ends a touchpad swipe. This will animate to the most
   // visible desk, whose index is also returned.
diff --git a/ash/wm/desks/root_window_desk_switch_animator_unittest.cc b/ash/wm/desks/root_window_desk_switch_animator_unittest.cc
index 7ad2418..921b8bf 100644
--- a/ash/wm/desks/root_window_desk_switch_animator_unittest.cc
+++ b/ash/wm/desks/root_window_desk_switch_animator_unittest.cc
@@ -486,12 +486,18 @@
   // Swipe enough so that our third and fourth desk screenshots are taken, and
   // then swipe so that the fourth desk is fully shown. There should be 3
   // visible desk changes in total.
-  ASSERT_TRUE(
-      animator()->UpdateSwipeAnimation(-touchpad_swipe_length_for_desk_change));
+  base::Optional<int> new_index =
+      animator()->UpdateSwipeAnimation(-touchpad_swipe_length_for_desk_change);
+  ASSERT_TRUE(new_index.has_value());
+  animator()->PrepareForEndingDeskScreenshot(*new_index);
   TakeEndingDeskScreenshotAndWait();
-  ASSERT_TRUE(
-      animator()->UpdateSwipeAnimation(-touchpad_swipe_length_for_desk_change));
+
+  new_index =
+      animator()->UpdateSwipeAnimation(-touchpad_swipe_length_for_desk_change);
+  ASSERT_TRUE(new_index.has_value());
+  animator()->PrepareForEndingDeskScreenshot(*new_index);
   TakeEndingDeskScreenshotAndWait();
+
   animator()->UpdateSwipeAnimation(-3 * touchpad_swipe_length_for_desk_change);
   EXPECT_EQ(3, visible_desk_changed_count());
 
diff --git a/ash/wm/gestures/wm_gesture_handler_unittest.cc b/ash/wm/gestures/wm_gesture_handler_unittest.cc
index 4bf8c91..3141538e 100644
--- a/ash/wm/gestures/wm_gesture_handler_unittest.cc
+++ b/ash/wm/gestures/wm_gesture_handler_unittest.cc
@@ -129,7 +129,7 @@
       auto* animation = DesksController::Get()->animation();
       DCHECK(animation);
       auto* desk_switch_animator =
-          animation->GetFirstDeskSwitchAnimatorForTesting();
+          animation->GetDeskSwitchAnimatorAtIndexForTesting(0);
       base::RunLoop run_loop;
       RootWindowDeskSwitchAnimatorTestApi(desk_switch_animator)
           .SetOnEndingScreenshotTakenCallback(run_loop.QuitClosure());
diff --git a/ash/wm/overview/overview_highlight_controller.cc b/ash/wm/overview/overview_highlight_controller.cc
index 555be09..7a59e01 100644
--- a/ash/wm/overview/overview_highlight_controller.cc
+++ b/ash/wm/overview/overview_highlight_controller.cc
@@ -200,7 +200,7 @@
 OverviewHighlightController::GetTraversableViews() const {
   std::vector<OverviewHighlightableView*> traversable_views;
   traversable_views.reserve(overview_session_->num_items() +
-                            (desks_util::kMaxNumberOfDesks + 1) *
+                            (desks_util::GetMaxNumberOfDesks() + 1) *
                                 Shell::Get()->GetAllRootWindows().size());
   for (auto& grid : overview_session_->grid_list()) {
     auto* bar_view = grid->desks_bar_view();
diff --git a/ash/wm/overview/overview_highlight_controller_unittest.cc b/ash/wm/overview/overview_highlight_controller_unittest.cc
index 0506967..df9f8bb 100644
--- a/ash/wm/overview/overview_highlight_controller_unittest.cc
+++ b/ash/wm/overview/overview_highlight_controller_unittest.cc
@@ -579,7 +579,8 @@
   while (desks_controller->CanCreateDesks())
     SendKey(ui::VKEY_RETURN);
   EXPECT_FALSE(new_desk_button->GetEnabled());
-  EXPECT_EQ(desks_util::kMaxNumberOfDesks, desks_controller->desks().size());
+  EXPECT_EQ(desks_util::GetMaxNumberOfDesks(),
+            desks_controller->desks().size());
 
   // Tests that after the button is disabled, it is no longer highlighted.
   EXPECT_FALSE(GetHighlightedView());
@@ -737,6 +738,8 @@
 // desk button and Bento is enabled.
 TEST_F(BentoOverviewHighlightControllerTest,
        ActivateCloseHighlightOnNewDeskButton) {
+  // Make sure the display is large enough to hold the max number of desks.
+  UpdateDisplay("1200x800");
   ToggleOverview();
   const auto* desk_bar_view =
       GetDesksBarViewForRoot(Shell::GetPrimaryRootWindow());
@@ -769,7 +772,8 @@
     SendKey(ui::VKEY_TAB);
   }
   EXPECT_FALSE(new_desk_button->GetEnabled());
-  EXPECT_EQ(desks_util::kMaxNumberOfDesks, desks_controller->desks().size());
+  EXPECT_EQ(desks_util::GetMaxNumberOfDesks(),
+            desks_controller->desks().size());
 }
 
 }  // namespace ash
diff --git a/ash/wm/switchable_windows.cc b/ash/wm/switchable_windows.cc
index 1f96904..41a86401 100644
--- a/ash/wm/switchable_windows.cc
+++ b/ash/wm/switchable_windows.cc
@@ -6,7 +6,6 @@
 
 #include <array>
 
-#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/wm/desks/desks_util.h"
 #include "base/stl_util.h"
@@ -16,15 +15,19 @@
 
 namespace {
 
-constexpr std::array<int, 6> kSwitchableContainers = {
-    kShellWindowId_DefaultContainerDeprecated,
-    kShellWindowId_DeskContainerB,
-    kShellWindowId_DeskContainerC,
-    kShellWindowId_DeskContainerD,
+constexpr std::array<int, 2> kSwitchableContainers = {
     kShellWindowId_AlwaysOnTopContainer,
     kShellWindowId_PipContainer,
 };
 
+std::vector<int> GetSwitchableContainerIds() {
+  std::vector<int> ids = desks_util::GetDesksContainersIds();
+  for (const int id : kSwitchableContainers)
+    ids.emplace_back(id);
+
+  return ids;
+}
+
 }  // namespace
 
 std::vector<aura::Window*> GetSwitchableContainersForRoot(
@@ -41,7 +44,7 @@
     return containers;
   }
 
-  for (const auto& id : kSwitchableContainers) {
+  for (const auto& id : GetSwitchableContainerIds()) {
     auto* container = root->GetChildById(id);
     DCHECK(container);
     containers.push_back(container);
@@ -54,9 +57,8 @@
 bool IsSwitchableContainer(const aura::Window* window) {
   if (!window)
     return false;
-  const int shell_window_id = window->id();
 
-  return base::Contains(kSwitchableContainers, shell_window_id);
+  return base::Contains(GetSwitchableContainerIds(), window->id());
 }
 
 }  // namespace ash
diff --git a/ash/wm/window_cycle_controller.cc b/ash/wm/window_cycle_controller.cc
index 8407ac1..e88a2ba3 100644
--- a/ash/wm/window_cycle_controller.cc
+++ b/ash/wm/window_cycle_controller.cc
@@ -60,7 +60,7 @@
                active_desk_container_id_before_cycle);
   UMA_HISTOGRAM_EXACT_LINEAR("Ash.WindowCycleController.DesksSwitchDistance",
                              desks_switch_distance,
-                             desks_util::kMaxNumberOfDesks);
+                             desks_util::GetMaxNumberOfDesks());
 }
 
 }  // namespace
diff --git a/base/allocator/partition_allocator/pcscan.cc b/base/allocator/partition_allocator/pcscan.cc
index ef07ec78..640cac4 100644
--- a/base/allocator/partition_allocator/pcscan.cc
+++ b/base/allocator/partition_allocator/pcscan.cc
@@ -5,6 +5,7 @@
 #include "base/allocator/partition_allocator/pcscan.h"
 
 #include <algorithm>
+#include <condition_variable>
 #include <map>
 #include <mutex>
 #include <numeric>
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 1a9a5740..55604da 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20201204.1.1
+0.20201206.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 1a9a5740..55604da 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20201204.1.1
+0.20201206.1.1
diff --git a/cc/paint/image_transfer_cache_entry.cc b/cc/paint/image_transfer_cache_entry.cc
index 527866a4..9bb1c2a 100644
--- a/cc/paint/image_transfer_cache_entry.cc
+++ b/cc/paint/image_transfer_cache_entry.cc
@@ -260,7 +260,7 @@
   // We don't need to populate the SerializeOptions here since the writer is
   // only used for serializing primitives.
   PaintOp::SerializeOptions options(nullptr, nullptr, nullptr, nullptr, nullptr,
-                                    nullptr, false, false, 0, SkMatrix::I());
+                                    nullptr, false, false, 0, SkM44());
   PaintOpWriter writer(data.data(), data.size(), options);
   writer.Write(plane_config_);
 
diff --git a/cc/paint/paint_canvas.h b/cc/paint/paint_canvas.h
index 2b9c34a..4bcee9f 100644
--- a/cc/paint/paint_canvas.h
+++ b/cc/paint/paint_canvas.h
@@ -76,9 +76,13 @@
   virtual void translate(SkScalar dx, SkScalar dy) = 0;
   virtual void scale(SkScalar sx, SkScalar sy) = 0;
   virtual void rotate(SkScalar degrees) = 0;
+  // TODO(aaronhk): crbug.com/1153330 deprecate these in favor of the SkM44
+  // versions. Also add concat with a SkM44 argument
   virtual void concat(const SkMatrix& matrix) = 0;
   virtual void setMatrix(const SkMatrix& matrix) = 0;
 
+  virtual void setMatrix(const SkM44& matrix) = 0;
+
   virtual void clipRect(const SkRect& rect,
                         SkClipOp op,
                         bool do_anti_alias) = 0;
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index ee63ac3..ce783ca8 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -85,6 +85,7 @@
   M(SaveLayerAlphaOp) \
   M(ScaleOp)          \
   M(SetMatrixOp)      \
+  M(SetMatrix44Op)    \
   M(SetNodeIdOp)      \
   M(TranslateOp)
 
@@ -295,6 +296,8 @@
       return "Scale";
     case PaintOpType::SetMatrix:
       return "SetMatrix";
+    case PaintOpType::SetMatrix44:
+      return "SetMatrix44";
     case PaintOpType::SetNodeId:
       return "SetNodeId";
     case PaintOpType::Translate:
@@ -335,7 +338,7 @@
     bool can_use_lcd_text,
     bool context_supports_distance_field_text,
     int max_texture_size,
-    const SkMatrix& original_ctm)
+    const SkM44& original_ctm)
     : image_provider(image_provider),
       transfer_cache(transfer_cache),
       paint_cache(paint_cache),
@@ -721,6 +724,12 @@
   return helper.size();
 }
 
+// Just a helper for moving SkMatrix -> SkM44 here, these early-outs might not
+// be necessary
+bool IsSkM44Identity(const SkM44& m) {
+  return m == SkM44();
+}
+
 size_t SetMatrixOp::Serialize(const PaintOp* base_op,
                               void* memory,
                               size_t size,
@@ -728,12 +737,26 @@
   auto* op = static_cast<const SetMatrixOp*>(base_op);
   PaintOpWriter helper(memory, size, options);
 
-  if (options.original_ctm.isIdentity()) {
+  // TODO(aaronhk) take out this early out and see if there's a perf regression
+  if (IsSkM44Identity(options.original_ctm)) {
     helper.Write(op->matrix);
   } else {
-    SkMatrix transformed = op->matrix;
-    transformed.postConcat(options.original_ctm);
-    helper.Write(transformed);
+    helper.Write(options.original_ctm.asM33() * op->matrix);
+  }
+  return helper.size();
+}
+
+size_t SetMatrix44Op::Serialize(const PaintOp* base_op,
+                                void* memory,
+                                size_t size,
+                                const SerializeOptions& options) {
+  auto* op = static_cast<const SetMatrix44Op*>(base_op);
+  PaintOpWriter helper(memory, size, options);
+
+  if (IsSkM44Identity(options.original_ctm)) {
+    helper.Write(op->matrix);
+  } else {
+    helper.Write(options.original_ctm * op->matrix);
   }
   return helper.size();
 }
@@ -1314,6 +1337,25 @@
   return op;
 }
 
+PaintOp* SetMatrix44Op::Deserialize(const volatile void* input,
+                                    size_t input_size,
+                                    void* output,
+                                    size_t output_size,
+                                    const DeserializeOptions& options) {
+  DCHECK_GE(output_size, sizeof(SetMatrix44Op));
+  SetMatrix44Op* op = new (output) SetMatrix44Op;
+
+  PaintOpReader helper(input, input_size, options);
+  helper.Read(&op->matrix);
+  if (!helper.valid() || !op->IsValid()) {
+    op->~SetMatrix44Op();
+    return nullptr;
+  }
+
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
 PaintOp* SetNodeIdOp::Deserialize(const volatile void* input,
                                   size_t input_size,
                                   void* output,
@@ -1701,6 +1743,16 @@
   canvas->setMatrix(SkMatrix::Concat(params.original_ctm, op->matrix));
 }
 
+void SetMatrix44Op::Raster(const SetMatrix44Op* op,
+                           SkCanvas* canvas,
+                           const PlaybackParams& params) {
+  // TODO(aaronhk) when PlaybackParams stores an SKM44:
+  // setMatrix(op->matrix * params.original_ctm)
+  SkM44 result_matrix = SkM44(params.original_ctm);
+  result_matrix.postConcat(op->matrix);
+  canvas->setMatrix(result_matrix);
+}
+
 void SetNodeIdOp::Raster(const SetNodeIdOp* op,
                          SkCanvas* canvas,
                          const PlaybackParams& params) {
@@ -1778,6 +1830,18 @@
 }
 
 // static
+bool PaintOp::AreSkM44sEqual(const SkM44& left, const SkM44& right) {
+  for (int r = 0; r < 4; ++r) {
+    for (int c = 0; c < 4; ++c) {
+      if (!AreEqualEvenIfNaN(left.rc(r, c), right.rc(r, c)))
+        return false;
+    }
+  }
+
+  return true;
+}
+
+// static
 bool PaintOp::AreSkFlattenablesEqual(SkFlattenable* left,
                                      SkFlattenable* right) {
   if (!right || !left)
@@ -2134,6 +2198,17 @@
   return true;
 }
 
+bool SetMatrix44Op::AreEqual(const PaintOp* base_left,
+                             const PaintOp* base_right) {
+  auto* left = static_cast<const SetMatrix44Op*>(base_left);
+  auto* right = static_cast<const SetMatrix44Op*>(base_right);
+  DCHECK(left->IsValid());
+  DCHECK(right->IsValid());
+  if (!AreSkM44sEqual(left->matrix, right->matrix))
+    return false;
+  return true;
+}
+
 bool SetNodeIdOp::AreEqual(const PaintOp* base_left,
                            const PaintOp* base_right) {
   auto* left = static_cast<const SetNodeIdOp*>(base_left);
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h
index 57b1d8e4..04533b66 100644
--- a/cc/paint/paint_op_buffer.h
+++ b/cc/paint/paint_op_buffer.h
@@ -105,6 +105,7 @@
   SaveLayerAlpha,
   Scale,
   SetMatrix,
+  SetMatrix44,
   SetNodeId,
   Translate,
   LastPaintOpType = Translate,
@@ -130,6 +131,7 @@
   PlaybackParams& operator=(const PlaybackParams& other);
 
   ImageProvider* image_provider;
+  // TODO(aaronhk) turn this into an SkM44
   SkMatrix original_ctm;
   CustomDataRasterCallback custom_callback;
   DidDrawOpCallback did_draw_op_callback;
@@ -166,7 +168,7 @@
                      bool can_use_lcd_text,
                      bool context_supports_distance_field_text,
                      int max_texture_size,
-                     const SkMatrix& original_ctm);
+                     const SkM44& original_ctm);
     SerializeOptions(const SerializeOptions&);
     SerializeOptions& operator=(const SerializeOptions&);
     ~SerializeOptions();
@@ -181,7 +183,7 @@
     bool can_use_lcd_text = false;
     bool context_supports_distance_field_text = true;
     int max_texture_size = 0;
-    SkMatrix original_ctm = SkMatrix::I();
+    SkM44 original_ctm = SkM44();  // Identity
 
     // Optional.
     // The flags to use when serializing this op. This can be used to override
@@ -332,6 +334,7 @@
   static bool AreSkRectsEqual(const SkRect& left, const SkRect& right);
   static bool AreSkRRectsEqual(const SkRRect& left, const SkRRect& right);
   static bool AreSkMatricesEqual(const SkMatrix& left, const SkMatrix& right);
+  static bool AreSkM44sEqual(const SkM44& left, const SkM44& right);
   static bool AreSkFlattenablesEqual(SkFlattenable* left, SkFlattenable* right);
 
   static constexpr bool kIsDrawOp = false;
@@ -956,6 +959,24 @@
   SetMatrixOp() : PaintOp(kType) {}
 };
 
+class CC_PAINT_EXPORT SetMatrix44Op final : public PaintOp {
+ public:
+  static constexpr PaintOpType kType = PaintOpType::SetMatrix44;
+  explicit SetMatrix44Op(const SkM44& matrix)
+      : PaintOp(kType), matrix(matrix) {}
+  static void Raster(const SetMatrix44Op* op,
+                     SkCanvas* canvas,
+                     const PlaybackParams& params);
+  bool IsValid() const { return true; }
+  static bool AreEqual(const PaintOp* left, const PaintOp* right);
+  HAS_SERIALIZATION_FUNCTIONS();
+
+  SkM44 matrix;
+
+ private:
+  SetMatrix44Op() : PaintOp(kType) {}
+};
+
 class CC_PAINT_EXPORT SetNodeIdOp final : public PaintOp {
  public:
   static constexpr PaintOpType kType = PaintOpType::SetNodeId;
diff --git a/cc/paint/paint_op_buffer_serializer.cc b/cc/paint/paint_op_buffer_serializer.cc
index 709b432..1c54872 100644
--- a/cc/paint/paint_op_buffer_serializer.cc
+++ b/cc/paint/paint_op_buffer_serializer.cc
@@ -380,7 +380,7 @@
       image_provider_, transfer_cache_, paint_cache_, text_blob_canvas_.get(),
       strike_server_, color_space_, can_use_lcd_text_,
       context_supports_distance_field_text_, max_texture_size_,
-      text_blob_canvas_->getTotalMatrix());
+      text_blob_canvas_->getLocalToDevice());
 }
 
 SimpleBufferSerializer::SimpleBufferSerializer(
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc
index d0a0d70..4542cc48 100644
--- a/cc/paint/paint_op_buffer_unittest.cc
+++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -1103,6 +1103,21 @@
     }(),
 };
 
+std::vector<SkM44> test_matrix44s = {
+    SkM44(),
+    SkM44::Scale(3.91f, 4.31f, 1.0f),
+    SkM44::Translate(-5.2f, 8.7f, 0.0f),
+    [] {
+      SkScalar buffer[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+      return SkM44::RowMajor(buffer);
+    }(),
+    [] {
+      SkScalar buffer[] = {1, 2,  3,  4,  5,  6,  7,  8,
+                           9, 10, 11, 12, 13, 14, 15, 16};
+      return SkM44::RowMajor(buffer);
+    }(),
+};
+
 std::vector<SkPath> test_paths = {
     [] {
       SkPath path;
@@ -1663,6 +1678,12 @@
   ValidateOps<SetMatrixOp>(buffer);
 }
 
+void PushSetMatrix44Ops(PaintOpBuffer* buffer) {
+  for (auto& test_matrix44 : test_matrix44s)
+    buffer->push<SetMatrix44Op>(test_matrix44);
+  ValidateOps<SetMatrix44Op>(buffer);
+}
+
 void PushTranslateOps(PaintOpBuffer* buffer) {
   for (size_t i = 0; i < test_floats.size() - 1; i += 2)
     buffer->push<TranslateOp>(test_floats[i], test_floats[i + 1]);
@@ -1765,6 +1786,9 @@
       case PaintOpType::SetMatrix:
         PushSetMatrixOps(&buffer_);
         break;
+      case PaintOpType::SetMatrix44:
+        PushSetMatrix44Ops(&buffer_);
+        break;
       case PaintOpType::Translate:
         PushTranslateOps(&buffer_);
         break;
diff --git a/cc/paint/paint_op_reader.cc b/cc/paint/paint_op_reader.cc
index fc9cff4..48394f53 100644
--- a/cc/paint/paint_op_reader.cc
+++ b/cc/paint/paint_op_reader.cc
@@ -646,6 +646,10 @@
   FixupMatrixPostSerialization(matrix);
 }
 
+void PaintOpReader::Read(SkM44* matrix) {
+  ReadSimple(matrix);
+}
+
 void PaintOpReader::Read(SkColorType* color_type) {
   uint32_t raw_color_type = kUnknown_SkColorType;
   ReadSimple(&raw_color_type);
diff --git a/cc/paint/paint_op_reader.h b/cc/paint/paint_op_reader.h
index 73ab3ad..ce03f47a 100644
--- a/cc/paint/paint_op_reader.h
+++ b/cc/paint/paint_op_reader.h
@@ -71,6 +71,7 @@
   void Read(sk_sp<PaintFilter>* filter);
   void Read(sk_sp<PaintShader>* shader);
   void Read(SkMatrix* matrix);
+  void Read(SkM44* matrix);
   void Read(SkColorType* color_type);
   void Read(SkImageInfo* info);
   void Read(sk_sp<SkColorSpace>* color_space);
diff --git a/cc/paint/paint_op_writer.cc b/cc/paint/paint_op_writer.cc
index 8393b764..e7affb7 100644
--- a/cc/paint/paint_op_writer.cc
+++ b/cc/paint/paint_op_writer.cc
@@ -449,6 +449,10 @@
   WriteSimple(matrix);
 }
 
+void PaintOpWriter::Write(const SkM44& matrix) {
+  WriteSimple(matrix);
+}
+
 void PaintOpWriter::Write(const PaintShader* shader, SkFilterQuality quality) {
   sk_sp<PaintShader> transformed_shader;
   uint32_t paint_image_transfer_cache_id = kInvalidImageTransferCacheEntryId;
diff --git a/cc/paint/paint_op_writer.h b/cc/paint/paint_op_writer.h
index 4060e10..660d8f8 100644
--- a/cc/paint/paint_op_writer.h
+++ b/cc/paint/paint_op_writer.h
@@ -53,6 +53,7 @@
 
   void Write(SkScalar data);
   void Write(SkMatrix data);
+  void Write(const SkM44& data);
   void Write(uint8_t data);
   void Write(uint32_t data);
   void Write(uint64_t data);
diff --git a/cc/paint/record_paint_canvas.cc b/cc/paint/record_paint_canvas.cc
index e8758c7..6f7d390 100644
--- a/cc/paint/record_paint_canvas.cc
+++ b/cc/paint/record_paint_canvas.cc
@@ -117,6 +117,11 @@
   GetCanvas()->setMatrix(matrix);
 }
 
+void RecordPaintCanvas::setMatrix(const SkM44& matrix) {
+  list_->push<SetMatrix44Op>(matrix);
+  GetCanvas()->setMatrix(matrix);
+}
+
 void RecordPaintCanvas::clipRect(const SkRect& rect,
                                  SkClipOp op,
                                  bool antialias) {
diff --git a/cc/paint/record_paint_canvas.h b/cc/paint/record_paint_canvas.h
index 4917088a..2df0d27 100644
--- a/cc/paint/record_paint_canvas.h
+++ b/cc/paint/record_paint_canvas.h
@@ -48,6 +48,7 @@
   void rotate(SkScalar degrees) override;
   void concat(const SkMatrix& matrix) override;
   void setMatrix(const SkMatrix& matrix) override;
+  void setMatrix(const SkM44& matrix) override;
 
   void clipRect(const SkRect& rect, SkClipOp op, bool antialias) override;
   void clipRRect(const SkRRect& rrect, SkClipOp op, bool antialias) override;
diff --git a/cc/paint/skia_paint_canvas.cc b/cc/paint/skia_paint_canvas.cc
index a9674da9..fcda71d 100644
--- a/cc/paint/skia_paint_canvas.cc
+++ b/cc/paint/skia_paint_canvas.cc
@@ -93,6 +93,10 @@
   canvas_->rotate(degrees);
 }
 
+void SkiaPaintCanvas::setMatrix(const SkM44& matrix) {
+  canvas_->setMatrix(matrix);
+}
+
 void SkiaPaintCanvas::concat(const SkMatrix& matrix) {
   canvas_->concat(matrix);
 }
diff --git a/cc/paint/skia_paint_canvas.h b/cc/paint/skia_paint_canvas.h
index dcc7f46a..9cf0de2 100644
--- a/cc/paint/skia_paint_canvas.h
+++ b/cc/paint/skia_paint_canvas.h
@@ -70,6 +70,7 @@
   void rotate(SkScalar degrees) override;
   void concat(const SkMatrix& matrix) override;
   void setMatrix(const SkMatrix& matrix) override;
+  void setMatrix(const SkM44& matrix) override;
 
   void clipRect(const SkRect& rect, SkClipOp op, bool do_anti_alias) override;
   void clipRRect(const SkRRect& rrect,
diff --git a/cc/paint/solid_color_analyzer.cc b/cc/paint/solid_color_analyzer.cc
index 4e3ca847..b50ef7e1 100644
--- a/cc/paint/solid_color_analyzer.cc
+++ b/cc/paint/solid_color_analyzer.cc
@@ -352,6 +352,7 @@
       case PaintOpType::Concat:
       case PaintOpType::Scale:
       case PaintOpType::SetMatrix:
+      case PaintOpType::SetMatrix44:
       case PaintOpType::Restore:
       case PaintOpType::Rotate:
       case PaintOpType::Save:
diff --git a/cc/test/paint_op_helper.h b/cc/test/paint_op_helper.h
index 6d727b7..f3370b4d 100644
--- a/cc/test/paint_op_helper.h
+++ b/cc/test/paint_op_helper.h
@@ -207,6 +207,12 @@
             << PaintOpHelper::SkiaTypeToString(op->matrix) << ")";
         break;
       }
+      case PaintOpType::SetMatrix44: {
+        const auto* op = static_cast<const SetMatrix44Op*>(base_op);
+        str << "SetMatrix44Op(matrix="
+            << PaintOpHelper::SkiaTypeToString(op->matrix) << ")";
+        break;
+      }
       case PaintOpType::Translate: {
         const auto* op = static_cast<const TranslateOp*>(base_op);
         str << "TranslateOp(dx=" << PaintOpHelper::SkiaTypeToString(op->dx)
diff --git a/cc/test/test_options_provider.cc b/cc/test/test_options_provider.cc
index b289bf7..1e57765a 100644
--- a/cc/test/test_options_provider.cc
+++ b/cc/test/test_options_provider.cc
@@ -47,7 +47,7 @@
                          can_use_lcd_text_,
                          context_supports_distance_field_text_,
                          max_texture_size_,
-                         SkMatrix::I()),
+                         SkM44()),
       deserialize_options_(this,
                            &service_paint_cache_,
                            &strike_client_,
diff --git a/chrome/VERSION b/chrome/VERSION
index 6243587..ac373c28 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=89
 MINOR=0
-BUILD=4346
+BUILD=4348
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 96ca160..9c45450 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -2105,6 +2105,7 @@
     "//base:base_java",
     "//chrome/browser/version:java",
     "//components/version_info/android:version_constants_java",
+    "//content/public/android:content_java",
   ]
   if (webview_includes_weblayer) {
     deps += [ "//weblayer/browser/java:base_module_java" ]
@@ -2118,7 +2119,6 @@
     "java/src/com/google/ipc/invalidation/ticl/android2/channel/GcmRegistrationTaskService.java",
     "java/src/org/chromium/chrome/browser/ChromeBackgroundService.java",
     "java/src/org/chromium/chrome/browser/ChromeBackupAgent.java",
-    "java/src/org/chromium/chrome/browser/base/MainDexApplicationImpl.java",
     "java/src/org/chromium/chrome/browser/base/SplitChromeApplication.java",
     "java/src/org/chromium/chrome/browser/base/SplitCompatAppComponentFactory.java",
     "java/src/org/chromium/chrome/browser/base/SplitCompatApplication.java",
@@ -2132,16 +2132,23 @@
     "java/src/org/chromium/chrome/browser/base/SplitCompatRemoteViewsService.java",
     "java/src/org/chromium/chrome/browser/base/SplitCompatService.java",
     "java/src/org/chromium/chrome/browser/base/SplitCompatUtils.java",
+    "java/src/org/chromium/chrome/browser/base/SplitPreloader.java",
     "java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetService.java",
+    "java/src/org/chromium/chrome/browser/crash/ApplicationStatusTracker.java",
     "java/src/org/chromium/chrome/browser/crash/ChromeMinidumpUploadJobService.java",
+    "java/src/org/chromium/chrome/browser/crash/FirebaseConfig.java",
     "java/src/org/chromium/chrome/browser/crash/MinidumpUploadService.java",
+    "java/src/org/chromium/chrome/browser/crash/PureJavaExceptionHandler.java",
     "java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionService.java",
     "java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java",
     "java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java",
     "java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java",
     "java/src/org/chromium/chrome/browser/invalidation/ChromeBrowserSyncAdapterService.java",
+    "java/src/org/chromium/chrome/browser/language/AppLocaleUtils.java",
+    "java/src/org/chromium/chrome/browser/language/GlobalAppLocaleController.java",
     "java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java",
     "java/src/org/chromium/chrome/browser/media/ui/ChromeMediaNotificationControllerServices.java",
+    "java/src/org/chromium/chrome/browser/metrics/UmaUtils.java",
     "java/src/org/chromium/chrome/browser/notifications/NotificationJobService.java",
     "java/src/org/chromium/chrome/browser/notifications/NotificationService.java",
     "java/src/org/chromium/chrome/browser/omaha/OmahaClient.java",
@@ -2156,6 +2163,11 @@
     ":chrome_base_module_resources",
     "$google_play_services_package:google_play_services_gcm_java",
     "//base:base_java",
+    "//base:jni_java",
+    "//chrome/browser/flags:java",
+    "//chrome/browser/preferences:java",
+    "//chrome/browser/util:java",
+    "//components/crash/android:java",
     "//components/embedder_support/android:application_java",
     "//components/media_router/browser/android:cast_options_provider_java",
     "//components/minidump_uploader:minidump_uploader_java",
@@ -2163,6 +2175,7 @@
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:androidx_collection_collection_java",
     "//third_party/android_deps:androidx_fragment_fragment_java",
+    "//third_party/google_android_play_core:com_google_android_play_core_java",
     "//ui/android:ui_no_recycler_view_java",
 
     # Deps needed for child processes.
@@ -2197,6 +2210,7 @@
   jar_excluded_patterns = [ "*/ProductConfig.class" ]
 
   resources_package = "org.chromium.chrome.base"
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 }
 
 # Defines a target that derives from the monochrome public application. This
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index ba98cb1..83a6780 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -410,16 +410,13 @@
   "java/src/org/chromium/chrome/browser/contextualsearch/TapSuppressionHeuristics.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/TapWordEdgeSuppression.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/TapWordLengthSuppression.java",
-  "java/src/org/chromium/chrome/browser/crash/ApplicationStatusTracker.java",
   "java/src/org/chromium/chrome/browser/crash/ChromeMinidumpUploadJobServiceImpl.java",
   "java/src/org/chromium/chrome/browser/crash/ChromeMinidumpUploaderDelegate.java",
   "java/src/org/chromium/chrome/browser/crash/CrashUploadCountStore.java",
-  "java/src/org/chromium/chrome/browser/crash/FirebaseConfig.java",
   "java/src/org/chromium/chrome/browser/crash/LogcatExtractionRunnable.java",
   "java/src/org/chromium/chrome/browser/crash/MinidumpLogcatPrepender.java",
   "java/src/org/chromium/chrome/browser/crash/MinidumpUploadRetry.java",
   "java/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceImpl.java",
-  "java/src/org/chromium/chrome/browser/crash/PureJavaExceptionHandler.java",
   "java/src/org/chromium/chrome/browser/crash/PureJavaExceptionReporter.java",
   "java/src/org/chromium/chrome/browser/cryptids/ProbabilisticCryptidRenderer.java",
   "java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java",
@@ -755,8 +752,6 @@
   "java/src/org/chromium/chrome/browser/invalidation/ResumableDelayedTaskRunner.java",
   "java/src/org/chromium/chrome/browser/invalidation/SessionsInvalidationManager.java",
   "java/src/org/chromium/chrome/browser/javascript/WebContextFetcher.java",
-  "java/src/org/chromium/chrome/browser/language/AppLocaleUtils.java",
-  "java/src/org/chromium/chrome/browser/language/GlobalAppLocaleController.java",
   "java/src/org/chromium/chrome/browser/language/LanguageAskPrompt.java",
   "java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java",
   "java/src/org/chromium/chrome/browser/language/settings/AvailableUiLanguages.java",
@@ -799,7 +794,6 @@
   "java/src/org/chromium/chrome/browser/metrics/PageLoadMetrics.java",
   "java/src/org/chromium/chrome/browser/metrics/UkmRecorder.java",
   "java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java",
-  "java/src/org/chromium/chrome/browser/metrics/UmaUtils.java",
   "java/src/org/chromium/chrome/browser/metrics/VariationsSession.java",
   "java/src/org/chromium/chrome/browser/metrics/WebApkSplashscreenMetrics.java",
   "java/src/org/chromium/chrome/browser/metrics/WebApkUma.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 0fd031ad..3a7460b 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -16,6 +16,7 @@
   "junit/src/org/chromium/chrome/browser/background_sync/BackgroundSyncGooglePlayServicesCheckerTest.java",
   "junit/src/org/chromium/chrome/browser/background_sync/PeriodicBackgroundSyncChromeWakeUpTaskTest.java",
   "junit/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTaskTest.java",
+  "junit/src/org/chromium/chrome/browser/base/SplitPreloaderTest.java",
   "junit/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeaderTest.java",
   "junit/src/org/chromium/chrome/browser/browserservices/ClearDataDialogResultRecorderTest.java",
   "junit/src/org/chromium/chrome/browser/browserservices/ClientAppBroadcastReceiverTest.java",
diff --git a/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
index c1bb9789..0c43162 100644
--- a/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
@@ -874,6 +874,7 @@
         android:authorities="$PACKAGE.FileProvider"
         android:exported="false"
         android:grantUriPermissions="true"
+        android:initOrder="10000"
         android:name="org.chromium.chrome.browser.util.ChromeFileProvider">
       <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/>
     </provider>  # DIFF-ANCHOR: 6e306896
diff --git a/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected b/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
index cb6797ad..1e37f8b 100644
--- a/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
+++ b/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
@@ -812,6 +812,7 @@
         android:authorities="$PACKAGE.FileProvider"
         android:exported="false"
         android:grantUriPermissions="true"
+        android:initOrder="10000"
         android:name="org.chromium.chrome.browser.util.ChromeFileProvider">
       <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/>
     </provider>  # DIFF-ANCHOR: 6e306896
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
index a8691fd..8dad709 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
@@ -6,11 +6,11 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.os.Build;
 import android.os.Bundle;
 
 import androidx.annotation.Nullable;
 
-import org.chromium.base.BuildInfo;
 import org.chromium.base.Callback;
 import org.chromium.base.FieldTrialList;
 import org.chromium.chrome.browser.ActivityTabProvider;
@@ -168,7 +168,7 @@
      * that direct actions are available at all.
      */
     public static boolean areDirectActionsAvailable(@ActivityType int activityType) {
-        return BuildInfo.isAtLeastQ()
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
                 && (activityType == ActivityType.CUSTOM_TAB || activityType == ActivityType.TABBED)
                 && ChromeFeatureList.isEnabled(ChromeFeatureList.AUTOFILL_ASSISTANT_DIRECT_ACTIONS)
                 && ChromeFeatureList.isEnabled(ChromeFeatureList.AUTOFILL_ASSISTANT);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
index c2ab324..513924692 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
@@ -218,11 +218,11 @@
                 pageInfoButton.getPrimaryTextView().setText(query);
             }
         } else if (TabProperties.SHOPPING_PERSISTED_TAB_DATA_FETCHER == propertyKey) {
+            PriceCardView priceCardView =
+                    (PriceCardView) view.fastFindViewById(R.id.price_info_box_outer);
             if (model.get(TabProperties.SHOPPING_PERSISTED_TAB_DATA_FETCHER) != null) {
                 model.get(TabProperties.SHOPPING_PERSISTED_TAB_DATA_FETCHER)
                         .fetch((shoppingPersistedTabData) -> {
-                            PriceCardView priceCardView = (PriceCardView) view.fastFindViewById(
-                                    R.id.price_info_box_outer);
                             if (shoppingPersistedTabData.getPriceDrop() == null) {
                                 priceCardView.setVisibility(View.GONE);
                             } else {
@@ -232,6 +232,8 @@
                                 priceCardView.setVisibility(View.VISIBLE);
                             }
                         });
+            } else {
+                priceCardView.setVisibility(View.GONE);
             }
         } else if (TabProperties.PAGE_INFO_LISTENER == propertyKey) {
             TabListMediator.TabActionListener listener =
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
index df72f2b..484d1234 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
@@ -620,6 +620,20 @@
                 tab, fetcher, View.GONE, EXPECTED_PRICE_STRING, EXPECTED_PREVIOUS_PRICE_STRING);
     }
 
+    @Test
+    @MediumTest
+    @UiThreadTest
+    public void testPriceStringTurnFeatureOff() {
+        Tab tab = MockTab.createAndInitialize(1, false);
+        MockShoppingPersistedTabDataFetcher fetcher = new MockShoppingPersistedTabDataFetcher(tab);
+        fetcher.setPriceStrings(EXPECTED_PRICE_STRING, EXPECTED_PREVIOUS_PRICE_STRING);
+        testPriceString(
+                tab, fetcher, View.VISIBLE, EXPECTED_PRICE_STRING, EXPECTED_PREVIOUS_PRICE_STRING);
+        mGridModel.set(TabProperties.SHOPPING_PERSISTED_TAB_DATA_FETCHER, null);
+        PriceCardView priceCardView = mTabGridView.findViewById(R.id.price_info_box_outer);
+        Assert.assertEquals(View.GONE, priceCardView.getVisibility());
+    }
+
     private void testPriceString(Tab tab, MockShoppingPersistedTabDataFetcher fetcher,
             int expectedVisibility, String expectedCurrentPrice, String expectedPreviousPrice) {
         TabUiFeatureUtilities.ENABLE_PRICE_TRACKING.setForTesting(true);
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index b30872a..1157b22c 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -886,15 +886,6 @@
                     android:readPermission="android.permission.GLOBAL_SEARCH" />
         </provider>
 
-        <!-- Provider for FileProvider. -->
-        <provider android:name="org.chromium.chrome.browser.util.ChromeFileProvider"
-            android:authorities="{{ manifest_package }}.FileProvider"
-            android:exported="false"
-            android:grantUriPermissions="true">
-            <meta-data android:name="android.support.FILE_PROVIDER_PATHS"
-                android:resource="@xml/file_paths" />
-        </provider>
-
         <provider android:name="org.chromium.chrome.browser.download.DownloadFileProvider"
             android:authorities="{{ manifest_package }}.DownloadFileProvider"
             android:exported="false"
@@ -1261,6 +1252,18 @@
           "com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
           android:value="org.chromium.components.media_router.caf.CastOptionsProvider"/>
 
+      <!-- Provider for FileProvider. This is declared in the base module and
+           has the initOrder attribute to ensure it is loaded first and gives
+           the chrome split preloader more time to work. -->
+      <provider android:name="org.chromium.chrome.browser.util.ChromeFileProvider"
+          android:authorities="{{ manifest_package }}.FileProvider"
+          android:exported="false"
+          android:initOrder="10000"
+          android:grantUriPermissions="true">
+          <meta-data android:name="android.support.FILE_PROVIDER_PATHS"
+              android:resource="@xml/file_paths" />
+      </provider>
+
       {% block base_application_definitions %}
       {% endblock %}
       {% block extra_application_definitions_for_test %}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
index b0c8c49..e092898 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
@@ -4,31 +4,16 @@
 
 package org.chromium.chrome.browser;
 
-import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.os.Bundle;
 
 import androidx.annotation.Nullable;
 
-import org.chromium.base.ApplicationState;
-import org.chromium.base.ApplicationStatus;
-import org.chromium.base.BuildInfo;
-import org.chromium.base.CommandLineInitUtil;
-import org.chromium.base.ContextUtils;
-import org.chromium.base.LocaleUtils;
-import org.chromium.base.PathUtils;
-import org.chromium.base.TraceEvent;
 import org.chromium.base.annotations.MainDex;
 import org.chromium.base.library_loader.LibraryLoader;
-import org.chromium.base.memory.MemoryPressureMonitor;
 import org.chromium.chrome.browser.background_task_scheduler.ChromeBackgroundTaskFactory;
-import org.chromium.chrome.browser.base.MainDexApplicationImpl;
 import org.chromium.chrome.browser.base.SplitCompatApplication;
-import org.chromium.chrome.browser.crash.ApplicationStatusTracker;
-import org.chromium.chrome.browser.crash.FirebaseConfig;
-import org.chromium.chrome.browser.crash.PureJavaExceptionHandler;
 import org.chromium.chrome.browser.crash.PureJavaExceptionReporter;
 import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
 import org.chromium.chrome.browser.dependency_injection.ChromeAppComponent;
@@ -37,13 +22,10 @@
 import org.chromium.chrome.browser.dependency_injection.ModuleFactoryOverrides;
 import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.language.GlobalAppLocaleController;
-import org.chromium.chrome.browser.metrics.UmaUtils;
 import org.chromium.chrome.browser.night_mode.SystemNightModeMonitor;
 import org.chromium.chrome.browser.vr.OnExitVrRequestListener;
 import org.chromium.chrome.browser.vr.VrModuleProvider;
 import org.chromium.components.browser_ui.util.GlobalDiscardableReferencePool;
-import org.chromium.components.module_installer.util.ModuleUtil;
 import org.chromium.components.version_info.Channel;
 import org.chromium.components.version_info.VersionConstants;
 import org.chromium.url.GURL;
@@ -56,102 +38,15 @@
  * called from the superclass. See {@link SplitCompatApplication} for more info.
  */
 public class ChromeApplication extends SplitCompatApplication {
-    private static final String COMMAND_LINE_FILE = "chrome-command-line";
-    // Public to allow use in ChromeBackupAgent
-    public static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome";
-
     /** Lock on creation of sComponent. */
     private static final Object sLock = new Object();
     @Nullable
     private static volatile ChromeAppComponent sComponent;
 
     /** Chrome application logic. */
-    public static class ChromeApplicationImpl extends MainDexApplicationImpl {
+    public static class ChromeApplicationImpl extends Impl {
         public ChromeApplicationImpl() {}
 
-        // Called by the framework for ALL processes. Runs before ContentProviders are created.
-        // Quirk: context.getApplicationContext() returns null during this method.
-        @Override
-        public void attachBaseContext(Context context) {
-            boolean isBrowserProcess = SplitCompatApplication.isBrowserProcess();
-
-            if (isBrowserProcess) {
-                UmaUtils.recordMainEntryPointTime();
-
-                // If the app locale override preference is set, create a new override
-                // context to use as the base context for the application.
-                // Must be initialized early to override Application level localizations.
-                if (GlobalAppLocaleController.getInstance().init(context)) {
-                    Configuration config =
-                            GlobalAppLocaleController.getInstance().getOverrideConfig(context);
-                    LocaleUtils.setDefaultLocalesFromConfiguration(config);
-                    context = context.createConfigurationContext(config);
-                }
-            }
-
-            super.attachBaseContext(context);
-            if (isBrowserProcess) {
-                checkAppBeingReplaced();
-
-                PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
-                // Renderer and GPU processes have command line passed to them via IPC
-                // (see ChildProcessService.java).
-                CommandLineInitUtil.initCommandLine(
-                        COMMAND_LINE_FILE, ChromeApplicationImpl::shouldUseDebugFlags);
-
-                // Enable ATrace on debug OS or app builds.
-                int applicationFlags = context.getApplicationInfo().flags;
-                boolean isAppDebuggable = (applicationFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
-                boolean isOsDebuggable = BuildInfo.isDebugAndroid();
-                // Requires command-line flags.
-                TraceEvent.maybeEnableEarlyTracing(
-                        (isAppDebuggable || isOsDebuggable) ? TraceEvent.ATRACE_TAG_APP : 0,
-                        /*readCommandLine=*/true);
-                TraceEvent.begin("ChromeApplication.attachBaseContext");
-
-                // Register for activity lifecycle callbacks. Must be done before any activities are
-                // created and is needed only by processes that use the ApplicationStatus api (which
-                // for Chrome is just the browser process).
-                ApplicationStatus.initialize(getApplication());
-
-                // Register and initialize application status listener for crashes, this needs to be
-                // done as early as possible so that this value is set before any crashes are
-                // reported.
-                ApplicationStatusTracker tracker = new ApplicationStatusTracker();
-                tracker.onApplicationStateChange(ApplicationStatus.getStateForApplication());
-                ApplicationStatus.registerApplicationStateListener(tracker);
-
-                // Disable MemoryPressureMonitor polling when Chrome goes to the background.
-                ApplicationStatus.registerApplicationStateListener(
-                        ChromeApplicationImpl::updateMemoryPressurePolling);
-
-                // Initializes the support for dynamic feature modules (browser only).
-                ModuleUtil.initApplication();
-
-                // Set Chrome factory for mapping BackgroundTask classes to TaskIds.
-                ChromeBackgroundTaskFactory.setAsDefault();
-
-                AppHooks.get().getChimeDelegate().initialize();
-
-                if (VersionConstants.CHANNEL == Channel.CANARY) {
-                    GURL.setReportDebugThrowableCallback(
-                            PureJavaExceptionReporter::reportJavaException);
-                }
-            }
-
-            BuildInfo.setFirebaseAppId(FirebaseConfig.getFirebaseAppId());
-
-            if (!ContextUtils.isIsolatedProcess()) {
-                // Incremental install disables process isolation, so things in this block will
-                // actually be run for incremental apks, but not normal apks.
-                PureJavaExceptionHandler.installHandler();
-            }
-
-            if (isBrowserProcess) {
-                TraceEvent.end("ChromeApplication.attachBaseContext");
-            }
-        }
-
         @Override
         public void onCreate() {
             super.onCreate();
@@ -161,28 +56,16 @@
                 // Kick off library loading in a separate thread so it's ready when we need it.
                 new Thread(() -> LibraryLoader.getInstance().ensureMainDexInitialized()).start();
             }
-        }
 
-        private static Boolean shouldUseDebugFlags() {
-            return CachedFeatureFlags.isEnabled(ChromeFeatureList.COMMAND_LINE_ON_NON_ROOTED);
-        }
-
-        private static void updateMemoryPressurePolling(@ApplicationState int newState) {
-            if (newState == ApplicationState.HAS_RUNNING_ACTIVITIES) {
-                MemoryPressureMonitor.INSTANCE.enablePolling();
-            } else if (newState == ApplicationState.HAS_STOPPED_ACTIVITIES) {
-                MemoryPressureMonitor.INSTANCE.disablePolling();
+            if (VersionConstants.CHANNEL == Channel.CANARY) {
+                GURL.setReportDebugThrowableCallback(
+                        PureJavaExceptionReporter::reportJavaException);
             }
-        }
 
-        /** Ensure this application object is not out-of-date. */
-        private static void checkAppBeingReplaced() {
-            // During app update the old apk can still be triggered by broadcasts and spin up an
-            // out-of-date application. Kill old applications in this bad state. See
-            // http://crbug.com/658130 for more context and http://b.android.com/56296 for the bug.
-            if (ContextUtils.getApplicationAssets() == null) {
-                throw new RuntimeException("App out of date, getResources() null, closing app.");
-            }
+            // Set Chrome factory for mapping BackgroundTask classes to TaskIds.
+            ChromeBackgroundTaskFactory.setAsDefault();
+
+            AppHooks.get().getChimeDelegate().initialize();
         }
 
         @MainDex
@@ -229,7 +112,7 @@
     }
 
     public ChromeApplication(Impl impl) {
-        setImpl(impl);
+        setImplSupplier(() -> impl);
     }
 
     public ChromeApplication() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java
index 89e8c16..8aeb1b7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java
@@ -9,10 +9,8 @@
 import org.chromium.android_webview.nonembedded.WebViewApkApplication;
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationStatus;
-import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.chrome.browser.base.SplitMonochromeApplication;
 import org.chromium.chrome.browser.version.ChromeVersionInfo;
-import org.chromium.content_public.browser.ChildProcessCreationParams;
 
 /**
  * This is Application class for Monochrome.
@@ -36,29 +34,13 @@
         public MonochromeApplicationImpl() {}
 
         @Override
-        public void attachBaseContext(Context context) {
-            super.attachBaseContext(context);
-            SplitMonochromeApplication.initializeMonochromeProcessCommon();
-            // ChildProcessCreationParams is only needed for browser process, though it is
-            // created and set in all processes. We must set isExternalService to true for
-            // Monochrome because Monochrome's renderer services are shared with WebView
-            // and are external, and will fail to bind otherwise.
-            boolean bindToCaller = false;
-            boolean ignoreVisibilityForImportance = false;
-            ChildProcessCreationParams.set(getApplication().getPackageName(),
-                    null /* privilegedServicesName */, getApplication().getPackageName(),
-                    null /* sandboxedServicesName */, true /* isExternalService */,
-                    LibraryProcessType.PROCESS_CHILD, bindToCaller, ignoreVisibilityForImportance);
-        }
-
-        @Override
         public void onCreate() {
             super.onCreate();
             if (!ChromeVersionInfo.isStableBuild()) {
                 // Performing Monochrome WebView DevTools Launcher icon showing/hiding logic in
                 // onCreate rather than in attachBaseContext() because it depends on application
                 // context being initiatied.
-                if (isWebViewProcess()) {
+                if (getApplication().isWebViewProcess()) {
                     // Whenever a monochrome webview process is launched (WebView service or
                     // developer UI), post a background task to show/hide the DevTools icon.
                     WebViewApkApplication.postDeveloperUiLauncherIconTask();
@@ -74,10 +56,16 @@
                 }
             }
         }
+    }
 
-        @Override
-        public boolean isWebViewProcess() {
-            return WebViewApkApplication.isWebViewProcess();
-        }
+    @Override
+    public void attachBaseContext(Context context) {
+        super.attachBaseContext(context);
+        SplitMonochromeApplication.initializeMonochromeProcessCommon(getPackageName());
+    }
+
+    @Override
+    public boolean isWebViewProcess() {
+        return WebViewApkApplication.isWebViewProcess();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/base/MainDexApplicationImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/base/MainDexApplicationImpl.java
deleted file mode 100644
index 4180289..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/base/MainDexApplicationImpl.java
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.base;
-
-import android.content.Context;
-
-import androidx.annotation.CallSuper;
-
-import org.chromium.base.BundleUtils;
-import org.chromium.base.ContextUtils;
-import org.chromium.base.JNIUtils;
-import org.chromium.base.library_loader.LibraryLoader;
-import org.chromium.base.library_loader.LibraryProcessType;
-import org.chromium.base.memory.MemoryPressureMonitor;
-import org.chromium.base.task.AsyncTask;
-import org.chromium.chrome.browser.ProductConfig;
-import org.chromium.components.embedder_support.application.FontPreloadingWorkaround;
-import org.chromium.components.module_installer.util.ModuleUtil;
-import org.chromium.ui.base.ResourceBundle;
-
-/**
- * Implementation of {@link SplitCompatApplication.Impl} that will be initialized in all processes.
- */
-public class MainDexApplicationImpl extends SplitCompatApplication.Impl {
-    @Override
-    @CallSuper
-    public void onCreate() {
-        // These can't go in attachBaseContext because Context.getApplicationContext() (which
-        // they use under-the-hood) does not work until after it returns.
-        FontPreloadingWorkaround.maybeInstallWorkaround(getApplication());
-        MemoryPressureMonitor.INSTANCE.registerComponentCallbacks();
-    }
-
-    @Override
-    @CallSuper
-    public void attachBaseContext(Context context) {
-        super.attachBaseContext(context);
-        // Perform initialization of globals common to all processes.
-        ContextUtils.initApplicationContext(getApplication());
-        maybeInitProcessType();
-        BundleUtils.setIsBundle(ProductConfig.IS_BUNDLE);
-
-        // Write installed modules to crash keys. This needs to be done as early as possible so
-        // that these values are set before any crashes are reported.
-        ModuleUtil.updateCrashKeys();
-
-        AsyncTask.takeOverAndroidThreadPool();
-        JNIUtils.setClassLoader(getApplication().getClassLoader());
-        ResourceBundle.setAvailablePakLocales(
-                ProductConfig.COMPRESSED_LOCALES, ProductConfig.UNCOMPRESSED_LOCALES);
-        LibraryLoader.getInstance().setLinkerImplementation(
-                ProductConfig.USE_CHROMIUM_LINKER, ProductConfig.USE_MODERN_LINKER);
-        LibraryLoader.getInstance().enableJniChecks();
-    }
-
-    public boolean isWebViewProcess() {
-        return false;
-    }
-
-    private void maybeInitProcessType() {
-        if (SplitCompatApplication.isBrowserProcess()) {
-            LibraryLoader.getInstance().setLibraryProcessType(LibraryProcessType.PROCESS_BROWSER);
-            return;
-        }
-        // WebView initialization sets the correct process type.
-        if (isWebViewProcess()) return;
-
-        // Child processes set their own process type when bound.
-        String processName = ContextUtils.getProcessName();
-        if (processName.contains("privileged_process")
-                || processName.contains("sandboxed_process")) {
-            return;
-        }
-
-        // We must be in an isolated service process.
-        LibraryLoader.getInstance().setLibraryProcessType(LibraryProcessType.PROCESS_CHILD);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitChromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitChromeApplication.java
index 4c6f94e..cb3c3e3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitChromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitChromeApplication.java
@@ -4,12 +4,16 @@
 
 package org.chromium.chrome.browser.base;
 
+import static org.chromium.chrome.browser.base.SplitCompatUtils.CHROME_SPLIT_NAME;
+
 import android.app.Activity;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
 
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationStatus;
+import org.chromium.base.JNIUtils;
 
 import java.lang.reflect.Field;
 
@@ -21,6 +25,7 @@
  */
 public class SplitChromeApplication extends SplitCompatApplication {
     private String mChromeApplicationClassName;
+    private SplitPreloader mSplitPreloader;
 
     public SplitChromeApplication() {
         this(SplitCompatUtils.getIdentifierName(
@@ -32,21 +37,58 @@
     }
 
     @Override
-    protected void attachBaseContext(Context context) {
-        if (isBrowserProcess()) {
-            context = SplitCompatUtils.createChromeContext(context);
-            setImpl((Impl) SplitCompatUtils.newInstance(context, mChromeApplicationClassName));
-        } else {
-            setImpl(createNonBrowserApplication());
+    public void onCreate() {
+        if (mSplitPreloader != null) {
+            mSplitPreloader.wait(CHROME_SPLIT_NAME);
         }
+        super.onCreate();
+    }
+
+    @Override
+    protected void attachBaseContext(Context context) {
         super.attachBaseContext(context);
         if (isBrowserProcess()) {
+            setImplSupplier(() -> {
+                Context chromeContext = SplitCompatUtils.createChromeContext(this);
+                return (Impl) SplitCompatUtils.newInstance(
+                        chromeContext, mChromeApplicationClassName);
+            });
             applyActivityClassLoaderWorkaround();
+        } else {
+            setImplSupplier(() -> createNonBrowserApplication());
         }
     }
 
-    protected MainDexApplicationImpl createNonBrowserApplication() {
-        return new MainDexApplicationImpl();
+    @Override
+    public Context createContextForSplit(String name) throws PackageManager.NameNotFoundException {
+        if (mSplitPreloader != null) {
+            // Wait for any splits that are preloading so we don't have a race to update the
+            // class loader cache (b/172602571).
+            mSplitPreloader.wait(name);
+        }
+        return super.createContextForSplit(name);
+    }
+
+    @Override
+    protected void performBrowserProcessPreloading(Context context) {
+        // The chrome split has a large amount of code, which can slow down startup. Loading
+        // this in the background allows us to do this in parallel with startup tasks which do
+        // not depend on code in the chrome split.
+        mSplitPreloader = new SplitPreloader(context);
+        mSplitPreloader.preload(CHROME_SPLIT_NAME, (chromeContext) -> {
+            // When installed, the vr module is always loaded on startup, so preload here.
+            mSplitPreloader.preload("vr", null);
+            // If the chrome module is not enabled, chromeContext will have the same ClassLoader as
+            // the base context, so no need to replace the ClassLoaders here.
+            if (!getClassLoader().equals(chromeContext.getClassLoader())) {
+                replaceClassLoader(this, chromeContext.getClassLoader());
+                JNIUtils.setClassLoader(chromeContext.getClassLoader());
+            }
+        });
+    }
+
+    protected Impl createNonBrowserApplication() {
+        return new Impl();
     }
 
     /**
@@ -75,21 +117,24 @@
                             return;
                         }
 
-                        Context baseContext = activity.getBaseContext();
-                        while (baseContext instanceof ContextWrapper) {
-                            baseContext = ((ContextWrapper) baseContext).getBaseContext();
-                        }
-
-                        try {
-                            // baseContext should now be an instance of ContextImpl.
-                            Field classLoaderField =
-                                    baseContext.getClass().getDeclaredField("mClassLoader");
-                            classLoaderField.setAccessible(true);
-                            classLoaderField.set(baseContext, activity.getClass().getClassLoader());
-                        } catch (ReflectiveOperationException e) {
-                            throw new RuntimeException("Error fixing Activity ClassLoader.", e);
-                        }
+                        replaceClassLoader(
+                                activity.getBaseContext(), activity.getClass().getClassLoader());
                     }
                 });
     }
+
+    private static void replaceClassLoader(Context baseContext, ClassLoader classLoader) {
+        while (baseContext instanceof ContextWrapper) {
+            baseContext = ((ContextWrapper) baseContext).getBaseContext();
+        }
+
+        try {
+            // baseContext should now be an instance of ContextImpl.
+            Field classLoaderField = baseContext.getClass().getDeclaredField("mClassLoader");
+            classLoaderField.setAccessible(true);
+            classLoaderField.set(baseContext, classLoader);
+        } catch (ReflectiveOperationException e) {
+            throw new RuntimeException("Error setting ClassLoader.", e);
+        }
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitCompatApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitCompatApplication.java
index 2c372a6..86e5f77c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitCompatApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitCompatApplication.java
@@ -7,12 +7,38 @@
 import android.app.Application;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.os.Bundle;
 
 import androidx.annotation.CallSuper;
 
+import org.chromium.base.ApplicationState;
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.BuildInfo;
+import org.chromium.base.BundleUtils;
+import org.chromium.base.CommandLineInitUtil;
 import org.chromium.base.ContextUtils;
+import org.chromium.base.JNIUtils;
+import org.chromium.base.LocaleUtils;
+import org.chromium.base.PathUtils;
+import org.chromium.base.TraceEvent;
+import org.chromium.base.library_loader.LibraryLoader;
+import org.chromium.base.library_loader.LibraryProcessType;
+import org.chromium.base.memory.MemoryPressureMonitor;
+import org.chromium.base.supplier.Supplier;
+import org.chromium.base.task.AsyncTask;
+import org.chromium.chrome.browser.ProductConfig;
+import org.chromium.chrome.browser.crash.ApplicationStatusTracker;
+import org.chromium.chrome.browser.crash.FirebaseConfig;
+import org.chromium.chrome.browser.crash.PureJavaExceptionHandler;
+import org.chromium.chrome.browser.flags.CachedFeatureFlags;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.language.GlobalAppLocaleController;
+import org.chromium.chrome.browser.metrics.UmaUtils;
+import org.chromium.components.embedder_support.application.FontPreloadingWorkaround;
+import org.chromium.components.module_installer.util.ModuleUtil;
+import org.chromium.ui.base.ResourceBundle;
 
 /**
  * Application base class which will call through to the given {@link Impl}. Application classes
@@ -20,6 +46,11 @@
  * calling {@link attachBaseContext(Context)}.
  */
 public class SplitCompatApplication extends Application {
+    private static final String COMMAND_LINE_FILE = "chrome-command-line";
+    // Public to allow use in ChromeBackupAgent
+    public static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome";
+
+    private Supplier<Impl> mImplSupplier;
     private Impl mImpl;
 
     /**
@@ -38,11 +69,6 @@
         }
 
         @CallSuper
-        public void attachBaseContext(Context context) {
-            mApplication.superAttachBaseContext(context);
-        }
-
-        @CallSuper
         public void startActivity(Intent intent, Bundle options) {
             mApplication.superStartActivity(intent, options);
         }
@@ -53,18 +79,18 @@
         public void onConfigurationChanged(Configuration newConfig) {}
     }
 
-    public final void setImpl(Impl impl) {
+    public final void setImplSupplier(Supplier<Impl> implSupplier) {
         assert mImpl == null;
-        mImpl = impl;
-        mImpl.setApplication(this);
+        assert mImplSupplier == null;
+        mImplSupplier = implSupplier;
     }
 
-    /**
-     * This exposes the super method so it can be called inside the Impl class code instead of just
-     * at the start.
-     */
-    private void superAttachBaseContext(Context context) {
-        super.attachBaseContext(context);
+    private Impl getImpl() {
+        if (mImpl == null) {
+            mImpl = mImplSupplier.get();
+            mImpl.setApplication(this);
+        }
+        return mImpl;
     }
 
     /**
@@ -75,21 +101,112 @@
         super.startActivity(intent, options);
     }
 
+    // Called by the framework for ALL processes. Runs before ContentProviders are created.
+    // Quirk: context.getApplicationContext() returns null during this method.
     @Override
     protected void attachBaseContext(Context context) {
-        mImpl.attachBaseContext(context);
+        boolean isBrowserProcess = isBrowserProcess();
+
+        if (isBrowserProcess) {
+            UmaUtils.recordMainEntryPointTime();
+            performBrowserProcessPreloading(context);
+
+            // If the app locale override preference is set, create a new override
+            // context to use as the base context for the application.
+            // Must be initialized early to override Application level localizations.
+            if (GlobalAppLocaleController.getInstance().init(context)) {
+                Configuration config =
+                        GlobalAppLocaleController.getInstance().getOverrideConfig(context);
+                LocaleUtils.setDefaultLocalesFromConfiguration(config);
+                context = context.createConfigurationContext(config);
+            }
+        }
+
+        super.attachBaseContext(context);
+        // Perform initialization of globals common to all processes.
+        ContextUtils.initApplicationContext(this);
+        maybeInitProcessType();
+        BundleUtils.setIsBundle(ProductConfig.IS_BUNDLE);
+
+        // Write installed modules to crash keys. This needs to be done as early as possible so
+        // that these values are set before any crashes are reported.
+        ModuleUtil.updateCrashKeys();
+
+        AsyncTask.takeOverAndroidThreadPool();
+        JNIUtils.setClassLoader(getClassLoader());
+        ResourceBundle.setAvailablePakLocales(
+                ProductConfig.COMPRESSED_LOCALES, ProductConfig.UNCOMPRESSED_LOCALES);
+        LibraryLoader.getInstance().setLinkerImplementation(
+                ProductConfig.USE_CHROMIUM_LINKER, ProductConfig.USE_MODERN_LINKER);
+        LibraryLoader.getInstance().enableJniChecks();
+
+        if (isBrowserProcess) {
+            checkAppBeingReplaced();
+
+            PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
+            // Renderer and GPU processes have command line passed to them via IPC
+            // (see ChildProcessService.java).
+            CommandLineInitUtil.initCommandLine(
+                    COMMAND_LINE_FILE, SplitCompatApplication::shouldUseDebugFlags);
+
+            // Enable ATrace on debug OS or app builds.
+            int applicationFlags = context.getApplicationInfo().flags;
+            boolean isAppDebuggable = (applicationFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+            boolean isOsDebuggable = BuildInfo.isDebugAndroid();
+            // Requires command-line flags.
+            TraceEvent.maybeEnableEarlyTracing(
+                    (isAppDebuggable || isOsDebuggable) ? TraceEvent.ATRACE_TAG_APP : 0,
+                    /*readCommandLine=*/true);
+            TraceEvent.begin("ChromeApplication.attachBaseContext");
+
+            // Register for activity lifecycle callbacks. Must be done before any activities are
+            // created and is needed only by processes that use the ApplicationStatus api (which
+            // for Chrome is just the browser process).
+            ApplicationStatus.initialize(this);
+
+            // Register and initialize application status listener for crashes, this needs to be
+            // done as early as possible so that this value is set before any crashes are
+            // reported.
+            ApplicationStatusTracker tracker = new ApplicationStatusTracker();
+            tracker.onApplicationStateChange(ApplicationStatus.getStateForApplication());
+            ApplicationStatus.registerApplicationStateListener(tracker);
+
+            // Disable MemoryPressureMonitor polling when Chrome goes to the background.
+            ApplicationStatus.registerApplicationStateListener(
+                    SplitCompatApplication::updateMemoryPressurePolling);
+
+            // Initializes the support for dynamic feature modules (browser only).
+            ModuleUtil.initApplication();
+        }
+
+        BuildInfo.setFirebaseAppId(FirebaseConfig.getFirebaseAppId());
+
+        if (!ContextUtils.isIsolatedProcess()) {
+            // Incremental install disables process isolation, so things in this block will
+            // actually be run for incremental apks, but not normal apks.
+            PureJavaExceptionHandler.installHandler();
+        }
+
+        if (isBrowserProcess) {
+            TraceEvent.end("ChromeApplication.attachBaseContext");
+        }
     }
 
     @Override
     public void onCreate() {
         super.onCreate();
-        mImpl.onCreate();
+        // These can't go in attachBaseContext because Context.getApplicationContext() (which
+        // they use under-the-hood) does not work until after it returns.
+        FontPreloadingWorkaround.maybeInstallWorkaround(this);
+        MemoryPressureMonitor.INSTANCE.registerComponentCallbacks();
+
+        getImpl().onCreate();
     }
 
     @Override
     public void onTrimMemory(int level) {
         super.onTrimMemory(level);
-        mImpl.onTrimMemory(level);
+        getImpl().onTrimMemory(level);
     }
 
     /** Forward all startActivity() calls to the two argument version. */
@@ -100,16 +217,67 @@
 
     @Override
     public void startActivity(Intent intent, Bundle options) {
-        mImpl.startActivity(intent, options);
+        getImpl().startActivity(intent, options);
     }
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        mImpl.onConfigurationChanged(newConfig);
+        getImpl().onConfigurationChanged(newConfig);
+    }
+
+    /**
+     * Called right after main entry point time is recorded in the browser process. This is called
+     * as early as possible to maximize preload time.
+     */
+    protected void performBrowserProcessPreloading(Context context) {}
+
+    public boolean isWebViewProcess() {
+        return false;
     }
 
     public static boolean isBrowserProcess() {
         return !ContextUtils.getProcessName().contains(":");
     }
+
+    private void maybeInitProcessType() {
+        if (isBrowserProcess()) {
+            LibraryLoader.getInstance().setLibraryProcessType(LibraryProcessType.PROCESS_BROWSER);
+            return;
+        }
+        // WebView initialization sets the correct process type.
+        if (isWebViewProcess()) return;
+
+        // Child processes set their own process type when bound.
+        String processName = ContextUtils.getProcessName();
+        if (processName.contains("privileged_process")
+                || processName.contains("sandboxed_process")) {
+            return;
+        }
+
+        // We must be in an isolated service process.
+        LibraryLoader.getInstance().setLibraryProcessType(LibraryProcessType.PROCESS_CHILD);
+    }
+
+    private static Boolean shouldUseDebugFlags() {
+        return CachedFeatureFlags.isEnabled(ChromeFeatureList.COMMAND_LINE_ON_NON_ROOTED);
+    }
+
+    private static void updateMemoryPressurePolling(@ApplicationState int newState) {
+        if (newState == ApplicationState.HAS_RUNNING_ACTIVITIES) {
+            MemoryPressureMonitor.INSTANCE.enablePolling();
+        } else if (newState == ApplicationState.HAS_STOPPED_ACTIVITIES) {
+            MemoryPressureMonitor.INSTANCE.disablePolling();
+        }
+    }
+
+    /** Ensure this application object is not out-of-date. */
+    private static void checkAppBeingReplaced() {
+        // During app update the old apk can still be triggered by broadcasts and spin up an
+        // out-of-date application. Kill old applications in this bad state. See
+        // http://crbug.com/658130 for more context and http://b.android.com/56296 for the bug.
+        if (ContextUtils.getApplicationAssets() == null) {
+            throw new RuntimeException("App out of date, getResources() null, closing app.");
+        }
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitCompatUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitCompatUtils.java
index 82adbe7..29066c6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitCompatUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitCompatUtils.java
@@ -18,7 +18,7 @@
 
 /** Utils for compatibility with isolated splits. */
 public class SplitCompatUtils {
-    private static final String CHROME_SPLIT_NAME = "chrome";
+    public static final String CHROME_SPLIT_NAME = "chrome";
     private static final ArraySet<ClassLoader> sInflationClassLoaders = new ArraySet<>();
 
     private SplitCompatUtils() {}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitMonochromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitMonochromeApplication.java
index 978b2013..2b69423 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitMonochromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitMonochromeApplication.java
@@ -10,34 +10,25 @@
 
 import org.chromium.android_webview.nonembedded.WebViewApkApplication;
 import org.chromium.base.library_loader.LibraryLoader;
+import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.chrome.browser.version.ChromeVersionInfo;
+import org.chromium.content_public.browser.ChildProcessCreationParams;
 
 /**
  * Application class to use for Monochrome when //chrome code is in an isolated split. See {@link
  * SplitChromeApplication} for more info.
  */
 public class SplitMonochromeApplication extends SplitChromeApplication {
-    private static class NonBrowserMonochromeApplication extends MainDexApplicationImpl {
-        @Override
-        public void attachBaseContext(Context context) {
-            super.attachBaseContext(context);
-            initializeMonochromeProcessCommon();
-        }
-
+    private static class NonBrowserMonochromeApplication extends Impl {
         @Override
         public void onCreate() {
             super.onCreate();
             // TODO(crbug.com/1126301): This matches logic in MonochromeApplication.java.
             // Deduplicate if chrome split launches.
-            if (!ChromeVersionInfo.isStableBuild() && isWebViewProcess()) {
+            if (!ChromeVersionInfo.isStableBuild() && getApplication().isWebViewProcess()) {
                 WebViewApkApplication.postDeveloperUiLauncherIconTask();
             }
         }
-
-        @Override
-        public boolean isWebViewProcess() {
-            return WebViewApkApplication.isWebViewProcess();
-        }
     }
 
     public SplitMonochromeApplication() {
@@ -46,14 +37,35 @@
     }
 
     @Override
-    protected MainDexApplicationImpl createNonBrowserApplication() {
+    public void attachBaseContext(Context context) {
+        super.attachBaseContext(context);
+        initializeMonochromeProcessCommon(getPackageName());
+    }
+
+    @Override
+    protected Impl createNonBrowserApplication() {
         return new NonBrowserMonochromeApplication();
     }
 
-    public static void initializeMonochromeProcessCommon() {
+    public static void initializeMonochromeProcessCommon(String packageName) {
         WebViewApkApplication.maybeInitProcessGlobals();
         if (!LibraryLoader.getInstance().isLoadedByZygote()) {
             LibraryLoader.getInstance().setNativeLibraryPreloader(new MonochromeLibraryPreloader());
         }
+
+        // ChildProcessCreationParams is only needed for browser process, though it is
+        // created and set in all processes. We must set isExternalService to true for
+        // Monochrome because Monochrome's renderer services are shared with WebView
+        // and are external, and will fail to bind otherwise.
+        boolean bindToCaller = false;
+        boolean ignoreVisibilityForImportance = false;
+        ChildProcessCreationParams.set(packageName, null /* privilegedServicesName */, packageName,
+                null /* sandboxedServicesName */, true /* isExternalService */,
+                LibraryProcessType.PROCESS_CHILD, bindToCaller, ignoreVisibilityForImportance);
+    }
+
+    @Override
+    public boolean isWebViewProcess() {
+        return WebViewApkApplication.isWebViewProcess();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitPreloader.java b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitPreloader.java
new file mode 100644
index 0000000..ba22c2d
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitPreloader.java
@@ -0,0 +1,96 @@
+// 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.base;
+
+import android.content.Context;
+
+import androidx.collection.SimpleArrayMap;
+
+import org.chromium.base.BundleUtils;
+import org.chromium.base.task.AsyncTask;
+import org.chromium.base.task.TaskTraits;
+
+/**
+ * Handles preloading split Contexts on a background thread. Loading a new isolated split
+ * Context can be expensive since the ClassLoader may need to be created. See crbug.com/1150600 for
+ * more info.
+ */
+public class SplitPreloader {
+    private final SimpleArrayMap<String, PreloadTask> mPreloadTasks = new SimpleArrayMap<>();
+    private final Context mContext;
+
+    /** Interface to run code after preload completion. */
+    public interface OnComplete {
+        void run(Context context);
+    }
+
+    private class PreloadTask extends AsyncTask<Void> {
+        private final String mName;
+        private OnComplete mOnComplete;
+
+        public PreloadTask(String name, OnComplete onComplete) {
+            mName = name;
+            mOnComplete = onComplete;
+        }
+
+        @Override
+        protected Void doInBackground() {
+            BundleUtils.createIsolatedSplitContext(mContext, mName);
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            finish();
+        }
+
+        /**
+         * Waits for the preload to finish and calls the onComplete function if needed. onComplete
+         * is expected to be called before {@link SplitPreloader#wait(String)} returns, so this
+         * method is called there since onPostExecute does not run before get() returns.
+         */
+        public void finish() {
+            try {
+                get();
+            } catch (Exception e) {
+                // Ignore exception, not a problem if preload fails.
+            }
+            if (mOnComplete != null) {
+                // Recreate the context here to make sure we have the latest version, in case there
+                // was a race to update the class loader cache, see b/172602571.
+                mOnComplete.run(BundleUtils.createIsolatedSplitContext(mContext, mName));
+                mOnComplete = null;
+            }
+        }
+    }
+
+    public SplitPreloader(Context context) {
+        mContext = context;
+    }
+
+    /** Starts preloading a split context on a background thread. */
+    public void preload(String name, OnComplete onComplete) {
+        if (!BundleUtils.isIsolatedSplitInstalled(mContext, name)) {
+            if (onComplete != null) {
+                onComplete.run(mContext);
+            }
+            return;
+        }
+
+        assert !mPreloadTasks.containsKey(name);
+        PreloadTask task = new PreloadTask(name, onComplete);
+        task.executeWithTaskTraits(TaskTraits.USER_BLOCKING_MAY_BLOCK);
+        mPreloadTasks.put(name, task);
+    }
+
+    /** Waits for the specified split to be finished loading. */
+    public void wait(String name) {
+        PreloadTask task = mPreloadTasks.remove(name);
+        if (task != null) {
+            // Make sure the task is finished and onComplete has run.
+            task.finish();
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/TrustedWebActivityPermissionManager.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/TrustedWebActivityPermissionManager.java
index 1391fa0..34bc604b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/TrustedWebActivityPermissionManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/TrustedWebActivityPermissionManager.java
@@ -14,6 +14,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.os.Build;
 import android.text.TextUtils;
 
 import androidx.annotation.Nullable;
@@ -22,7 +23,6 @@
 import androidx.browser.trusted.Token;
 
 import org.chromium.base.ApplicationStatus;
-import org.chromium.base.BuildInfo;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.chrome.browser.ChromeApplication;
@@ -237,7 +237,7 @@
                 // are not able to detect if use is changing the setting to "ask every time", when
                 // there is no permission, return ASK to let the client app decide whether to show
                 // the prompt.
-                if (BuildInfo.isAtLeastR()) {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                     if (!enabled) return ContentSettingValues.ASK;
                 }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionHandler.java
index 75f710d..882f584 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionHandler.java
@@ -4,8 +4,10 @@
 
 package org.chromium.chrome.browser.crash;
 
+import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.MainDex;
+import org.chromium.chrome.browser.base.SplitCompatUtils;
 import org.chromium.components.crash.CrashKeys;
 
 /**
@@ -20,6 +22,11 @@
     private boolean mHandlingException;
     private static boolean sIsDisabled;
 
+    /** Interface to allow uploading reports. */
+    public interface JavaExceptionReporter {
+        void createAndUploadReport(Throwable e);
+    }
+
     private PureJavaExceptionHandler(Thread.UncaughtExceptionHandler parent) {
         mParent = parent;
     }
@@ -53,6 +60,10 @@
     }
 
     private void reportJavaException(Throwable e) {
-        PureJavaExceptionReporter.reportJavaException(e);
+        // PureJavaExceptionReporter may be in the chrome module, so load by reflection from there.
+        JavaExceptionReporter reporter = (JavaExceptionReporter) SplitCompatUtils.newInstance(
+                SplitCompatUtils.createChromeContext(ContextUtils.getApplicationContext()),
+                "org.chromium.chrome.browser.crash.PureJavaExceptionReporter");
+        reporter.createAndUploadReport(e);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionReporter.java b/chrome/android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionReporter.java
index 4739e8b2..9f05146 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionReporter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionReporter.java
@@ -19,6 +19,7 @@
 import org.chromium.base.PiiElider;
 import org.chromium.base.StrictModeContext;
 import org.chromium.base.annotations.MainDex;
+import org.chromium.base.annotations.UsedByReflection;
 import org.chromium.chrome.browser.version.ChromeVersionInfo;
 import org.chromium.components.crash.CrashKeys;
 
@@ -35,7 +36,8 @@
  * This class is written in pure Java, so it can handle exception happens before native is loaded.
  */
 @MainDex
-public class PureJavaExceptionReporter {
+@UsedByReflection("PureJavaExceptionHandler.java")
+public class PureJavaExceptionReporter implements PureJavaExceptionHandler.JavaExceptionReporter {
     // report fields, please keep the name sync with MIME blocks in breakpad_linux.cc
     public static final String CHANNEL = "channel";
     public static final String VERSION = "ver";
@@ -68,6 +70,9 @@
     private final String mLocalId = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
     private final String mBoundary = "------------" + UUID.randomUUID() + RN;
 
+    @UsedByReflection("PureJavaExceptionHandler.java")
+    public PureJavaExceptionReporter() {}
+
     /**
      * Report and upload the device info and stack trace as if it was a crash. Runs synchronously
      * and results in I/O on the main thread.
@@ -79,8 +84,8 @@
         reporter.createAndUploadReport(javaException);
     }
 
-    @VisibleForTesting
-    void createAndUploadReport(Throwable javaException) {
+    @Override
+    public void createAndUploadReport(Throwable javaException) {
         // It is OK to do IO in main thread when we know there is a crash happens.
         try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) {
             createReport(javaException);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
index ecd29fa4..43aa44b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
@@ -13,6 +13,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.provider.Browser;
@@ -31,7 +32,6 @@
 import org.xmlpull.v1.XmlPullParserFactory;
 
 import org.chromium.base.ApplicationStatus;
-import org.chromium.base.BuildInfo;
 import org.chromium.base.ContentUriUtils;
 import org.chromium.base.Log;
 import org.chromium.base.ObserverList;
@@ -991,7 +991,7 @@
                 String path = mDownloadInfo.getFilePath();
                 if (!TextUtils.isEmpty(path)) {
                     File fromFile = new File(path);
-                    if (BuildInfo.isAtLeastQ()) {
+                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                         // Copy the downloaded content to the intermediate URI and publish it.
                         String pendingUri =
                                 DownloadCollectionBridge.createIntermediateUriForPublish(
@@ -1023,13 +1023,13 @@
                     }
                     if (!success) {
                         if (fromFile.delete()) {
-                            if (BuildInfo.isAtLeastQ()) {
+                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                                 Log.w(TAG, "Failed to publish the downloaded file.");
                             } else {
                                 Log.w(TAG, "Failed to rename the file.");
                             }
                         } else {
-                            if (BuildInfo.isAtLeastQ()) {
+                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                                 Log.w(TAG, "Failed to publish and delete the file.");
                             } else {
                                 Log.w(TAG, "Failed to rename and delete the file.");
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaUtils.java
index 9932cf1..2c7e4a0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaUtils.java
@@ -106,11 +106,6 @@
         return sForegroundStartTimeMs;
     }
 
-    @CalledByNative
-    private static void setUsageAndCrashReportingFromNative(boolean enabled) {
-        UmaSessionStats.changeMetricsReportingConsent(enabled);
-    }
-
     @NativeMethods
     interface Natives {
         boolean isClientInMetricsReportingSample();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeUtils.java
index 24d836b..99a010b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeUtils.java
@@ -16,7 +16,6 @@
 import androidx.annotation.StyleRes;
 import androidx.annotation.VisibleForTesting;
 
-import org.chromium.base.BuildInfo;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 
 /**
@@ -137,7 +136,7 @@
         if (sNightModeDefaultToLightForTesting != null) {
             return sNightModeDefaultToLightForTesting;
         }
-        return !BuildInfo.isAtLeastQ();
+        return Build.VERSION.SDK_INT < Build.VERSION_CODES.Q;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/night_mode/settings/RadioButtonGroupThemePreference.java b/chrome/android/java/src/org/chromium/chrome/browser/night_mode/settings/RadioButtonGroupThemePreference.java
index c0b764e..262544d4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/night_mode/settings/RadioButtonGroupThemePreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/night_mode/settings/RadioButtonGroupThemePreference.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.night_mode.settings;
 
 import android.content.Context;
+import android.os.Build;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.CheckBox;
@@ -15,7 +16,6 @@
 import androidx.preference.Preference;
 import androidx.preference.PreferenceViewHolder;
 
-import org.chromium.base.BuildInfo;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.night_mode.NightModeMetrics;
@@ -81,7 +81,7 @@
         assert ThemeType.NUM_ENTRIES == 3;
         mButtons.set(ThemeType.SYSTEM_DEFAULT,
                 (RadioButtonWithDescription) holder.findViewById(R.id.system_default));
-        if (BuildInfo.isAtLeastQ()) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
             mButtons.get(ThemeType.SYSTEM_DEFAULT)
                     .setDescriptionText(
                             getContext().getString(R.string.themes_system_default_summary_api_29));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageArchivePublisherBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageArchivePublisherBridge.java
index fa4f816..2b5441fd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageArchivePublisherBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageArchivePublisherBridge.java
@@ -139,7 +139,7 @@
     @CalledByNative
     @VisibleForTesting
     public static String publishArchiveToDownloadsCollection(OfflinePageItem page) {
-        assert org.chromium.base.BuildInfo.isAtLeastQ();
+        assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
 
         final String isPending = "is_pending"; // MediaStore.IS_PENDING
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
index 0f3cd69..65ab38f2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
@@ -6,9 +6,11 @@
 
 import android.view.ActionMode;
 import android.view.View;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.core.view.ViewCompat;
+
 import org.chromium.base.CallbackController;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.OneshotSupplierImpl;
@@ -26,6 +28,7 @@
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionsDropdownEmbedder;
 import org.chromium.chrome.browser.omnibox.voice.AssistantVoiceSearchService;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
+import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManagerImpl;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.share.ShareDelegate;
 import org.chromium.chrome.browser.tabmodel.IncognitoStateProvider;
@@ -115,8 +118,10 @@
         mUrlBar = mLocationBarLayout.findViewById(R.id.url_bar);
         OneshotSupplierImpl<AssistantVoiceSearchService> assistantVoiceSearchSupplier =
                 new OneshotSupplierImpl();
-        mLocationBarMediator = new LocationBarMediator(
-                mLocationBarLayout, locationBarDataProvider, assistantVoiceSearchSupplier);
+
+        mLocationBarMediator = new LocationBarMediator(mLocationBarLayout, locationBarDataProvider,
+                assistantVoiceSearchSupplier, profileObservableSupplier,
+                PrivacyPreferencesManagerImpl.getInstance());
         mUrlCoordinator =
                 new UrlBarCoordinator((UrlBar) mUrlBar, windowDelegate, actionModeCallback,
                         mCallbackController.makeCancelable(mLocationBarMediator::onUrlFocusChange),
@@ -128,8 +133,10 @@
         StatusView statusView = mLocationBarLayout.findViewById(R.id.location_bar_status);
         mStatusCoordinator = new StatusCoordinator(isTablet(), statusView, mUrlCoordinator,
                 incognitoStateProvider, modalDialogManagerSupplier, locationBarDataProvider);
-        mLocationBarMediator.setCoordinators(mStatusCoordinator, mAutocompleteCoordinator);
+        mLocationBarMediator.setCoordinators(
+                mUrlCoordinator, mAutocompleteCoordinator, mStatusCoordinator);
         mUrlBar.setOnKeyListener(mLocationBarMediator);
+
         mUrlCoordinator.addUrlTextChangeListener(mAutocompleteCoordinator);
 
         // The LocationBar's direction is tied to the UrlBar's text direction. Icons inside the
@@ -142,9 +149,8 @@
 
         mLocationBarLayout.addUrlFocusChangeListener(mAutocompleteCoordinator);
         mLocationBarLayout.initialize(mAutocompleteCoordinator, mUrlCoordinator, mStatusCoordinator,
-                locationBarDataProvider, profileObservableSupplier, windowDelegate, windowAndroid,
-                overrideUrlLoadingDelegate, mLocationBarMediator.getVoiceRecognitionHandler(),
-                assistantVoiceSearchSupplier);
+                locationBarDataProvider, windowDelegate, windowAndroid, overrideUrlLoadingDelegate,
+                mLocationBarMediator.getVoiceRecognitionHandler(), assistantVoiceSearchSupplier);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index f65e469..f33a682 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -30,17 +30,12 @@
 import androidx.core.view.MarginLayoutParamsCompat;
 
 import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.Callback;
-import org.chromium.base.CallbackController;
-import org.chromium.base.CommandLine;
 import org.chromium.base.ObserverList;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
-import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.WindowDelegate;
-import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.ntp.NewTabPageUma;
 import org.chromium.chrome.browser.omnibox.UrlBar.ScrollType;
@@ -51,8 +46,6 @@
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinator;
 import org.chromium.chrome.browser.omnibox.voice.AssistantVoiceSearchService;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
-import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManagerImpl;
-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.util.ChromeAccessibilityUtil;
@@ -110,8 +103,6 @@
     private boolean mUrlFocusedWithoutAnimations;
     protected boolean mVoiceSearchEnabled;
 
-    private OmniboxPrerender mOmniboxPrerender;
-
     protected float mUrlFocusChangeFraction;
     protected LinearLayout mUrlActionContainer;
 
@@ -122,9 +113,6 @@
     private OneshotSupplier<AssistantVoiceSearchService> mAssistantVoiceSearchServiceSupplier;
     private Runnable mKeyboardResizeModeTask;
     private Runnable mKeyboardHideTask;
-    private ObservableSupplier<Profile> mProfileSupplier;
-    private Callback<Profile> mProfileSupplierObserver;
-    private CallbackController mCallbackController = new CallbackController();
     private TemplateUrlServiceObserver mTemplateUrlObserver;
     private OverrideUrlLoadingDelegate mOverrideUrlLoadingDelegate;
 
@@ -157,17 +145,6 @@
             mAutocompleteCoordinator = null;
         }
 
-        if (mCallbackController != null) {
-            mCallbackController.destroy();
-            mCallbackController = null;
-        }
-
-        if (mProfileSupplier != null) {
-            mProfileSupplier.removeObserver(mProfileSupplierObserver);
-            mProfileSupplier = null;
-            mProfileSupplierObserver = null;
-        }
-
         if (mTemplateUrlObserver != null) {
             TemplateUrlServiceFactory.get().removeObserver(mTemplateUrlObserver);
             mTemplateUrlObserver = null;
@@ -224,7 +201,6 @@
      * @param urlCoordinator The coordinator for interacting with the url bar.
      * @param statusCoordinator The coordinator for interacting with the status icon.
      * @param locationBarDataProvider Provider of LocationBar data, e.g. url and title.
-     * @param profileSupplier Supplier of the profile for the currently active TabModel.
      * @param windowDelegate {@link WindowDelegate} that will provide {@link Window} related info.
      * @param windowAndroid {@link WindowAndroid} that is used by the owning {@link Activity}.
      */
@@ -232,7 +208,6 @@
     public void initialize(@NonNull AutocompleteCoordinator autocompleteCoordinator,
             @NonNull UrlBarCoordinator urlCoordinator, @NonNull StatusCoordinator statusCoordinator,
             @NonNull LocationBarDataProvider locationBarDataProvider,
-            @NonNull ObservableSupplier<Profile> profileSupplier,
             @NonNull WindowDelegate windowDelegate, @NonNull WindowAndroid windowAndroid,
             @NonNull OverrideUrlLoadingDelegate overrideUrlLoadingDelegate,
             @NonNull VoiceRecognitionHandler voiceRecognitionHandler,
@@ -247,12 +222,6 @@
         mVoiceRecognitionHandler = voiceRecognitionHandler;
         mAssistantVoiceSearchServiceSupplier = assistantVoiceSearchSupplier;
 
-        assert profileSupplier != null;
-        assert mProfileSupplier == null;
-        mProfileSupplier = profileSupplier;
-        mProfileSupplierObserver = mCallbackController.makeCancelable(this::setProfile);
-        mProfileSupplier.addObserver(mProfileSupplierObserver);
-
         updateButtonVisibility();
         updateShouldAnimateIconChanges();
     }
@@ -263,8 +232,7 @@
     }
 
     /**
-     *  Signals to LocationBarLayout that's it safe to call code that requires native to be loaded,
-     * e.g. OmniboxPrerender.
+     *  Signals to LocationBarLayout that's it safe to call code that requires native to be loaded.
      */
     public void onFinishNativeInitialization() {
         TemplateUrlServiceFactory.get().runWhenLoaded(this::registerTemplateUrlObserver);
@@ -274,8 +242,6 @@
         mDeleteButton.setOnClickListener(this);
         mMicButton.setOnClickListener(this);
 
-        mOmniboxPrerender = new OmniboxPrerender();
-
         for (Runnable deferredRunnable : mDeferredNativeRunnables) {
             post(deferredRunnable);
         }
@@ -284,8 +250,6 @@
         onPrimaryColorChanged();
 
         updateMicButtonVisibility();
-
-        setProfile(mProfileSupplier.get());
     }
 
     /** Initiates a prefetch of autocomplete suggestions. */
@@ -351,26 +315,12 @@
 
     /* package */ void onSuggestionsHidden() {}
 
-    /* package */ void onSuggestionsChanged(String autocompleteText) {
-        String userText = mUrlCoordinator.getTextWithoutAutocomplete();
-        if (mUrlCoordinator.shouldAutocomplete()) {
-            mUrlCoordinator.setAutocompleteText(userText, autocompleteText);
-        }
-
+    /* package */ void onSuggestionsChanged() {
         // Handle the case where suggestions (in particular zero suggest) are received without the
         // URL focusing happening.
         if (mUrlFocusedWithoutAnimations && mUrlHasFocus) {
             handleUrlFocusAnimation(mUrlHasFocus);
         }
-
-        if (mNativeInitialized
-                && !CommandLine.getInstance().hasSwitch(ChromeSwitches.DISABLE_INSTANT)
-                && PrivacyPreferencesManagerImpl.getInstance().shouldPrerender()
-                && mLocationBarDataProvider.hasTab()) {
-            mOmniboxPrerender.prerenderMaybe(userText, getOriginalUrl(),
-                    mAutocompleteCoordinator.getCurrentNativeAutocompleteResult(),
-                    mLocationBarDataProvider.getProfile(), mLocationBarDataProvider.getTab());
-        }
     }
 
     @Override
@@ -643,10 +593,6 @@
         setUrlBarText(mLocationBarDataProvider.getUrlBarData(), UrlBar.ScrollType.SCROLL_TO_TLD,
                 SelectionState.SELECT_ALL);
         if (!mLocationBarDataProvider.hasTab()) return;
-
-        // Profile may be null if switching to a tab that has not yet been initialized.
-        Profile profile = mLocationBarDataProvider.getProfile();
-        if (profile != null && mOmniboxPrerender != null) mOmniboxPrerender.clear(profile);
     }
 
     /* package */ void setOmniboxEditingText(String text) {
@@ -789,20 +735,6 @@
         mStatusCoordinator.setShouldAnimateIconChanges(shouldAnimate);
     }
 
-    /**
-     * Updates the profile used by this LocationBar, for, e.g. determining incognito status or
-     * generating autocomplete suggestions..
-     * @param profile The profile to be used.
-     */
-    private void setProfile(Profile profile) {
-        if (profile == null || !mNativeInitialized) return;
-        mAutocompleteCoordinator.setAutocompleteProfile(profile);
-        mOmniboxPrerender.initializeForProfile(profile);
-
-        setShowIconsWhenUrlFocused(
-                SearchEngineLogoUtils.shouldShowSearchEngineLogo(profile.isOffTheRecord()));
-    }
-
     /** Focuses the current page. */
     private void focusCurrentTab() {
         if (mLocationBarDataProvider.hasTab()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
index 7c662073..f48b921 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
@@ -13,8 +13,12 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import org.chromium.base.CallbackController;
+import org.chromium.base.CommandLine;
+import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.chrome.browser.AppHooks;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.gsa.GSAState;
 import org.chromium.chrome.browser.native_page.NativePageFactory;
 import org.chromium.chrome.browser.ntp.FakeboxDelegate;
@@ -26,6 +30,8 @@
 import org.chromium.chrome.browser.omnibox.voice.AssistantVoiceSearchService;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
+import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManagerImpl;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.browser.util.KeyNavigationUtil;
 import org.chromium.ui.base.WindowAndroid;
@@ -47,16 +53,42 @@
     private final OneshotSupplierImpl<AssistantVoiceSearchService> mAssistantVoiceSearchSupplier;
     private StatusCoordinator mStatusCoordinator;
     private AutocompleteCoordinator mAutocompleteCoordinator;
+    private OmniboxPrerender mOmniboxPrerender;
+    private UrlBarCoordinator mUrlCoordinator;
+    private ObservableSupplier<Profile> mProfileSupplier;
+    private PrivacyPreferencesManagerImpl mPrivacyPreferencesManager;
+    private CallbackController mCallbackController = new CallbackController();
+
+    private boolean mNativeInitialized;
 
     /*package */ LocationBarMediator(@NonNull LocationBarLayout locationBarLayout,
             @NonNull LocationBarDataProvider locationBarDataProvider,
-            @NonNull OneshotSupplierImpl<AssistantVoiceSearchService>
-                    assistantVoiceSearchSupplier) {
+            @NonNull OneshotSupplierImpl<AssistantVoiceSearchService> assistantVoiceSearchSupplier,
+            @NonNull ObservableSupplier<Profile> profileSupplier,
+            @NonNull PrivacyPreferencesManagerImpl privacyPreferencesManager) {
         mLocationBarLayout = locationBarLayout;
         mLocationBarDataProvider = locationBarDataProvider;
         mLocationBarDataProvider.addObserver(this);
         mAssistantVoiceSearchSupplier = assistantVoiceSearchSupplier;
         mVoiceRecognitionHandler = new VoiceRecognitionHandler(this, mAssistantVoiceSearchSupplier);
+        mProfileSupplier = profileSupplier;
+        mProfileSupplier.addObserver(mCallbackController.makeCancelable(this::setProfile));
+        mPrivacyPreferencesManager = privacyPreferencesManager;
+    }
+
+    /**
+     * Sets coordinators post-construction; they can't be set at construction time since
+     * LocationBarMediator is a delegate for them, so is constructed beforehand.
+     *
+     * @param urlCoordinator Coordinator for the url bar.
+     * @param autocompleteCoordinator Coordinator for the autocomplete component.
+     * @param statusCoordinator Coordinator for the status icon.
+     */
+    /*package */ void setCoordinators(UrlBarCoordinator urlCoordinator,
+            AutocompleteCoordinator autocompleteCoordinator, StatusCoordinator statusCoordinator) {
+        mUrlCoordinator = urlCoordinator;
+        mAutocompleteCoordinator = autocompleteCoordinator;
+        mStatusCoordinator = statusCoordinator;
     }
 
     /*package */ void destroy() {
@@ -66,6 +98,8 @@
         }
         mStatusCoordinator = null;
         mAutocompleteCoordinator = null;
+        mUrlCoordinator = null;
+        mPrivacyPreferencesManager = null;
     }
 
     /*package */ void onUrlFocusChange(boolean hasFocus) {
@@ -73,12 +107,15 @@
     }
 
     /*package */ void onFinishNativeInitialization() {
+        mNativeInitialized = true;
+        mOmniboxPrerender = new OmniboxPrerender();
         Context context = mLocationBarLayout.getContext();
         mAssistantVoiceSearchService = new AssistantVoiceSearchService(context,
                 AppHooks.get().getExternalAuthUtils(), TemplateUrlServiceFactory.get(),
                 GSAState.getInstance(context), this, SharedPreferencesManager.getInstance());
         mAssistantVoiceSearchSupplier.set(mAssistantVoiceSearchService);
         mLocationBarLayout.onFinishNativeInitialization();
+        setProfile(mProfileSupplier.get());
     }
 
     /*package */ void setUrlFocusChangeFraction(float fraction) {
@@ -95,17 +132,15 @@
         mLocationBarLayout.setVoiceRecognitionHandlerForTesting(voiceRecognitionHandler);
     }
 
-    /**
-     * Sets coordinators post-construction; they can't be set at construction time since
-     * LocationBarMediator is a delegate for them, so is constructed beforehand.
-     *
-     * @param statusCoordinator
-     * @param autocompleteCoordinator
-     */
-    /* package */ void setCoordinators(@NonNull StatusCoordinator statusCoordinator,
-            @NonNull AutocompleteCoordinator autocompleteCoordinator) {
-        mStatusCoordinator = statusCoordinator;
-        mAutocompleteCoordinator = autocompleteCoordinator;
+    // Private methods
+
+    private void setProfile(Profile profile) {
+        if (profile == null || !mNativeInitialized) return;
+        mAutocompleteCoordinator.setAutocompleteProfile(profile);
+        mOmniboxPrerender.initializeForProfile(profile);
+
+        mLocationBarLayout.setShowIconsWhenUrlFocused(
+                SearchEngineLogoUtils.shouldShowSearchEngineLogo(profile.isOffTheRecord()));
     }
 
     /*package */ void updateVisualsForState() {
@@ -152,6 +187,9 @@
     @Override
     public void onUrlChanged() {
         mLocationBarLayout.setUrl(mLocationBarDataProvider.getCurrentUrl());
+        // Profile may be null if switching to a tab that has not yet been initialized.
+        Profile profile = mProfileSupplier.get();
+        if (profile != null && mOmniboxPrerender != null) mOmniboxPrerender.clear(profile);
     }
 
     @Override
@@ -210,10 +248,21 @@
 
     @Override
     public void onSuggestionsChanged(String autocompleteText, boolean defaultMatchIsSearch) {
-        mLocationBarLayout.onSuggestionsChanged(autocompleteText);
-        // TODO (https://crbug.com/1152501): Refactor the LBM/LBC relationship such that LBM doesn't
-        // need to communicate with other coordinators like this.
         mStatusCoordinator.onDefaultMatchClassified(defaultMatchIsSearch);
+        String userText = mUrlCoordinator.getTextWithoutAutocomplete();
+        if (mUrlCoordinator.shouldAutocomplete()) {
+            mUrlCoordinator.setAutocompleteText(userText, autocompleteText);
+        }
+
+        mLocationBarLayout.onSuggestionsChanged();
+        if (mNativeInitialized
+                && !CommandLine.getInstance().hasSwitch(ChromeSwitches.DISABLE_INSTANT)
+                && mPrivacyPreferencesManager.shouldPrerender()
+                && mLocationBarDataProvider.hasTab()) {
+            mOmniboxPrerender.prerenderMaybe(userText, mLocationBarLayout.getOriginalUrl(),
+                    mAutocompleteCoordinator.getCurrentNativeAutocompleteResult(),
+                    mProfileSupplier.get(), mLocationBarDataProvider.getTab());
+        }
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
index a251949..ff74044 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
@@ -16,7 +16,6 @@
 
 import androidx.annotation.NonNull;
 
-import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.WindowDelegate;
@@ -25,7 +24,6 @@
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinator;
 import org.chromium.chrome.browser.omnibox.voice.AssistantVoiceSearchService;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
-import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.toolbar.top.ToolbarTablet;
 import org.chromium.components.browser_ui.widget.animation.CancelAwareAnimatorListener;
@@ -120,14 +118,13 @@
     public void initialize(@NonNull AutocompleteCoordinator autocompleteCoordinator,
             @NonNull UrlBarCoordinator urlCoordinator, @NonNull StatusCoordinator statusCoordinator,
             @NonNull LocationBarDataProvider locationBarDataProvider,
-            @NonNull ObservableSupplier<Profile> profileSupplier,
             @NonNull WindowDelegate windowDelegate, @NonNull WindowAndroid windowAndroid,
             @NonNull OverrideUrlLoadingDelegate overrideUrlLoadingDelegate,
             @NonNull VoiceRecognitionHandler voiceRecognitionHandler,
             @NonNull OneshotSupplier<AssistantVoiceSearchService> assistantVoiceSearchSupplier) {
         super.initialize(autocompleteCoordinator, urlCoordinator, statusCoordinator,
-                locationBarDataProvider, profileSupplier, windowDelegate, windowAndroid,
-                overrideUrlLoadingDelegate, voiceRecognitionHandler, assistantVoiceSearchSupplier);
+                locationBarDataProvider, windowDelegate, windowAndroid, overrideUrlLoadingDelegate,
+                voiceRecognitionHandler, assistantVoiceSearchSupplier);
         mStatusCoordinator.setShowIconsWhenUrlFocused(true);
         if (SearchEngineLogoUtils.shouldShowSearchEngineLogo(
                     mLocationBarDataProvider.isIncognito())) {
@@ -244,8 +241,8 @@
     }
 
     @Override
-    void onSuggestionsChanged(String autocompleteText) {
-        super.onSuggestionsChanged(autocompleteText);
+    void onSuggestionsChanged() {
+        super.onSuggestionsChanged();
         mStatusCoordinator.setFirstSuggestionIsSearchType(
                 mAutocompleteCoordinator.getSuggestionCount() > 0
                 && mAutocompleteCoordinator.getSuggestionAt(0).isSearchSuggestion());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxPrerender.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxPrerender.java
index f553f337..9c0f2e3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxPrerender.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxPrerender.java
@@ -50,8 +50,8 @@
     }
 
     /**
-     * Potentailly invokes a pre-render or pre-connect given the url typed into the omnibox and
-     * a corresponding autocomplete result. This should be invoked everytime the omnibox changes
+     * Potentially invokes a pre-render or pre-connect given the url typed into the omnibox and
+     * a corresponding autocomplete result. This should be invoked every time the omnibox changes
      * (e.g. As the user types characters this method should be invoked at least once per character)
      *
      * @param url url in the omnibox.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManager.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManager.java
index 8f53b0c3..c79f2bb3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManager.java
@@ -12,6 +12,7 @@
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.os.Build;
 import android.os.Process;
 import android.os.SystemClock;
 import android.telephony.CellIdentityCdma;
@@ -29,7 +30,6 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.BuildInfo;
 import org.chromium.base.Callback;
 import org.chromium.base.compat.ApiHelperForQ;
 import org.chromium.base.task.PostTask;
@@ -337,7 +337,7 @@
     }
 
     private static boolean hasLocationPermission(Context context) {
-        if (BuildInfo.isAtLeastQ()) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
             return hasPermission(context, Manifest.permission.ACCESS_FINE_LOCATION);
         }
 
@@ -352,7 +352,7 @@
 
     private static void requestCellInfoUpdate(
             TelephonyManager telephonyManager, Callback<List<CellInfo>> callback) {
-        if (BuildInfo.isAtLeastQ()) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
             ApiHelperForQ.requestCellInfoUpdate(telephonyManager, callback);
             return;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestService.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestService.java
index 68fd6cf..3dacdd4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestService.java
@@ -10,7 +10,6 @@
 import androidx.collection.ArrayMap;
 
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.payments.handler.PaymentHandlerCoordinator;
@@ -252,7 +251,7 @@
         if (shouldSkipAppSelector) {
             mHasSkippedAppSelector = true;
         } else {
-            mPaymentUiService.getPaymentRequestUI().show(isShowWaitingForUpdatedDetails);
+            mPaymentUiService.showAppSelector(isShowWaitingForUpdatedDetails);
         }
         return null;
     }
@@ -270,7 +269,7 @@
             // are the only ones that can open the bottom-sheet.
             return;
         }
-        mPaymentUiService.getPaymentRequestUI().dimBackground();
+        mPaymentUiService.dimBackground();
     }
 
     // Implements BrowserPaymentRequest:
@@ -280,7 +279,7 @@
         // immediately after we determine the apps are ready and UI is shown.
         if (mHasSkippedAppSelector) {
             assert !mPaymentUiService.getPaymentApps().isEmpty();
-            assert mPaymentUiService.getPaymentRequestUI() != null;
+            assert mPaymentUiService.isPaymentRequestUiAlive();
 
             if (isMinimalUiApplicable(isUserGestureShow)) {
                 ChromeActivity chromeActivity = ChromeActivity.fromWebContents(mWebContents);
@@ -320,8 +319,7 @@
      * @return Whether the minimal UI should be shown.
      */
     private boolean isMinimalUiApplicable(boolean isUserGestureShow) {
-        if (!isUserGestureShow || mPaymentUiService.getPaymentApps().isEmpty()
-                || mPaymentUiService.getPaymentApps().size() != 1) {
+        if (!isUserGestureShow || mPaymentUiService.getPaymentApps().size() != 1) {
             return false;
         }
 
@@ -415,7 +413,7 @@
 
         // Do not create shipping section When UI is not built yet. This happens when the show
         // promise gets resolved before all apps are ready.
-        if (mPaymentUiService.getPaymentRequestUI() != null
+        if (mPaymentUiService.isPaymentRequestUiAlive()
                 && mPaymentUiService.shouldShowShippingSection()) {
             mPaymentUiService.createShippingSectionForPaymentRequestUI(chromeActivity);
         }
@@ -429,7 +427,7 @@
         if (mDidRecordShowEvent) {
             assert mSpec.getRawTotal() != null;
             mJourneyLogger.recordTransactionAmount(mSpec.getRawTotal().amount.currency,
-                    mSpec.getRawTotal().amount.value, false /*completed*/);
+                    mSpec.getRawTotal().amount.value, /*completed=*/false);
         }
 
         if (isFinishedQueryingPaymentApps && !mHasSkippedAppSelector) {
@@ -476,13 +474,8 @@
     // Implements BrowserPaymentRequest:
     @Override
     public void onInstrumentDetailsLoading() {
-        if (mPaymentUiService.getPaymentRequestUI() == null) {
-            return;
-        }
-
         assert mPaymentUiService.getSelectedPaymentAppType() == PaymentAppType.AUTOFILL;
-
-        mPaymentUiService.getPaymentRequestUI().showProcessingMessage();
+        mPaymentUiService.showProcessingMessage();
     }
 
     // Implements PaymentUiService.Delegate:
@@ -490,9 +483,6 @@
     public boolean invokePaymentApp(EditableOption selectedShippingAddress,
             EditableOption selectedShippingOption, PaymentApp selectedPaymentApp) {
         if (mPaymentRequestService == null || mSpec == null || mSpec.isDestroyed()) return false;
-        EditableOption selectedContact = mPaymentUiService.getContactSection() != null
-                ? mPaymentUiService.getContactSection().getSelectedItem()
-                : null;
         selectedPaymentApp.setPaymentHandlerHost(getPaymentHandlerHost());
         // Only native apps can use PaymentDetailsUpdateService.
         if (selectedPaymentApp.getPaymentAppType() == PaymentAppType.NATIVE_MOBILE_APP) {
@@ -500,9 +490,10 @@
                     ((AndroidPaymentApp) selectedPaymentApp).packageName(),
                     mPaymentRequestService /* PaymentApp.PaymentRequestUpdateEventListener */);
         }
-        PaymentResponseHelperInterface paymentResponseHelper = new ChromePaymentResponseHelper(
-                selectedShippingAddress, selectedShippingOption, selectedContact,
-                selectedPaymentApp, mSpec.getPaymentOptions(), mSkipToGPayHelper != null);
+        PaymentResponseHelperInterface paymentResponseHelper =
+                new ChromePaymentResponseHelper(selectedShippingAddress, selectedShippingOption,
+                        mPaymentUiService.getSelectedContact(), selectedPaymentApp,
+                        mSpec.getPaymentOptions(), mSkipToGPayHelper != null);
         mPaymentRequestService.invokePaymentApp(selectedPaymentApp, paymentResponseHelper);
         return !selectedPaymentApp.isAutofillInstrument();
     }
@@ -659,8 +650,8 @@
 
         // Showing the app selector UI if we were previously skipping it so the loading
         // spinner shows up until the merchant notifies that payment was completed.
-        if (mHasSkippedAppSelector && mPaymentUiService.getPaymentRequestUI() != null) {
-            mPaymentUiService.getPaymentRequestUI().showProcessingMessageAfterUiSkip();
+        if (mHasSkippedAppSelector) {
+            mPaymentUiService.showProcessingMessageAfterUiSkip();
         }
     }
 
@@ -673,10 +664,9 @@
     // Implements BrowserPaymentRequest:
     @Override
     public void onInstrumentDetailsError(String errorMessage) {
-        if (mPaymentUiService.getMinimalUI() != null) {
+        if (mPaymentUiService.isShowingMinimalUi()) {
             mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
-            mPaymentUiService.getMinimalUI().showErrorAndClose(
-                    /*observer=*/this::close, R.string.payments_error_message);
+            mPaymentUiService.closeMinimalUiOnError(this::close);
             return;
         }
 
@@ -686,7 +676,7 @@
             mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
             disconnectFromClientWithDebugMessage(errorMessage);
         } else {
-            mPaymentUiService.getPaymentRequestUI().onPayButtonProcessingCancelled();
+            mPaymentUiService.onPayButtonProcessingCancelled();
             PaymentDetailsUpdateServiceHelper.getInstance().reset();
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentResponseHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentResponseHelper.java
index 233c0791..431d177 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentResponseHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentResponseHelper.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.payments;
 
+import androidx.annotation.Nullable;
+
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.NormalizedAddressRequestDelegate;
@@ -22,6 +24,7 @@
  */
 public class ChromePaymentResponseHelper
         implements NormalizedAddressRequestDelegate, PaymentResponseHelperInterface {
+    @Nullable
     private final AutofillContact mSelectedContact;
     private final PaymentApp mSelectedPaymentApp;
     private final PaymentOptions mPaymentOptions;
@@ -37,13 +40,13 @@
      * Builds a helper to contruct and fill a PaymentResponse.
      * @param selectedShippingAddress The shipping address picked by the user.
      * @param selectedShippingOption  The shipping option picked by the user.
-     * @param selectedContact         The contact info picked by the user.
+     * @param selectedContact         The contact info picked by the user, can be null.
      * @param selectedPaymentApp      The payment app picked by the user.
      * @param paymentOptions          The paymentOptions of the corresponding payment request.
      * @param skipToGpay              Whether or not Gpay bridge is activated for skip to Gpay.
      */
     public ChromePaymentResponseHelper(EditableOption selectedShippingAddress,
-            EditableOption selectedShippingOption, EditableOption selectedContact,
+            EditableOption selectedShippingOption, @Nullable AutofillContact selectedContact,
             PaymentApp selectedPaymentApp, PaymentOptions paymentOptions, boolean skipToGpay) {
         mPaymentResponse = new PaymentResponse();
         mPaymentResponse.payer = new PayerDetail();
@@ -52,9 +55,7 @@
         mPaymentOptions = paymentOptions;
         mSkipToGpay = skipToGpay;
 
-        // Contacts are created in ChromePaymentRequestService.init(). These should all be instances
-        // of AutofillContact.
-        mSelectedContact = (AutofillContact) selectedContact;
+        mSelectedContact = selectedContact;
 
         // Set up the shipping option section of the response when it comes from payment sheet
         // (Shipping option comes from payment app when the app can handle shipping, or from Gpay
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
index 656c46b5..4c7c400 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
@@ -277,8 +277,8 @@
     /**
      * Length of the animation to either show the UI or expand it to full height.
      * Note that click of 'Pay' button is not accepted until the animation is done, so this duration
-     * also serves the function of preventing the user from accidently double-clicking on the screen
-     * when triggering payment and thus authorizing unwanted transaction.
+     * also serves the function of preventing the user from accidentally double-clicking on the
+     * screen when triggering payment and thus authorizing unwanted transaction.
      */
     private static final int DIALOG_ENTER_ANIMATION_MS = 225;
 
@@ -305,6 +305,7 @@
     private final ViewGroup mRequestView;
     private final Callback<PaymentInformation> mUpdateSectionsCallback;
     private final ShippingStrings mShippingStrings;
+    private final int mAnimatorTranslation;
 
     private FadingEdgeScrollView mPaymentContainer;
     private LinearLayout mPaymentContainerLayout;
@@ -338,7 +339,6 @@
 
     private Animator mSheetAnimator;
     private FocusAnimator mSectionAnimator;
-    private int mAnimatorTranslation;
 
     /**
      * Builds the UI for PaymentRequest.
@@ -652,7 +652,7 @@
             mRetryErrorView.setVisibility(View.GONE);
         } else {
             if (mIsExpandedToFullHeight) {
-                // Add paddings instead of margin to let getMeasuredHeight return correct value for
+                // Add padding instead of margin to let getMeasuredHeight return correct value for
                 // section resize animation.
                 int paddingSize = mContext.getResources().getDimensionPixelSize(
                         R.dimen.editor_dialog_section_large_spacing);
@@ -987,7 +987,7 @@
 
     /**
      * Called when the user has clicked on pay. The message is shown while the payment information
-     * is processed right until a confimation from the merchant is received.
+     * is processed right until a confirmation from the merchant is received.
      */
     public void showProcessingMessage() {
         assert mIsProcessingPayClicked;
@@ -1152,7 +1152,7 @@
         view.setMovementMethod(LinkMovementMethod.getInstance());
         ApiCompatibilityUtils.setTextAppearance(view, R.style.TextAppearance_TextMedium_Secondary);
 
-        // Add paddings instead of margin to let getMeasuredHeight return correct value for section
+        // Add padding instead of margin to let getMeasuredHeight return correct value for section
         // resize animation.
         int paddingSize = mContext.getResources().getDimensionPixelSize(
                 R.dimen.editor_dialog_section_large_spacing);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java
index ddb044c..c0eaaa7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java
@@ -321,9 +321,13 @@
         };
     }
 
-    /** @return The PaymentRequestUI. */
-    public PaymentRequestUI getPaymentRequestUI() {
-        return mPaymentRequestUI;
+    /**
+     * @return Whether the PaymentRequest UI is alive. The UI comes to live when
+     *         buildPaymentRequestUi() has been called to create it; it stops being alive when
+     *         close() is called to destroy it.
+     */
+    public boolean isPaymentRequestUiAlive() {
+        return mPaymentRequestUI != null;
     }
 
     /**
@@ -380,16 +384,6 @@
         updateAppModifiedTotals();
     }
 
-    /** Get the ShippingAddressesSection of the PaymentRequest UI. */
-    public SectionInformation getShippingAddressesSection() {
-        return mShippingAddressesSection;
-    }
-
-    /** Get the ContactSection of the PaymentRequest UI. */
-    public ContactDetailsSection getContactSection() {
-        return mContactSection;
-    }
-
     /** Set the AutofillPaymentAppCreator. */
     public void setAutofillPaymentAppCreator(AutofillPaymentAppCreator autofillPaymentAppCreator) {
         mAutofillPaymentAppCreator = autofillPaymentAppCreator;
@@ -414,12 +408,10 @@
         mHandler.post(callback.bind(mUiShoppingCart));
     }
 
-    /**
-     * The UI model for the shipping options. Includes the label and sublabel for each shipping
-     * option. Also keeps track of the selected shipping option. This data is passed to the UI.
-     */
-    public SectionInformation getUiShippingOptions() {
-        return mUiShippingOptions;
+    /** @return The selected contact, can be null. */
+    @Nullable
+    public AutofillContact getSelectedContact() {
+        return mContactSection != null ? (AutofillContact) mContactSection.getSelectedItem() : null;
     }
 
     /** Get the contact editor on PaymentRequest UI. */
@@ -437,9 +429,20 @@
         return mHaveRequestedAutofillData;
     }
 
-    /** @return The minimal UI coordinator. */
-    public MinimalUICoordinator getMinimalUI() {
-        return mMinimalUi;
+    /**
+     * Closes the minimal UI and shows an error message, called only when isShowingMinimalUi()
+     * returns true.
+     * @param onClosed Called when the minimal UI is closed.
+     */
+    public void closeMinimalUiOnError(Runnable onClosed) {
+        assert mMinimalUi != null;
+        mMinimalUi.showErrorAndClose(
+                /*observer=*/onClosed::run, R.string.payments_error_message);
+    }
+
+    /** @return Whether the minimal UI is showing. */
+    public boolean isShowingMinimalUi() {
+        return mMinimalUi != null;
     }
 
     // Implements PaymentUiServiceTestInterface:
@@ -1126,6 +1129,53 @@
     }
 
     /**
+     * Dims the background of the payment UIs. Precondition: isPaymentRequestUiAlive() needs to be
+     * true for the method to take effect.
+     */
+    public void dimBackground() {
+        if (mPaymentRequestUI == null) return;
+        mPaymentRequestUI.dimBackground();
+    }
+
+    /**
+     * Shows the app selector UI. Precondition: isPaymentRequestUiAlive() needs to be true for
+     * the method to take effect.
+     * @param isShowWaitingForUpdatedDetails
+     *        Whether showing payment app or the app selector is blocked on the updated payment
+     *        details.
+     */
+    public void showAppSelector(boolean isShowWaitingForUpdatedDetails) {
+        if (mPaymentRequestUI == null) return;
+        mPaymentRequestUI.show(isShowWaitingForUpdatedDetails);
+    }
+
+    /**
+     * Shows the processing message. Precondition: isPaymentRequestUiAlive() needs to be true for
+     * the method to take effect.
+     */
+    public void showProcessingMessage() {
+        if (mPaymentRequestUI == null) return;
+        mPaymentRequestUI.showProcessingMessage();
+    }
+
+    /**
+     *  Shows the processing message after payment details have been loaded in the case the
+     *  app selector UI has been skipped. Precondition: isPaymentRequestUiAlive() needs to be
+     *  true for the method to take effect.
+     */
+    public void showProcessingMessageAfterUiSkip() {
+        if (mPaymentRequestUI != null) mPaymentRequestUI.showProcessingMessageAfterUiSkip();
+    }
+
+    /**
+     * Called when user cancelled out of the UI that was shown after they clicked [PAY] button.
+     * Precondition: isPaymentRequestUiAlive() needs to be true for the method to take effect.
+     */
+    public void onPayButtonProcessingCancelled() {
+        if (mPaymentRequestUI != null) mPaymentRequestUI.onPayButtonProcessingCancelled();
+    }
+
+    /**
      * Build the PaymentRequest UI.
      * @param activity The ChromeActivity for the payment request, cannot be null.
      * @param isWebContentsActive Whether the merchant's WebContents is active.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
index 682f800..048a70b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.privacy.settings;
 
+import android.os.Build;
 import android.os.Bundle;
 import android.text.SpannableString;
 import android.view.Menu;
@@ -14,7 +15,6 @@
 import androidx.preference.PreferenceFragmentCompat;
 import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
 
-import org.chromium.base.BuildInfo;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherImpl;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -205,7 +205,8 @@
 
         Preference usageStatsPref = findPreference(PREF_USAGE_STATS);
         if (usageStatsPref != null) {
-            if (BuildInfo.isAtLeastQ() && prefService.getBoolean(Pref.USAGE_STATS_ENABLED)) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
+                    && prefService.getBoolean(Pref.USAGE_STATS_ENABLED)) {
                 usageStatsPref.setOnPreferenceClickListener(preference -> {
                     UsageStatsConsentDialog
                             .create(getActivity(), true,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
index 1edb1fb..70d7d1f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
@@ -9,12 +9,9 @@
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
-
-import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.WindowDelegate;
@@ -30,7 +27,6 @@
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinator;
 import org.chromium.chrome.browser.omnibox.voice.AssistantVoiceSearchService;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
-import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.toolbar.top.ToolbarPhone;
 import org.chromium.ui.base.WindowAndroid;
 
@@ -67,16 +63,14 @@
     public void initialize(@NonNull AutocompleteCoordinator autocompleteCoordinator,
             @NonNull UrlBarCoordinator urlCoordinator, @NonNull StatusCoordinator statusCoordinator,
             @NonNull LocationBarDataProvider locationBarDataProvider,
-            @NonNull ObservableSupplier<Profile> profileSupplier,
             @NonNull WindowDelegate windowDelegate, @NonNull WindowAndroid windowAndroid,
             @NonNull OverrideUrlLoadingDelegate overrideUrlLoadingDelegate,
             @NonNull VoiceRecognitionHandler voiceRecognitionHandler,
             @NonNull OneshotSupplier<AssistantVoiceSearchService>
                     assistantVoiceSearchServiceSupplier) {
         super.initialize(autocompleteCoordinator, urlCoordinator, statusCoordinator,
-                locationBarDataProvider, profileSupplier, windowDelegate, windowAndroid,
-                overrideUrlLoadingDelegate, voiceRecognitionHandler,
-                assistantVoiceSearchServiceSupplier);
+                locationBarDataProvider, windowDelegate, windowAndroid, overrideUrlLoadingDelegate,
+                voiceRecognitionHandler, assistantVoiceSearchServiceSupplier);
         setUrlBarFocusable(true);
         mPendingSearchPromoDecision = LocaleManager.getInstance().needToCheckForSearchEnginePromo();
         getAutocompleteCoordinator().setShouldPreventOmniboxAutocomplete(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java
index c69e6ea5..db90048 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java
@@ -15,7 +15,6 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import org.chromium.base.BuildInfo;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.IntentUtils;
 import org.chromium.base.annotations.CalledByNative;
@@ -128,7 +127,7 @@
      */
     private static boolean shouldOpenDialer() {
         // On Android Q and above, we never open the dialer directly.
-        if (BuildInfo.isAtLeastQ()) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
             return false;
         }
 
@@ -151,7 +150,7 @@
     private static boolean shouldShowNotification() {
         // Always show the notification for Android Q and above. For pre-Q, only show notification
         // if device is locked.
-        return BuildInfo.isAtLeastQ()
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
                 || !DeviceConditions.isCurrentlyScreenOnAndUnlocked(
                         ContextUtils.getApplicationContext());
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/AutofillSessionLifetimeController.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/AutofillSessionLifetimeController.java
index 1730aa2..b17080a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/AutofillSessionLifetimeController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/AutofillSessionLifetimeController.java
@@ -10,7 +10,7 @@
 
 import org.chromium.base.compat.ApiHelperForO;
 import org.chromium.chrome.browser.ActivityTabProvider;
-import org.chromium.chrome.browser.app.ChromeActivity;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.content_public.browser.NavigationHandle;
 
@@ -28,10 +28,12 @@
  * Autofill sessions are cancelled:
  * 1. when the domain part of the UrlBar content changes:
  *    In this case the session is cancelled by the Android Autofill service's compat mode.
- * 2. right before the tab is hidden:
+ * 2. right before the tab is hidden, unless Chrome itself is being stopped:
  *    Ensures that no save UI is shown when the user switches to a different tab or the tab
  *    switcher. By cancelling the session in onInteractabilityChanged, we catch both cases at a
- *    point where tab contents are not yet hidden.
+ *    point where tab contents are not yet hidden. Since some third-party Autofill services use
+ *    fullscreen authentication flows before they fill, the session must be preserved through
+ *    the main activity's lifecycle events.
  * 3. when browser-initiated navigation occurs:
  *    As opposed to renderer-initiated navigation (e.g., submitting a form), navigation initiated by
  *    browser controls should never trigger save UI. In order to cancel the session before web
@@ -44,7 +46,9 @@
 
     @TargetApi(Build.VERSION_CODES.O)
     public AutofillSessionLifetimeController(
-            ChromeActivity activity, ActivityTabProvider activityTabProvider) {
+            Activity activity,
+            ActivityLifecycleDispatcher lifecycleDispatcher,
+            ActivityTabProvider activityTabProvider) {
         mActivity = activity;
         mActivityTabObserver = new ActivityTabProvider.ActivityTabTabObserver(activityTabProvider) {
             @Override
@@ -56,12 +60,17 @@
 
             @Override
             public void onInteractabilityChanged(Tab tab, boolean isInteractable) {
-                if (!isInteractable) {
+                // While onInteractabilityChanged is called in ChromeActivity.onStop(), the session
+                // must remain active to allow Autofill services' fullscreen authentication flows to
+                // succeed.
+                boolean isStopped = lifecycleDispatcher.getCurrentActivityState() ==
+                        ActivityLifecycleDispatcher.ActivityState.STOPPED_WITH_NATIVE;
+                if (!isInteractable && !isStopped) {
                     ApiHelperForO.cancelAutofillSession(mActivity);
                 }
             }
         };
-        activity.getLifecycleDispatcher().register(this);
+        lifecycleDispatcher.register(this);
     }
 
     // Destroyable
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index 45cfe18..23bb653e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -229,7 +229,7 @@
         // require the compatibility mode introduced in Android P.
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
             mAutofillSessionLifetimeController = new AutofillSessionLifetimeController(
-                    activity, mActivityTabProvider);
+                    activity, activity.getLifecycleDispatcher(), mActivityTabProvider);
         } else {
             mAutofillSessionLifetimeController = null;
         }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/night_mode/settings/ThemeSettingsFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/night_mode/settings/ThemeSettingsFragmentTest.java
index 7b7c403..cea6397 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/night_mode/settings/ThemeSettingsFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/night_mode/settings/ThemeSettingsFragmentTest.java
@@ -8,6 +8,7 @@
 import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.UI_THEME_DARKEN_WEBSITES_ENABLED;
 import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.UI_THEME_SETTING;
 
+import android.os.Build;
 import android.view.View;
 import android.widget.LinearLayout;
 
@@ -18,7 +19,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.BuildInfo;
 import org.chromium.base.test.params.ParameterAnnotations;
 import org.chromium.base.test.params.ParameterSet;
 import org.chromium.base.test.params.ParameterizedRunner;
@@ -92,7 +92,7 @@
     public void testSelectThemes() {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             // Default to light parameter is only applicable pre-Q.
-            if (mDefaultToLight && BuildInfo.isAtLeastQ()) {
+            if (mDefaultToLight && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                 Assert.assertFalse("Q should not default to light.",
                         NightModeUtils.isNightModeDefaultToLight());
                 return;
@@ -137,7 +137,7 @@
     public void testDarkenWebsiteButton() {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             // Default to light parameter is only applicable pre-Q.
-            if (mDefaultToLight && BuildInfo.isAtLeastQ()) {
+            if (mDefaultToLight && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                 Assert.assertFalse("Q should not default to light.",
                         NightModeUtils.isNightModeDefaultToLight());
                 return;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFreeShippingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFreeShippingTest.java
index 319a8cd6..771095d9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFreeShippingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFreeShippingTest.java
@@ -53,8 +53,16 @@
     public PaymentRequestTestRule mPaymentRequestTestRule =
             new PaymentRequestTestRule("payment_request_free_shipping_test.html", this, true);
 
+    private static final int RENDER_TEST_REVISION = 1;
+    private static final String RENDER_TEST_REVISION_DESCRIPTION =
+            "Updated EditText hint color for a11y";
+
     @Rule
-    public RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus().build();
+    public RenderTestRule mRenderTestRule =
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(RENDER_TEST_REVISION)
+                    .setDescription(RENDER_TEST_REVISION_DESCRIPTION)
+                    .build();
 
     @BeforeClass
     public static void setUpBeforeActivityLaunched() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/base/SplitPreloaderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/base/SplitPreloaderTest.java
new file mode 100644
index 0000000..a3cc83c
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/base/SplitPreloaderTest.java
@@ -0,0 +1,177 @@
+// 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.base;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.os.Build;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link SplitPreloader}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = Build.VERSION_CODES.O)
+public class SplitPreloaderTest {
+    private static final String SPLIT_A = "split_a";
+    private static final String SPLIT_B = "split_b";
+
+    private static class SplitContext extends ContextWrapper {
+        private final String mName;
+        private final boolean mCreatedOnUiThread;
+
+        public SplitContext(Context context, String name) {
+            super(context);
+            mName = name;
+            mCreatedOnUiThread = ThreadUtils.runningOnUiThread();
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        public boolean wasCreatedOnUiThread() {
+            return mCreatedOnUiThread;
+        }
+    }
+
+    private static class MainContext extends ContextWrapper {
+        private final List<String> mUiThreadContextNames = new ArrayList<>();
+        private final List<String> mBackgroundThreadContextNames = new ArrayList<>();
+
+        public MainContext(Context context) {
+            super(context);
+        }
+
+        @Override
+        public Context createContextForSplit(String name)
+                throws PackageManager.NameNotFoundException {
+            if (ThreadUtils.runningOnUiThread()) {
+                mUiThreadContextNames.add(name);
+            } else {
+                mBackgroundThreadContextNames.add(name);
+            }
+            return new SplitContext(this, name);
+        }
+
+        public List<String> getUiThreadContextNames() {
+            return mUiThreadContextNames;
+        }
+
+        public List<String> getBackgroundThreadContextNames() {
+            return mBackgroundThreadContextNames;
+        }
+    }
+
+    private MainContext mContext;
+    private SplitPreloader mPreloader;
+
+    @Before
+    public void setUp() {
+        mContext = new MainContext(ContextUtils.getApplicationContext());
+        mPreloader = new SplitPreloader(mContext);
+    }
+
+    @Test
+    public void testPreload_splitInstalled() {
+        mContext.getApplicationInfo().splitNames = new String[] {SPLIT_A};
+
+        mPreloader.preload(SPLIT_A, null);
+        mPreloader.wait(SPLIT_A);
+
+        assertThat(mContext.getUiThreadContextNames()).isEmpty();
+        assertThat(mContext.getBackgroundThreadContextNames()).containsExactly(SPLIT_A);
+    }
+
+    @Test
+    public void testPreload_withOnComplete_splitInstalled() {
+        mContext.getApplicationInfo().splitNames = new String[] {SPLIT_A};
+
+        SplitContext[] contextHolder = new SplitContext[1];
+        mPreloader.preload(SPLIT_A, (Context context) -> contextHolder[0] = (SplitContext) context);
+        mPreloader.wait(SPLIT_A);
+
+        assertThat(mContext.getUiThreadContextNames()).containsExactly(SPLIT_A);
+        assertThat(mContext.getBackgroundThreadContextNames()).containsExactly(SPLIT_A);
+        assertTrue(contextHolder[0].wasCreatedOnUiThread());
+        assertEquals(contextHolder[0].getName(), SPLIT_A);
+    }
+
+    @Test
+    public void testPreload_multipleWaitCalls() {
+        mContext.getApplicationInfo().splitNames = new String[] {SPLIT_A};
+
+        SplitContext[] contextHolder = new SplitContext[1];
+        mPreloader.preload(SPLIT_A, (Context context) -> contextHolder[0] = (SplitContext) context);
+        mPreloader.wait(SPLIT_A);
+        mPreloader.wait(SPLIT_A);
+        mPreloader.wait(SPLIT_A);
+
+        assertThat(mContext.getUiThreadContextNames()).containsExactly(SPLIT_A);
+        assertThat(mContext.getBackgroundThreadContextNames()).containsExactly(SPLIT_A);
+        assertTrue(contextHolder[0].wasCreatedOnUiThread());
+        assertEquals(contextHolder[0].getName(), SPLIT_A);
+    }
+
+    @Test
+    public void testPreload_withOnComplete_multipleSplitsInstalled() {
+        mContext.getApplicationInfo().splitNames = new String[] {SPLIT_A, SPLIT_B};
+
+        SplitContext[] contextHolderA = new SplitContext[1];
+        mPreloader.preload(
+                SPLIT_A, (Context context) -> contextHolderA[0] = (SplitContext) context);
+
+        SplitContext[] contextHolderB = new SplitContext[1];
+        mPreloader.preload(
+                SPLIT_B, (Context context) -> contextHolderB[0] = (SplitContext) context);
+        mPreloader.wait(SPLIT_A);
+        mPreloader.wait(SPLIT_B);
+
+        assertThat(mContext.getUiThreadContextNames()).containsExactly(SPLIT_A, SPLIT_B);
+        assertThat(mContext.getBackgroundThreadContextNames()).containsExactly(SPLIT_A, SPLIT_B);
+        assertTrue(contextHolderA[0].wasCreatedOnUiThread());
+        assertEquals(contextHolderA[0].getName(), SPLIT_A);
+
+        assertTrue(contextHolderB[0].wasCreatedOnUiThread());
+        assertEquals(contextHolderB[0].getName(), SPLIT_B);
+    }
+
+    @Test
+    public void testPreload_splitNotInstalled() {
+        mPreloader.preload(SPLIT_A, null);
+        mPreloader.wait(SPLIT_A);
+
+        assertThat(mContext.getUiThreadContextNames()).isEmpty();
+        assertThat(mContext.getBackgroundThreadContextNames()).isEmpty();
+    }
+
+    @Test
+    public void testPreload_withOnComplete_splitNotInstalled() {
+        Context[] contextHolder = new Context[1];
+        mPreloader.preload(SPLIT_A, (Context context) -> contextHolder[0] = context);
+        mPreloader.wait(SPLIT_A);
+
+        assertThat(mContext.getUiThreadContextNames()).isEmpty();
+        assertThat(mContext.getBackgroundThreadContextNames()).isEmpty();
+        assertEquals(contextHolder[0], mContext);
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java
index 26bede4..6342a6756 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java
@@ -4,9 +4,13 @@
 
 package org.chromium.chrome.browser.omnibox;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
@@ -16,17 +20,26 @@
 import org.junit.Test;
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.robolectric.annotation.Config;
 
+import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.omnibox.UrlBarCoordinator.SelectionState;
+import org.chromium.chrome.browser.omnibox.status.StatusCoordinator;
+import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinator;
 import org.chromium.chrome.browser.omnibox.voice.AssistantVoiceSearchService;
+import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManagerImpl;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.profiles.ProfileJni;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.search_engines.TemplateUrlService;
@@ -40,6 +53,8 @@
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Rule
     public TestRule mProcessor = new Features.JUnitProcessor();
+    @Rule
+    public JniMocker mJniMocker = new JniMocker();
 
     @Mock
     LocationBarLayout mLocationBarLayout;
@@ -51,15 +66,36 @@
     private LocationBarDataProvider mLocationBarDataProvider;
     @Mock
     private OneshotSupplierImpl<AssistantVoiceSearchService> mAssistantVoiceSearchSupplier;
+    @Mock
+    private Profile.Natives mProfileNativesJniMock;
+    @Mock
+    private Tab mTab;
+    @Mock
+    private AutocompleteCoordinator mAutocompleteCoordinator;
+    @Mock
+    private UrlBarCoordinator mUrlCoordinator;
+    @Mock
+    private StatusCoordinator mStatusCoordintor;
+    @Mock
+    private PrivacyPreferencesManagerImpl mPrivacyPreferencesManager;
+    @Mock
+    private OmniboxPrerender.Natives mPrerenderJni;
+    @Mock
+    private SearchEngineLogoUtils.Delegate mSearchEngineDelegate;
 
+    private ObservableSupplierImpl<Profile> mProfileSupplier = new ObservableSupplierImpl<>();
     private LocationBarMediator mMediator;
 
     @Before
     public void setUp() {
         doReturn(mContext).when(mLocationBarLayout).getContext();
         TemplateUrlServiceFactory.setInstanceForTesting(mTemplateUrlService);
-        mMediator = new LocationBarMediator(
-                mLocationBarLayout, mLocationBarDataProvider, mAssistantVoiceSearchSupplier);
+        mJniMocker.mock(ProfileJni.TEST_HOOKS, mProfileNativesJniMock);
+        mJniMocker.mock(OmniboxPrerenderJni.TEST_HOOKS, mPrerenderJni);
+        mMediator = new LocationBarMediator(mLocationBarLayout, mLocationBarDataProvider,
+                mAssistantVoiceSearchSupplier, mProfileSupplier, mPrivacyPreferencesManager);
+        mMediator.setCoordinators(mUrlCoordinator, mAutocompleteCoordinator, mStatusCoordintor);
+        SearchEngineLogoUtils.setDelegateForTesting(mSearchEngineDelegate);
     }
 
     @Test
@@ -112,4 +148,36 @@
         mMediator.revertChanges();
         verify(mLocationBarLayout).setUrl("http://url.com");
     }
+
+    @Test
+    public void testOnSuggestionsChanged() {
+        ArgumentCaptor<OmniboxPrerender> omniboxPrerenderCaptor =
+                ArgumentCaptor.forClass(OmniboxPrerender.class);
+        doReturn(123L).when(mPrerenderJni).init(omniboxPrerenderCaptor.capture());
+        mMediator.onFinishNativeInitialization();
+        Profile profile = mock(Profile.class);
+        mProfileSupplier.set(profile);
+        verify(mPrerenderJni)
+                .initializeForProfile(123L, omniboxPrerenderCaptor.getValue(), profile);
+
+        doReturn(false).when(mPrivacyPreferencesManager).shouldPrerender();
+        mMediator.onSuggestionsChanged("text", true);
+        verify(mPrerenderJni, never())
+                .prerenderMaybe(
+                        anyLong(), any(), anyString(), anyString(), anyLong(), any(), any());
+
+        doReturn(true).when(mPrivacyPreferencesManager).shouldPrerender();
+        doReturn(true).when(mLocationBarDataProvider).hasTab();
+        doReturn(mTab).when(mLocationBarDataProvider).getTab();
+        doReturn("originalUrl").when(mLocationBarLayout).getOriginalUrl();
+        doReturn(456L).when(mAutocompleteCoordinator).getCurrentNativeAutocompleteResult();
+        doReturn("text").when(mUrlCoordinator).getTextWithoutAutocomplete();
+        doReturn(true).when(mUrlCoordinator).shouldAutocomplete();
+
+        mMediator.onSuggestionsChanged("textWithAutocomplete", true);
+        verify(mPrerenderJni)
+                .prerenderMaybe(123L, omniboxPrerenderCaptor.getValue(), "text", "originalUrl",
+                        456L, profile, mTab);
+        verify(mUrlCoordinator).setAutocompleteText("text", "textWithAutocomplete");
+    }
 }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 64d3794..693c7fb2 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5967,7 +5967,7 @@
         Reopen a tab if you accidentally closed it
       </message>
       <message name="IDS_DESKTOP_PWA_INSTALL_PROMO" desc="Text shown on promotional UI appearing next to the PWA install icon">
-        To get back to here quickly, install this app
+        To get back here quickly, install <ph name="APP_NAME">$1<ex>Google Maps</ex></ph>
       </message>
       <message name="IDS_REOPEN_TAB_PROMO_SCREENREADER" desc="Text announced encouraging users to reopen a tab with the given shortcut">
         <ph name="SHORTCUT">$1<ex>CTRL+SHIFT+T</ex></ph> can reopen accidentally closed tabs
diff --git a/chrome/app/generated_resources_grd/IDS_DESKTOP_PWA_INSTALL_PROMO.png.sha1 b/chrome/app/generated_resources_grd/IDS_DESKTOP_PWA_INSTALL_PROMO.png.sha1
index 5d6cdf3..f46e5869 100644
--- a/chrome/app/generated_resources_grd/IDS_DESKTOP_PWA_INSTALL_PROMO.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_DESKTOP_PWA_INSTALL_PROMO.png.sha1
@@ -1 +1 @@
-a1ef35f0f98b4502ebb0a0f27eac40e063eb2c7f
\ No newline at end of file
+34a9e96d0617141c5d1e542741566260cb823b75
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 95e9d88..cab3dcb9 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -2469,6 +2469,15 @@
   <message name="IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_CONNECTION_LOST_WITH_PHONE_SUMMARY" desc="The body text of the dialog containing the Phone Hub notification opt-in flow when the device loses its connection to the phone.">
     We were unable to maintain a connection with your phone. Make sure your phone is nearby, unlocked, and has Bluetooth and Wi-Fi turned on.
   </message>
+  <message name="IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_ACCESS_PROHIBITED_TITLE" desc="The title of the dialog containing the Phone Hub notification opt-in flow when access to notifications is prohibited because the user's phone has a work profile.">
+    Could not set up notifications syncing
+  </message>
+  <message name="IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_ACCESS_PROHIBITED_SUMMARY" desc="The title of the dialog containing the Phone Hub notification opt-in flow when access to notifications is prohibited because the user's phone has a work profile.">
+    Notification syncing is not supported for phones in a work profile. <ph name="LINK_BEGIN">&lt;a target="_blank" href="$1<ex>https://google.com/</ex>"&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
+  </message>
+  <message name="IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_PROHIBITED_TOOLTIP" desc="Tooltip shown to users when hovering the help icon in settings next to the Phone Hub notification syncing feature. The tooltip describes how users with work profiles cannot opt into this feature.">
+    Notification syncing is not supported for phones in a work profile
+  </message>
   <message name="IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_GET_STARTED_BUTTON_LABEL" desc="The label to a button in the Phone Hub notification opt-in flow that appears when the dialog first opens. Clicking the button will allow the user to try enabling the feature.">
     Get started
   </message>
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_PROHIBITED_TOOLTIP.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_PROHIBITED_TOOLTIP.png.sha1
new file mode 100644
index 0000000..b8f6270
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_PROHIBITED_TOOLTIP.png.sha1
@@ -0,0 +1 @@
+c999f0fb3f14985d9eef8b59bd8c1cb0e09f6282
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_ACCESS_PROHIBITED_SUMMARY.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_ACCESS_PROHIBITED_SUMMARY.png.sha1
new file mode 100644
index 0000000..8c54f07
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_ACCESS_PROHIBITED_SUMMARY.png.sha1
@@ -0,0 +1 @@
+2d53bf170afdbdbcc73d341c1e2fe774bfa30391
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_ACCESS_PROHIBITED_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_ACCESS_PROHIBITED_TITLE.png.sha1
new file mode 100644
index 0000000..8c54f07
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_ACCESS_PROHIBITED_TITLE.png.sha1
@@ -0,0 +1 @@
+2d53bf170afdbdbcc73d341c1e2fe774bfa30391
\ No newline at end of file
diff --git a/chrome/app/shared_settings_strings.grdp b/chrome/app/shared_settings_strings.grdp
index 96f35fe4..c91076b 100644
--- a/chrome/app/shared_settings_strings.grdp
+++ b/chrome/app/shared_settings_strings.grdp
@@ -96,6 +96,9 @@
   <message name="IDS_SETTINGS_NEARBY_SHARE_TITLE" desc="Name of the settings page for the Nearby Share feature">
     Nearby Share
   </message>
+  <message name="IDS_SETTINGS_NEARBY_SHARE_SET_UP_BUTTON_TITLE" desc="Title of the button that lets the user set up and enable the Nearby Share feature for the first time.">
+    Set up
+  </message>
   <message name="IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_ROW_TITLE" desc="Text for the row in settings which allows a user to set the name of their device which is shown to other devices for the Nearby Share feature.">
     Device name
   </message>
diff --git a/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_SET_UP_BUTTON_TITLE.png.sha1 b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_SET_UP_BUTTON_TITLE.png.sha1
new file mode 100644
index 0000000..841ee9b
--- /dev/null
+++ b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_SET_UP_BUTTON_TITLE.png.sha1
@@ -0,0 +1 @@
+40934fa308097a8225f37a26ff7b8bfb0efb2ac2
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 606bde2..d922c9c 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -6165,8 +6165,6 @@
 
   if (enable_supervised_users) {
     sources += [
-      "component_updater/supervised_user_whitelist_installer.cc",
-      "component_updater/supervised_user_whitelist_installer.h",
       "content_settings/content_settings_supervised_provider.cc",
       "content_settings/content_settings_supervised_provider.h",
       "supervised_user/child_accounts/child_account_service.cc",
@@ -6186,8 +6184,6 @@
       "supervised_user/kids_management_url_checker_client.cc",
       "supervised_user/kids_management_url_checker_client.h",
       "supervised_user/permission_request_creator.h",
-      "supervised_user/supervised_user_allowlist_service.cc",
-      "supervised_user/supervised_user_allowlist_service.h",
       "supervised_user/supervised_user_constants.cc",
       "supervised_user/supervised_user_constants.h",
       "supervised_user/supervised_user_denylist.cc",
diff --git a/chrome/browser/accessibility/soda_installer_impl.cc b/chrome/browser/accessibility/soda_installer_impl.cc
index e5ee23eb..232c0d1 100644
--- a/chrome/browser/accessibility/soda_installer_impl.cc
+++ b/chrome/browser/accessibility/soda_installer_impl.cc
@@ -108,13 +108,13 @@
       }
     } break;
     case Events::COMPONENT_UPDATED:
-    case Events::COMPONENT_NOT_UPDATED:
       NotifyOnSODAInstalled();
       break;
     case Events::COMPONENT_UPDATE_ERROR:
       NotifyOnSODAError();
       break;
     case Events::COMPONENT_CHECKING_FOR_UPDATES:
+    case Events::COMPONENT_NOT_UPDATED:
       // Do nothing.
       break;
   }
diff --git a/chrome/browser/android/metrics/uma_utils.cc b/chrome/browser/android/metrics/uma_utils.cc
index 3a09f03..783c8d4d 100644
--- a/chrome/browser/android/metrics/uma_utils.cc
+++ b/chrome/browser/android/metrics/uma_utils.cc
@@ -44,10 +44,5 @@
                           : metrics::EnableMetricsDefault::OPT_OUT);
 }
 
-void SetUsageAndCrashReporting(bool enabled) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_UmaUtils_setUsageAndCrashReportingFromNative(env, enabled);
-}
-
 }  // namespace android
 }  // namespace chrome
diff --git a/chrome/browser/android/metrics/uma_utils.h b/chrome/browser/android/metrics/uma_utils.h
index b1f63f9..c1c1522 100644
--- a/chrome/browser/android/metrics/uma_utils.h
+++ b/chrome/browser/android/metrics/uma_utils.h
@@ -15,10 +15,6 @@
 base::TimeTicks GetApplicationStartTime();
 base::TimeTicks GetProcessStartTime();
 
-// Sets whether UMA reporting is enabled. This will call to Java to update
-// the shared preference that is the source of truth for UMA reporting.
-void SetUsageAndCrashReporting(bool enabled);
-
 }  // namespace android
 }  // namespace chrome
 
diff --git a/chrome/browser/browser_process.h b/chrome/browser/browser_process.h
index ee9693c..4adbeee 100644
--- a/chrome/browser/browser_process.h
+++ b/chrome/browser/browser_process.h
@@ -67,7 +67,6 @@
 
 namespace component_updater {
 class ComponentUpdateService;
-class SupervisedUserWhitelistInstaller;
 }
 
 namespace extensions {
@@ -251,11 +250,6 @@
 
   virtual component_updater::ComponentUpdateService* component_updater() = 0;
 
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-  virtual component_updater::SupervisedUserWhitelistInstaller*
-  supervised_user_whitelist_installer() = 0;
-#endif
-
   virtual MediaFileSystemRegistry* media_file_system_registry() = 0;
 
   virtual WebRtcLogUploader* webrtc_log_uploader() = 0;
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 48698aad..2e76ca4 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -201,10 +201,6 @@
 #include "chrome/browser/error_reporting/chrome_js_error_report_processor.h"  // nogncheck
 #endif
 
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-#include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
-#endif
-
 #if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS)
 // How often to check if the persistent instance of Chrome needs to restart
 // to install an update.
@@ -408,12 +404,6 @@
   notification_ui_manager_.reset();
 #endif
 
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-  // The SupervisedUserWhitelistInstaller observes the ProfileAttributesStorage,
-  // so it needs to be shut down before the ProfileManager.
-  supervised_user_whitelist_installer_.reset();
-#endif
-
   // Debugger must be cleaned up before ProfileManager.
   remote_debugging_server_.reset();
   devtools_auto_opener_.reset();
@@ -1068,20 +1058,6 @@
   return component_updater_.get();
 }
 
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-component_updater::SupervisedUserWhitelistInstaller*
-BrowserProcessImpl::supervised_user_whitelist_installer() {
-  if (!supervised_user_whitelist_installer_) {
-    supervised_user_whitelist_installer_ =
-        component_updater::SupervisedUserWhitelistInstaller::Create(
-            component_updater(),
-            &profile_manager()->GetProfileAttributesStorage(),
-            local_state());
-  }
-  return supervised_user_whitelist_installer_.get();
-}
-#endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
-
 void BrowserProcessImpl::OnKeepAliveStateChanged(bool is_keeping_alive) {
   if (is_keeping_alive)
     Pin();
diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h
index e600e77..7cfbdefc 100644
--- a/chrome/browser/browser_process_impl.h
+++ b/chrome/browser/browser_process_impl.h
@@ -184,10 +184,6 @@
 #endif
 
   component_updater::ComponentUpdateService* component_updater() override;
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-  component_updater::SupervisedUserWhitelistInstaller*
-  supervised_user_whitelist_installer() override;
-#endif
   MediaFileSystemRegistry* media_file_system_registry() override;
   WebRtcLogUploader* webrtc_log_uploader() override;
   network_time::NetworkTimeTracker* network_time_tracker() override;
@@ -373,11 +369,6 @@
   // but some users of component updater only install per-user.
   std::unique_ptr<component_updater::ComponentUpdateService> component_updater_;
 
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-  std::unique_ptr<component_updater::SupervisedUserWhitelistInstaller>
-      supervised_user_whitelist_installer_;
-#endif
-
 #if BUILDFLAG(ENABLE_PLUGINS)
   std::unique_ptr<PluginsResourceService> plugins_resource_service_;
 #endif
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 2cf6e4a..d77607f 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -709,7 +709,7 @@
       handler_registry->ClearUserDefinedHandlers(delete_begin_, delete_end_);
 
     ChromeTranslateClient::CreateTranslatePrefs(prefs)
-        ->DeleteBlacklistedSitesBetween(delete_begin_, delete_end_);
+        ->DeleteNeverPromptSitesBetween(delete_begin_, delete_end_);
 
     host_content_settings_map_->ClearSettingsForOneTypeWithPredicate(
         ContentSettingsType::PERMISSION_AUTOREVOCATION_DATA, delete_begin,
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 4b3517d0..49f29f2 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
@@ -2388,23 +2388,23 @@
 TEST_F(ChromeBrowsingDataRemoverDelegateTest, RemoveTranslateBlocklist) {
   auto translate_prefs =
       ChromeTranslateClient::CreateTranslatePrefs(GetProfile()->GetPrefs());
-  translate_prefs->BlacklistSite("google.com");
+  translate_prefs->AddSiteToNeverPromptList("google.com");
   base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
   base::Time t = base::Time::Now();
-  translate_prefs->BlacklistSite("maps.google.com");
+  translate_prefs->AddSiteToNeverPromptList("maps.google.com");
 
-  EXPECT_TRUE(translate_prefs->IsSiteBlacklisted("google.com"));
-  EXPECT_TRUE(translate_prefs->IsSiteBlacklisted("maps.google.com"));
+  EXPECT_TRUE(translate_prefs->IsSiteOnNeverPromptList("google.com"));
+  EXPECT_TRUE(translate_prefs->IsSiteOnNeverPromptList("maps.google.com"));
 
   BlockUntilBrowsingDataRemoved(t, base::Time::Max(),
                                 constants::DATA_TYPE_CONTENT_SETTINGS, false);
-  EXPECT_TRUE(translate_prefs->IsSiteBlacklisted("google.com"));
-  EXPECT_FALSE(translate_prefs->IsSiteBlacklisted("maps.google.com"));
+  EXPECT_TRUE(translate_prefs->IsSiteOnNeverPromptList("google.com"));
+  EXPECT_FALSE(translate_prefs->IsSiteOnNeverPromptList("maps.google.com"));
 
   BlockUntilBrowsingDataRemoved(base::Time(), base::Time::Max(),
                                 constants::DATA_TYPE_CONTENT_SETTINGS, false);
-  EXPECT_FALSE(translate_prefs->IsSiteBlacklisted("google.com"));
-  EXPECT_FALSE(translate_prefs->IsSiteBlacklisted("maps.google.com"));
+  EXPECT_FALSE(translate_prefs->IsSiteOnNeverPromptList("google.com"));
+  EXPECT_FALSE(translate_prefs->IsSiteOnNeverPromptList("maps.google.com"));
 }
 
 TEST_F(ChromeBrowsingDataRemoverDelegateTest, RemoveDurablePermission) {
diff --git a/chrome/browser/browsing_data/counters/site_settings_counter.cc b/chrome/browser/browsing_data/counters/site_settings_counter.cc
index 9b3aac4..da52f8b9 100644
--- a/chrome/browser/browsing_data/counters/site_settings_counter.cc
+++ b/chrome/browser/browsing_data/counters/site_settings_counter.cc
@@ -104,7 +104,7 @@
 
   std::vector<std::string> blacklisted_sites =
       ChromeTranslateClient::CreateTranslatePrefs(pref_service_)
-          ->GetBlacklistedSitesBetween(period_start, period_end);
+          ->GetNeverPromptSitesBetween(period_start, period_end);
   for (const auto& site : blacklisted_sites)
     hosts.insert(site);
 
diff --git a/chrome/browser/browsing_data/counters/site_settings_counter_unittest.cc b/chrome/browser/browsing_data/counters/site_settings_counter_unittest.cc
index b291bb9..9501206 100644
--- a/chrome/browser/browsing_data/counters/site_settings_counter_unittest.cc
+++ b/chrome/browser/browsing_data/counters/site_settings_counter_unittest.cc
@@ -260,9 +260,9 @@
 
   auto translate_prefs =
       ChromeTranslateClient::CreateTranslatePrefs(profile()->GetPrefs());
-  translate_prefs->BlacklistSite("www.google.com");
-  translate_prefs->BlacklistSite("docs.google.com");
-  translate_prefs->BlacklistSite("photos.google.com");
+  translate_prefs->AddSiteToNeverPromptList("www.google.com");
+  translate_prefs->AddSiteToNeverPromptList("docs.google.com");
+  translate_prefs->AddSiteToNeverPromptList("photos.google.com");
   counter()->Restart();
   EXPECT_EQ(6, GetResult());
 }
@@ -288,8 +288,8 @@
 TEST_F(SiteSettingsCounterTest, TranslatedSitesCounting) {
   auto translate_prefs =
       ChromeTranslateClient::CreateTranslatePrefs(profile()->GetPrefs());
-  translate_prefs->BlacklistSite("www.google.com");
-  translate_prefs->BlacklistSite("maps.google.com");
+  translate_prefs->AddSiteToNeverPromptList("www.google.com");
+  translate_prefs->AddSiteToNeverPromptList("maps.google.com");
 
   SetDeletionPeriodPref(browsing_data::TimePeriod::ALL_TIME);
   EXPECT_EQ(2, GetResult());
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 1ddd395c..59e5175 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -159,6 +159,7 @@
 #include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/tab_contents/chrome_web_contents_view_delegate.h"
 #include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/webid/identity_dialog_controller.h"
 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
 #include "chrome/browser/ui/webui/log_web_ui_url.h"
 #include "chrome/browser/usb/frame_usb_services.h"
@@ -5933,6 +5934,11 @@
       error_page::Error::kHttpErrorDomain, http_status_code);
 }
 
+std::unique_ptr<content::IdentityRequestDialogController>
+ChromeContentBrowserClient::CreateIdentityRequestDialogController() {
+  return std::make_unique<IdentityDialogController>();
+}
+
 void ChromeContentBrowserClient::OnKeepaliveTimerFired(
     std::unique_ptr<ScopedKeepAlive> keep_alive_handle) {
 #if !defined(OS_ANDROID)
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 74c5d1f9..59479aa 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -720,6 +720,9 @@
 
   StartupData* startup_data() { return &startup_data_; }
 
+  std::unique_ptr<content::IdentityRequestDialogController>
+  CreateIdentityRequestDialogController() override;
+
  protected:
   static bool HandleWebUI(GURL* url, content::BrowserContext* browser_context);
   static bool HandleWebUIReverse(GURL* url,
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index ab828203c..b7ef1b8 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -2027,6 +2027,8 @@
     "net/network_diagnostics/tls_prober.h",
     "net/network_diagnostics/udp_prober.cc",
     "net/network_diagnostics/udp_prober.h",
+    "net/network_diagnostics/video_conferencing_routine.cc",
+    "net/network_diagnostics/video_conferencing_routine.h",
     "net/network_health/network_health.cc",
     "net/network_health/network_health.h",
     "net/network_health/network_health_localized_strings.cc",
@@ -3663,6 +3665,7 @@
     "net/network_diagnostics/signal_strength_routine_unittest.cc",
     "net/network_diagnostics/tls_prober_unittest.cc",
     "net/network_diagnostics/udp_prober_unittest.cc",
+    "net/network_diagnostics/video_conferencing_routine_unittest.cc",
     "net/network_health/network_health_unittest.cc",
     "net/network_portal_detector_impl_unittest.cc",
     "net/network_pref_state_observer_unittest.cc",
diff --git a/chrome/browser/chromeos/nearby/BUILD.gn b/chrome/browser/chromeos/nearby/BUILD.gn
index 9e6f030e..aedeba23 100644
--- a/chrome/browser/chromeos/nearby/BUILD.gn
+++ b/chrome/browser/chromeos/nearby/BUILD.gn
@@ -13,6 +13,7 @@
 
   deps = [
     "//base",
+    "//chromeos/services/nearby/public/cpp",
     "//device/bluetooth",
     "//device/bluetooth:deprecated_experimental_mojo",
     "//mojo/public/cpp/bindings",
diff --git a/chrome/browser/chromeos/nearby/OWNERS b/chrome/browser/chromeos/nearby/OWNERS
new file mode 100644
index 0000000..c860245
--- /dev/null
+++ b/chrome/browser/chromeos/nearby/OWNERS
@@ -0,0 +1 @@
+file://chromeos/services/nearby/OWNERS
diff --git a/chrome/browser/chromeos/nearby/bluetooth_adapter_util.cc b/chrome/browser/chromeos/nearby/bluetooth_adapter_util.cc
index 0ce9a81..5388f77 100644
--- a/chrome/browser/chromeos/nearby/bluetooth_adapter_util.cc
+++ b/chrome/browser/chromeos/nearby/bluetooth_adapter_util.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "chromeos/services/nearby/public/cpp/nearby_client_uuids.h"
 #include "device/bluetooth/adapter.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 
@@ -15,10 +16,18 @@
 
 void MakeSelfOwnedBluetoothAdapter(
     mojo::PendingReceiver<bluetooth::mojom::Adapter> pending_receiver,
-    scoped_refptr<device::BluetoothAdapter> adapter) {
-  mojo::MakeSelfOwnedReceiver(
-      std::make_unique<bluetooth::Adapter>(std::move(adapter)),
-      std::move(pending_receiver));
+    scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) {
+  auto adapter =
+      std::make_unique<bluetooth::Adapter>(std::move(bluetooth_adapter));
+
+  // Nearby Connections clients will be initiating and listening for connections
+  // from the untrusted Nearby utility process. Pre-emptively allowlist their
+  // UUIDs here in the browser process.
+  for (const auto& uuid : GetNearbyClientUuids()) {
+    adapter->AllowConnectionsForUuid(uuid);
+  }
+
+  mojo::MakeSelfOwnedReceiver(std::move(adapter), std::move(pending_receiver));
 }
 
 }  // namespace nearby
diff --git a/chrome/browser/chromeos/net/network_diagnostics/README.md b/chrome/browser/chromeos/net/network_diagnostics/README.md
index 69cd7510..ed2099d 100644
--- a/chrome/browser/chromeos/net/network_diagnostics/README.md
+++ b/chrome/browser/chromeos/net/network_diagnostics/README.md
@@ -168,6 +168,26 @@
 * `kHighLatency`: HTTPS request latency is high.
 * `kVeryHighLatency`: HTTPS request latency is very high.
 
+#### VideoConferencing
+
+Tests the device's video conferencing capabalities by testing whether the device
+can:
+1. Contact either a default or specified STUN server via UDP.
+2. Contact either a default or specified STUN server via TCP.
+3. Reach common media endpoints.
+
+Problems:
+* `kPotentialProblemUdpFailure`: Failed requests to a STUN server via UDP.
+* `kPotentialProblemTcpFailure`: Failed requests to a STUN server via TCP.
+* `kPotentialProblemMediaFailure`: Failed to establish a TLS connection to media hostnames.
+* `kPotentialProblemUdpAndMediaFailure`: Failed requests to a STUN server via
+UDP and failed to establish a TLS connection to media hostnames.
+* `kUdpAndTcpFailure`: Failed requests to a STUN server via UDP and TCP.
+* `kTcpAndMediaFailure`: Failed requests to a STUN server via TCP and failed to
+established a TLS connection to media hostnames.
+* `kUdpAndTcpAndMediaFailure`: Failed requests to a STUN server via UDP and TCP,
+and failed to establish a TLS connection to media hostnames.
+
 [Network Health and Configuration]: https://docs.google.com/document/d/10DSy-jZXaRo9I9aq1UqERy76t7HkgGvInWk57pHEkzg
 [network_diagnostics.mojom]: https://source.chromium.org/chromium/chromium/src/+/master:chromeos/services/network_health/public/mojom/network_diagnostics.mojom?originalUrl=https:%2F%2Fcs.chromium.org%2F
 [NetworkHealthService]: https://source.chromium.org/chromium/chromium/src/+/master:chrome/browser/chromeos/net/network_health/network_health_service.h?originalUrl=https:%2F%2Fcs.chromium.org%2F
diff --git a/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics.cc b/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics.cc
index 99c49fa1..abb553c 100644
--- a/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics.cc
+++ b/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/optional.h"
 #include "chrome/browser/chromeos/net/network_diagnostics/captive_portal_routine.h"
 #include "chrome/browser/chromeos/net/network_diagnostics/dns_latency_routine.h"
 #include "chrome/browser/chromeos/net/network_diagnostics/dns_resolution_routine.h"
@@ -19,6 +20,7 @@
 #include "chrome/browser/chromeos/net/network_diagnostics/https_latency_routine.h"
 #include "chrome/browser/chromeos/net/network_diagnostics/lan_connectivity_routine.h"
 #include "chrome/browser/chromeos/net/network_diagnostics/signal_strength_routine.h"
+#include "chrome/browser/chromeos/net/network_diagnostics/video_conferencing_routine.h"
 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
 #include "components/device_event_log/device_event_log.h"
 
@@ -209,5 +211,26 @@
       std::move(routine), std::move(callback)));
 }
 
+void NetworkDiagnostics::VideoConferencing(
+    const base::Optional<std::string>& stun_server_name,
+    VideoConferencingCallback callback) {
+  auto routine = std::make_unique<VideoConferencingRoutine>();
+  if (stun_server_name.has_value()) {
+    routine =
+        std::make_unique<VideoConferencingRoutine>(stun_server_name.value());
+  }
+  // RunRoutine() takes a lambda callback that takes ownership of the routine.
+  // This ensures that the routine stays alive when it makes asynchronous mojo
+  // calls. The routine will be destroyed when the lambda exits.
+  routine->RunRoutine(base::BindOnce(
+      [](std::unique_ptr<VideoConferencingRoutine> routine,
+         VideoConferencingCallback callback, mojom::RoutineVerdict verdict,
+         const std::vector<mojom::VideoConferencingProblem>& problems,
+         const base::Optional<std::string>& support_details) {
+        std::move(callback).Run(verdict, problems, support_details);
+      },
+      std::move(routine), std::move(callback)));
+}
+
 }  // namespace network_diagnostics
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics.h b/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics.h
index 04352ef..ce17a7d 100644
--- a/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics.h
+++ b/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics.h
@@ -5,7 +5,10 @@
 #ifndef CHROME_BROWSER_CHROMEOS_NET_NETWORK_DIAGNOSTICS_NETWORK_DIAGNOSTICS_H_
 #define CHROME_BROWSER_CHROMEOS_NET_NETWORK_DIAGNOSTICS_NETWORK_DIAGNOSTICS_H_
 
+#include <string>
+
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "chromeos/services/network_health/public/mojom/network_diagnostics.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
@@ -39,6 +42,8 @@
   void DnsResolution(DnsResolutionCallback callback) override;
   void CaptivePortal(CaptivePortalCallback callback) override;
   void HttpsLatency(HttpsLatencyCallback callback) override;
+  void VideoConferencing(const base::Optional<std::string>& stun_server_name,
+                         VideoConferencingCallback callback) override;
 
  private:
   // An unowned pointer to the DebugDaemonClient instance.
diff --git a/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_util.cc b/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_util.cc
index c138b64..118d6bb 100644
--- a/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_util.cc
+++ b/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_util.cc
@@ -182,6 +182,35 @@
   )");
 }
 
+std::vector<int> GetUdpPortsForGoogleStunServer() {
+  return {19302, 19303, 19304, 19305, 19306, 19307, 19308, 19309};
+}
+
+std::vector<int> GetUdpPortsForCustomStunServer() {
+  return {3478};
+}
+
+std::vector<int> GetTcpPortsForGoogleStunServer() {
+  return {19305, 19306, 19307, 19308};
+}
+
+std::vector<int> GetTcpPortsForCustomStunServer() {
+  return {3478};
+}
+
+std::vector<GURL> GetDefaultMediaUrls() {
+  const char* const kHostnames[] = {
+      "https://apis.google.com",           "https://talkgadget.google.com",
+      "https://clients6.google.com",       "https://hangouts.google.com",
+      "https://client-channel.google.com", "https://googleapis.com",
+      "https://accounts.google.com",       "https://clients4.google.com"};
+  std::vector<GURL> hostnames;
+  for (auto* const& hostname : kHostnames) {
+    hostnames.push_back(GURL(hostname));
+  }
+  return hostnames;
+}
+
 }  // namespace util
 
 }  // namespace network_diagnostics
diff --git a/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_util.h b/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_util.h
index 850c67a..d2419f77 100644
--- a/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_util.h
+++ b/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_util.h
@@ -90,6 +90,21 @@
 // Returns the traffic annotation tag for STUN traffic.
 net::NetworkTrafficAnnotationTag GetStunNetworkAnnotationTag();
 
+// Returns the ports used to speak to Google's STUN server over UDP.
+std::vector<int> GetUdpPortsForGoogleStunServer();
+
+// Returns the ports used to speak to a custom STUN server over UDP.
+std::vector<int> GetUdpPortsForCustomStunServer();
+
+// Returns the ports used to speak to Google's STUN server over TCP.
+std::vector<int> GetTcpPortsForGoogleStunServer();
+
+// Returns the ports used to speak to a custom STUN server over TCP.
+std::vector<int> GetTcpPortsForCustomStunServer();
+
+// Returns the list of urls related to Google media.
+std::vector<GURL> GetDefaultMediaUrls();
+
 }  // namespace util
 
 }  // namespace network_diagnostics
diff --git a/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_util_unittest.cc b/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_util_unittest.cc
index 47c1fb69..1c57e32 100644
--- a/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_util_unittest.cc
+++ b/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_util_unittest.cc
@@ -76,5 +76,11 @@
   }
 }
 
+TEST(NetworkDiagnosticsUtilTest, TestDefaultMediaHostnamesAreValidUrls) {
+  for (const GURL& url : util::GetDefaultMediaUrls()) {
+    EXPECT_TRUE(url.is_valid());
+  }
+}
+
 }  // namespace network_diagnostics
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/net/network_diagnostics/video_conferencing_routine.cc b/chrome/browser/chromeos/net/network_diagnostics/video_conferencing_routine.cc
new file mode 100644
index 0000000..145ef34
--- /dev/null
+++ b/chrome/browser/chromeos/net/network_diagnostics/video_conferencing_routine.cc
@@ -0,0 +1,215 @@
+// 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/chromeos/net/network_diagnostics/video_conferencing_routine.h"
+
+#include <string>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_util.h"
+#include "chrome/browser/chromeos/net/network_diagnostics/udp_prober.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "content/public/browser/storage_partition.h"
+#include "net/base/net_errors.h"
+#include "services/network/public/mojom/network_context.mojom.h"
+
+namespace chromeos {
+namespace network_diagnostics {
+
+namespace {
+
+const char kDefaultStunServer[] = "stun.l.google.com";
+
+}  // namespace
+
+const char kSupportDetails[] = "https://support.google.com/a/answer/1279090";
+const base::TimeDelta kTimeoutAfterHostResolution =
+    base::TimeDelta::FromSeconds(10);
+
+VideoConferencingRoutine::VideoConferencingRoutine()
+    : stun_server_hostname_(kDefaultStunServer),
+      udp_prober_getter_callback_(base::BindRepeating(
+          &VideoConferencingRoutine::CreateAndExecuteUdpProber)),
+      tls_prober_getter_callback_(base::BindRepeating(
+          &VideoConferencingRoutine::CreateAndExecuteTlsProber)),
+      udp_ports_(util::GetUdpPortsForGoogleStunServer()),
+      tcp_ports_(util::GetTcpPortsForGoogleStunServer()),
+      media_hostnames_(util::GetDefaultMediaUrls()) {}
+
+VideoConferencingRoutine::VideoConferencingRoutine(
+    const std::string& stun_server_hostname)
+    : stun_server_hostname_(stun_server_hostname),
+      udp_prober_getter_callback_(base::BindRepeating(
+          &VideoConferencingRoutine::CreateAndExecuteUdpProber)),
+      tls_prober_getter_callback_(base::BindRepeating(
+          &VideoConferencingRoutine::CreateAndExecuteTlsProber)),
+      udp_ports_(util::GetUdpPortsForCustomStunServer()),
+      tcp_ports_(util::GetTcpPortsForCustomStunServer()),
+      media_hostnames_(util::GetDefaultMediaUrls()) {}
+
+VideoConferencingRoutine::~VideoConferencingRoutine() = default;
+
+void VideoConferencingRoutine::AnalyzeResultsAndExecuteCallback() {
+  base::Optional<std::string> support_details = kSupportDetails;
+  set_verdict(mojom::RoutineVerdict::kProblem);
+  if (!open_udp_port_found_) {
+    problems_.push_back(mojom::VideoConferencingProblem::kUdpFailure);
+  }
+  if (!open_tcp_port_found_) {
+    problems_.push_back(mojom::VideoConferencingProblem::kTcpFailure);
+  }
+  if (!media_hostnames_reachable_) {
+    problems_.push_back(mojom::VideoConferencingProblem::kMediaFailure);
+  }
+  if (problems_.empty()) {
+    set_verdict(mojom::RoutineVerdict::kNoProblem);
+    support_details = base::nullopt;
+  }
+  std::move(routine_completed_callback_)
+      .Run(verdict(), std::move(problems_), support_details);
+}
+
+void VideoConferencingRoutine::RunRoutine(
+    VideoConferencingRoutineCallback callback) {
+  if (!CanRun()) {
+    std::move(callback).Run(verdict(), std::move(problems_), base::nullopt);
+    return;
+  }
+  routine_completed_callback_ = std::move(callback);
+  ProbeStunServerOverUdp();
+}
+
+void VideoConferencingRoutine::ProbeStunServerOverUdp() {
+  if (udp_ports_.empty()) {
+    ProbeStunServerOverTcp();
+    return;
+  }
+  int port = udp_ports_.back();
+  udp_ports_.pop_back();
+  AttemptUdpProbe(net::HostPortPair(stun_server_hostname_, port));
+}
+
+void VideoConferencingRoutine::ProbeStunServerOverTcp() {
+  if (tcp_ports_.empty()) {
+    ProbeMediaHostnames();
+    return;
+  }
+  int port = tcp_ports_.back();
+  tcp_ports_.pop_back();
+  AttemptTcpProbe(net::HostPortPair(stun_server_hostname_, port));
+}
+
+void VideoConferencingRoutine::ProbeMediaHostnames() {
+  if (media_hostnames_.empty()) {
+    AnalyzeResultsAndExecuteCallback();
+    return;
+  }
+  auto host_pair = net::HostPortPair::FromURL(media_hostnames_.back());
+  media_hostnames_.pop_back();
+  AttemptTlsProbe(host_pair);
+}
+
+network::mojom::NetworkContext* VideoConferencingRoutine::GetNetworkContext() {
+  Profile* profile = util::GetUserProfile();
+  DCHECK(profile);
+
+  return content::BrowserContext::GetDefaultStoragePartition(profile)
+      ->GetNetworkContext();
+}
+
+std::unique_ptr<UdpProber> VideoConferencingRoutine::CreateAndExecuteUdpProber(
+    UdpProber::NetworkContextGetter network_context_getter,
+    net::HostPortPair host_port_pair,
+    base::span<const uint8_t> data,
+    net::NetworkTrafficAnnotationTag tag,
+    base::TimeDelta timeout_after_host_resolution,
+    UdpProber::UdpProbeCompleteCallback callback) {
+  return UdpProber::Start(std::move(network_context_getter), host_port_pair,
+                          std::move(data), tag, timeout_after_host_resolution,
+                          std::move(callback));
+}
+
+std::unique_ptr<TlsProber> VideoConferencingRoutine::CreateAndExecuteTlsProber(
+    TlsProber::NetworkContextGetter network_context_getter,
+    net::HostPortPair host_port_pair,
+    bool negotiate_tls,
+    TlsProber::TlsProbeCompleteCallback callback) {
+  return std::make_unique<TlsProber>(std::move(network_context_getter),
+                                     host_port_pair, negotiate_tls,
+                                     std::move(callback));
+}
+
+void VideoConferencingRoutine::AttemptUdpProbe(
+    net::HostPortPair host_port_pair) {
+  // Store the instance of UdpProber.
+  udp_prober_ = udp_prober_getter_callback_.Run(
+      base::BindRepeating(&VideoConferencingRoutine::GetNetworkContext),
+      host_port_pair, util::GetStunHeader(),
+      util::GetStunNetworkAnnotationTag(), kTimeoutAfterHostResolution,
+      base::BindOnce(&VideoConferencingRoutine::OnUdpProbeComplete,
+                     weak_ptr()));
+}
+
+void VideoConferencingRoutine::AttemptTcpProbe(
+    net::HostPortPair host_port_pair) {
+  tls_prober_ = tls_prober_getter_callback_.Run(
+      base::BindRepeating(&VideoConferencingRoutine::GetNetworkContext),
+      host_port_pair,
+      /*negotiate_tls=*/false,
+      base::BindOnce(&VideoConferencingRoutine::OnTcpProbeComplete,
+                     weak_ptr()));
+}
+
+void VideoConferencingRoutine::AttemptTlsProbe(
+    net::HostPortPair host_port_pair) {
+  // Store the instance of TlsProber.
+  tls_prober_ = tls_prober_getter_callback_.Run(
+      base::BindRepeating(&VideoConferencingRoutine::GetNetworkContext),
+      host_port_pair,
+      /*negotiate_tls=*/true,
+      base::BindOnce(&VideoConferencingRoutine::OnTlsProbeComplete,
+                     weak_ptr()));
+}
+
+void VideoConferencingRoutine::OnUdpProbeComplete(
+    int result,
+    UdpProber::ProbeExitEnum probe_exit_enum) {
+  if (result == net::OK) {
+    open_udp_port_found_ = true;
+    // Only one open UDP port needs to be detected.
+    ProbeStunServerOverTcp();
+    return;
+  }
+  ProbeStunServerOverUdp();
+}
+
+void VideoConferencingRoutine::OnTcpProbeComplete(
+    int result,
+    TlsProber::ProbeExitEnum probe_exit_enum) {
+  if (result == net::OK) {
+    open_tcp_port_found_ = true;
+    // Only one open TCP port needs to be detected.
+    ProbeMediaHostnames();
+    return;
+  }
+  ProbeStunServerOverTcp();
+}
+
+void VideoConferencingRoutine::OnTlsProbeComplete(
+    int result,
+    TlsProber::ProbeExitEnum probe_exit_enum) {
+  if (result != net::OK) {
+    media_hostnames_reachable_ = false;
+    // All media hostnames must be reachable.
+    AnalyzeResultsAndExecuteCallback();
+    return;
+  }
+  ProbeMediaHostnames();
+}
+
+}  // namespace network_diagnostics
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/net/network_diagnostics/video_conferencing_routine.h b/chrome/browser/chromeos/net/network_diagnostics/video_conferencing_routine.h
new file mode 100644
index 0000000..68e64bf
--- /dev/null
+++ b/chrome/browser/chromeos/net/network_diagnostics/video_conferencing_routine.h
@@ -0,0 +1,151 @@
+// 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_NET_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_ROUTINE_H_
+#define CHROME_BROWSER_CHROMEOS_NET_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_ROUTINE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/containers/span.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_routine.h"
+#include "chrome/browser/chromeos/net/network_diagnostics/tls_prober.h"
+#include "chrome/browser/chromeos/net/network_diagnostics/udp_prober.h"
+#include "net/base/net_errors.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "url/gurl.h"
+
+namespace chromeos {
+namespace network_diagnostics {
+
+extern const char kSupportDetails[];
+extern const base::TimeDelta kTimeoutAfterHostResolution;
+
+// Tests the device's video conferencing capabilities by testing the connection
+// to a sample of Google servers used in various GVC offerings. See the
+// README.md file for more details.
+class VideoConferencingRoutine : public NetworkDiagnosticsRoutine {
+ public:
+  using VideoConferencingRoutineCallback =
+      mojom::NetworkDiagnosticsRoutines::VideoConferencingCallback;
+  using UdpProberGetterCallback =
+      base::RepeatingCallback<std::unique_ptr<UdpProber>(
+          UdpProber::NetworkContextGetter network_context_getter,
+          net::HostPortPair host_port_pair,
+          base::span<const uint8_t> data,
+          net::NetworkTrafficAnnotationTag tag,
+          base::TimeDelta timeout_after_host_resolution,
+          UdpProber::UdpProbeCompleteCallback callback)>;
+  using TlsProberGetterCallback =
+      base::RepeatingCallback<std::unique_ptr<TlsProber>(
+          TlsProber::NetworkContextGetter network_context_getter,
+          net::HostPortPair host_port_pair,
+          bool negotiate_tls,
+          TlsProber::TlsProbeCompleteCallback callback)>;
+
+  // Creates a routine using a default STUN server.
+  VideoConferencingRoutine();
+  // Creates a routine using a custom STUN server.
+  explicit VideoConferencingRoutine(const std::string& stun_server_hostname);
+  VideoConferencingRoutine(const VideoConferencingRoutine&) = delete;
+  VideoConferencingRoutine& operator=(const VideoConferencingRoutine&) = delete;
+  ~VideoConferencingRoutine() override;
+
+  // NetworkDiagnosticsRoutine:
+  void AnalyzeResultsAndExecuteCallback() override;
+
+  // Run the core logic of this routine. Set |callback| to
+  // |routine_completed_callback_|, which is to be executed in
+  // AnalyzeResultsAndExecuteCallback().
+  void RunRoutine(VideoConferencingRoutineCallback callback);
+
+  void set_udp_prober_getter_callback_for_testing(
+      UdpProberGetterCallback udp_prober_getter_callback) {
+    udp_prober_getter_callback_ = std::move(udp_prober_getter_callback);
+  }
+
+  void set_tls_prober_getter_callback_for_testing(
+      TlsProberGetterCallback tls_prober_getter_callback) {
+    tls_prober_getter_callback_ = std::move(tls_prober_getter_callback);
+  }
+
+ private:
+  // Probes the STUN server over UDP to determine whether an open port
+  // connection exists.
+  void ProbeStunServerOverUdp();
+
+  // Probes the STUN server over TCP to determine whether an open port
+  // connection exists.
+  void ProbeStunServerOverTcp();
+
+  // Probes media endpoints over TCP with TLS.
+  void ProbeMediaHostnames();
+
+  // Returns the network context.
+  static network::mojom::NetworkContext* GetNetworkContext();
+
+  // Creates and instance of UdpProber.
+  static std::unique_ptr<UdpProber> CreateAndExecuteUdpProber(
+      UdpProber::NetworkContextGetter network_context_getter,
+      net::HostPortPair host_port_pair,
+      base::span<const uint8_t> data,
+      net::NetworkTrafficAnnotationTag tag,
+      base::TimeDelta timeout_after_host_resolution,
+      UdpProber::UdpProbeCompleteCallback callback);
+
+  // Creates an instance of TlsProber.
+  static std::unique_ptr<TlsProber> CreateAndExecuteTlsProber(
+      TlsProber::NetworkContextGetter network_context_getter,
+      net::HostPortPair host_port_pair,
+      bool negotiate_tls,
+      TlsProber::TlsProbeCompleteCallback callback);
+
+  // Launches a UDP probe.
+  void AttemptUdpProbe(net::HostPortPair host_port_pair);
+
+  // Launches a TCP probe.
+  void AttemptTcpProbe(net::HostPortPair host_port_pair);
+
+  // Launches a TLS probe.
+  void AttemptTlsProbe(net::HostPortPair host_port_pair);
+
+  // Handles UDP probe completion.
+  void OnUdpProbeComplete(int result, UdpProber::ProbeExitEnum probe_exit_enum);
+
+  // Handles TCP probe completion.
+  void OnTcpProbeComplete(int result, TlsProber::ProbeExitEnum probe_exit_enum);
+
+  // Handles TLS probe completion.
+  void OnTlsProbeComplete(int result, TlsProber::ProbeExitEnum probe_exit_enum);
+
+  // Returns the weak pointer to |this|.
+  base::WeakPtr<VideoConferencingRoutine> weak_ptr() {
+    return weak_factory_.GetWeakPtr();
+  }
+
+  std::vector<mojom::VideoConferencingProblem> problems_;
+  VideoConferencingRoutineCallback routine_completed_callback_;
+  std::string stun_server_hostname_;
+  bool open_udp_port_found_ = false;
+  bool open_tcp_port_found_ = false;
+  bool media_hostnames_reachable_ = true;
+  UdpProberGetterCallback udp_prober_getter_callback_;
+  TlsProberGetterCallback tls_prober_getter_callback_;
+  std::unique_ptr<UdpProber> udp_prober_;
+  std::unique_ptr<TlsProber> tls_prober_;
+  std::vector<int> udp_ports_;
+  std::vector<int> tcp_ports_;
+  std::vector<GURL> media_hostnames_;
+
+  base::WeakPtrFactory<VideoConferencingRoutine> weak_factory_{this};
+};
+
+}  // namespace network_diagnostics
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_NET_NETWORK_DIAGNOSTICS_VIDEO_CONFERENCING_ROUTINE_H_
diff --git a/chrome/browser/chromeos/net/network_diagnostics/video_conferencing_routine_unittest.cc b/chrome/browser/chromeos/net/network_diagnostics/video_conferencing_routine_unittest.cc
new file mode 100644
index 0000000..bc09d5e
--- /dev/null
+++ b/chrome/browser/chromeos/net/network_diagnostics/video_conferencing_routine_unittest.cc
@@ -0,0 +1,542 @@
+// 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/chromeos/net/network_diagnostics/video_conferencing_routine.h"
+
+#include <deque>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/optional.h"
+#include "base/run_loop.h"
+#include "base/time/time.h"
+#include "chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_util.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+namespace network_diagnostics {
+
+namespace {
+
+// Test implementation of UdpProber.
+class TestUdpProber final : public UdpProber {
+ public:
+  TestUdpProber(UdpProber::UdpProbeCompleteCallback callback,
+                int result,
+                UdpProber::ProbeExitEnum probe_exit_enum) {
+    // Post an asynchronus task simulating a completed probe. This mimics the
+    // behavior of the production UdpProber constructor since the TestUdpProber
+    // instance will be complete before FinishProbe is invoked. In the
+    // production UdpProber, the constructor completes before DNS host
+    // resolution is complete.
+    content::GetUIThreadTaskRunner({})->PostTask(
+        FROM_HERE,
+        base::BindOnce(&TestUdpProber::FinishProbe, weak_factory_.GetWeakPtr(),
+                       std::move(callback), result, probe_exit_enum));
+  }
+
+  TestUdpProber(const TestUdpProber&) = delete;
+  TestUdpProber& operator=(const TestUdpProber&) = delete;
+  ~TestUdpProber() override = default;
+
+ private:
+  void FinishProbe(UdpProber::UdpProbeCompleteCallback callback,
+                   int result,
+                   UdpProber::ProbeExitEnum probe_exit_enum) {
+    std::move(callback).Run(result, probe_exit_enum);
+  }
+
+  base::WeakPtrFactory<TestUdpProber> weak_factory_{this};
+};
+
+// Test implementation of TlsProber.
+class TestTlsProber final : public TlsProber {
+ public:
+  TestTlsProber(TlsProber::TlsProbeCompleteCallback callback,
+                int result,
+                TlsProber::ProbeExitEnum probe_exit_enum) {
+    // Post an asynchronus task simulating a completed probe. This mimics the
+    // behavior of the production TlsProber constructor since the TestTlsProber
+    // instance will be complete before FinishProbe is invoked. In the
+    // production TlsProber, the constructor completes before DNS host
+    // resolution is complete.
+    content::GetUIThreadTaskRunner({})->PostTask(
+        FROM_HERE,
+        base::BindOnce(&TestTlsProber::FinishProbe, weak_factory_.GetWeakPtr(),
+                       std::move(callback), result, probe_exit_enum));
+  }
+
+  TestTlsProber(const TestTlsProber&) = delete;
+  TestTlsProber& operator=(const TestTlsProber&) = delete;
+  ~TestTlsProber() override = default;
+
+ private:
+  void FinishProbe(TlsProber::TlsProbeCompleteCallback callback,
+                   int result,
+                   TlsProber::ProbeExitEnum probe_exit_enum) {
+    std::move(callback).Run(result, probe_exit_enum);
+  }
+
+  base::WeakPtrFactory<TestTlsProber> weak_factory_{this};
+};
+
+}  // namespace
+
+class VideoConferencingRoutineTest : public ::testing::Test {
+ public:
+  struct UdpProberReturnValue {
+    net::Error result;
+    UdpProber::ProbeExitEnum probe_exit_enum;
+  };
+  struct TlsProberReturnValue {
+    net::Error result;
+    TlsProber::ProbeExitEnum probe_exit_enum;
+  };
+
+  VideoConferencingRoutineTest() = default;
+  VideoConferencingRoutineTest(const VideoConferencingRoutineTest&) = delete;
+  VideoConferencingRoutineTest& operator=(const VideoConferencingRoutineTest&) =
+      delete;
+  ~VideoConferencingRoutineTest() override = default;
+
+  void CompareVerdict(
+      mojom::RoutineVerdict expected_verdict,
+      const std::vector<mojom::VideoConferencingProblem>& expected_problems,
+      const base::Optional<std::string>& expected_support_details,
+      mojom::RoutineVerdict actual_verdict,
+      const std::vector<mojom::VideoConferencingProblem>& actual_problems,
+      const base::Optional<std::string>& actual_support_details) {
+    EXPECT_EQ(expected_verdict, actual_verdict);
+    EXPECT_EQ(expected_problems, actual_problems);
+    EXPECT_EQ(expected_support_details.has_value(),
+              actual_support_details.has_value());
+    if (expected_support_details.has_value() &&
+        actual_support_details.has_value()) {
+      EXPECT_EQ(expected_support_details.has_value(),
+                actual_support_details.has_value());
+    }
+    run_loop_.Quit();
+  }
+
+  std::unique_ptr<UdpProber> CreateAndExecuteUdpProber(
+      UdpProber::NetworkContextGetter network_context_getter,
+      net::HostPortPair host_port_pair,
+      base::span<const uint8_t> data,
+      net::NetworkTrafficAnnotationTag tag,
+      base::TimeDelta timeout_after_host_resolution,
+      UdpProber::UdpProbeCompleteCallback callback) {
+    DCHECK(fake_udp_probe_results_.size() > 0);
+
+    auto value = fake_udp_probe_results_.front();
+    fake_udp_probe_results_.pop_front();
+    auto test_udp_prober = std::make_unique<TestUdpProber>(
+        std::move(callback), value.result, value.probe_exit_enum);
+    return std::move(test_udp_prober);
+  }
+
+  std::unique_ptr<TlsProber> CreateAndExecuteTlsProber(
+      TlsProber::NetworkContextGetter network_context_getter,
+      net::HostPortPair host_port_pair,
+      bool negotiate_tls,
+      TlsProber::TlsProbeCompleteCallback callback) {
+    DCHECK(fake_tls_probe_results_.size() > 0);
+
+    auto value = fake_tls_probe_results_.front();
+    fake_tls_probe_results_.pop_front();
+    auto test_tls_prober = std::make_unique<TestTlsProber>(
+        std::move(callback), value.result, value.probe_exit_enum);
+    return std::move(test_tls_prober);
+  }
+
+ protected:
+  void RunRoutine(
+      mojom::RoutineVerdict expected_routine_verdict,
+      const std::vector<mojom::VideoConferencingProblem>& expected_problems,
+      const base::Optional<std::string>& expected_support_details) {
+    video_conferencing_routine_->RunRoutine(base::BindOnce(
+        &VideoConferencingRoutineTest::CompareVerdict, weak_ptr(),
+        expected_routine_verdict, expected_problems, expected_support_details));
+    run_loop_.Run();
+  }
+
+  void SetUpRoutine(std::deque<UdpProberReturnValue> fake_udp_probe_results,
+                    std::deque<TlsProberReturnValue> fake_tls_probe_results) {
+    fake_udp_probe_results_ = std::move(fake_udp_probe_results);
+    fake_tls_probe_results_ = std::move(fake_tls_probe_results);
+    video_conferencing_routine_ = std::make_unique<VideoConferencingRoutine>();
+    video_conferencing_routine_->set_udp_prober_getter_callback_for_testing(
+        base::BindRepeating(
+            &VideoConferencingRoutineTest::CreateAndExecuteUdpProber,
+            base::Unretained(this)));
+    video_conferencing_routine_->set_tls_prober_getter_callback_for_testing(
+        base::BindRepeating(
+            &VideoConferencingRoutineTest::CreateAndExecuteTlsProber,
+            base::Unretained(this)));
+  }
+
+  // Sets up required properties (via fakes) and runs the test.
+  //
+  // Parameters:
+  // |fake_udp_probe_results|: Represents the results of UDP probes.
+  // |fake_tls_probe_results|: Represents the results of TCP and TLS probes.
+  // |expected_routine_verdict|: Represents the expected verdict
+  // reported by this test.
+  // |expected_problems|: Represents the expected problem
+  // reported by this test.
+  void SetUpAndRunRoutine(
+      std::deque<UdpProberReturnValue> fake_udp_probe_results,
+      std::deque<TlsProberReturnValue> fake_tls_probe_results,
+      mojom::RoutineVerdict expected_routine_verdict,
+      const std::vector<mojom::VideoConferencingProblem>& expected_problems,
+      const base::Optional<std::string>& expected_support_details) {
+    SetUpRoutine(std::move(fake_udp_probe_results),
+                 std::move(fake_tls_probe_results));
+    RunRoutine(expected_routine_verdict, expected_problems,
+               expected_support_details);
+  }
+
+  base::WeakPtr<VideoConferencingRoutineTest> weak_ptr() {
+    return weak_factory_.GetWeakPtr();
+  }
+
+ private:
+  content::BrowserTaskEnvironment task_environment_;
+  base::RunLoop run_loop_;
+  std::deque<UdpProberReturnValue> fake_udp_probe_results_;
+  std::deque<TlsProberReturnValue> fake_tls_probe_results_;
+  std::unique_ptr<VideoConferencingRoutine> video_conferencing_routine_;
+  base::WeakPtrFactory<VideoConferencingRoutineTest> weak_factory_{this};
+};
+
+// Tests the scenario where:
+// (1) An open port to the STUN server is found via UDP and TCP.
+// (2) All media hostnames are reachable.
+TEST_F(VideoConferencingRoutineTest, TestSuccessfulPath) {
+  // Results corresponding to UDP requests to the STUN server.
+  int num_udp_ports_tested = util::GetUdpPortsForGoogleStunServer().size();
+  std::deque<UdpProberReturnValue> fake_udp_probe_results;
+  for (int i = 0; i < num_udp_ports_tested; i++) {
+    if (i < num_udp_ports_tested - 1) {
+      fake_udp_probe_results.push_back(UdpProberReturnValue{
+          net::ERR_FAILED, UdpProber::ProbeExitEnum::kNoDataReceivedFailure});
+      continue;
+    }
+    fake_udp_probe_results.push_back(
+        UdpProberReturnValue{net::OK, UdpProber::ProbeExitEnum::kSuccess});
+  }
+
+  // Tracks the results corresponding to TCP requests to the STUN server and
+  // TLS requests to media hostnames.
+  std::deque<TlsProberReturnValue> fake_tls_probe_results;
+
+  // Results corresponding to the STUN server via TCP.
+  int num_tcp_ports_tested = util::GetTcpPortsForGoogleStunServer().size();
+  for (int i = 0; i < num_tcp_ports_tested; i++) {
+    if (i < num_tcp_ports_tested - 1) {
+      fake_tls_probe_results.push_back(TlsProberReturnValue{
+          net::ERR_FAILED, TlsProber::ProbeExitEnum::kTcpConnectionFailure});
+      continue;
+    }
+    fake_tls_probe_results.push_back(
+        TlsProberReturnValue{net::OK, TlsProber::ProbeExitEnum::kSuccess});
+  }
+
+  // Results corresponding to the TLS requests to media hostnames.
+  int num_media_hostnames_tested = util::GetDefaultMediaUrls().size();
+  for (int i = 0; i < num_media_hostnames_tested; i++) {
+    fake_tls_probe_results.push_back(
+        TlsProberReturnValue{net::OK, TlsProber::ProbeExitEnum::kSuccess});
+  }
+
+  SetUpAndRunRoutine(
+      std::move(fake_udp_probe_results), std::move(fake_tls_probe_results),
+      mojom::RoutineVerdict::kNoProblem,
+      /*expected_problems=*/{}, /*expected_support_problems=*/base::nullopt);
+}
+
+// Tests the scenario where:
+// (1) No open port to the STUN server is found via UDP.
+TEST_F(VideoConferencingRoutineTest, TestUdpFailure) {
+  // Results corresponding to UDP requests to the STUN server.
+  int num_udp_ports_tested = util::GetUdpPortsForGoogleStunServer().size();
+  std::deque<UdpProberReturnValue> fake_udp_probe_results;
+  for (int i = 0; i < num_udp_ports_tested; i++) {
+    fake_udp_probe_results.push_back(UdpProberReturnValue{
+        net::ERR_FAILED, UdpProber::ProbeExitEnum::kNoDataReceivedFailure});
+  }
+
+  // Tracks the results corresponding to TCP requests to the STUN server and
+  // TLS requests to media hostnames.
+  std::deque<TlsProberReturnValue> fake_tls_probe_results;
+
+  // Results corresponding to the STUN server via TCP.
+  int num_tcp_ports_tested = util::GetTcpPortsForGoogleStunServer().size();
+  for (int i = 0; i < num_tcp_ports_tested; i++) {
+    if (i < num_tcp_ports_tested - 1) {
+      fake_tls_probe_results.push_back(TlsProberReturnValue{
+          net::ERR_FAILED, TlsProber::ProbeExitEnum::kTcpConnectionFailure});
+      continue;
+    }
+    fake_tls_probe_results.push_back(
+        TlsProberReturnValue{net::OK, TlsProber::ProbeExitEnum::kSuccess});
+  }
+
+  // Results corresponding to the TLS requests to media hostnames.
+  int num_media_hostnames_tested = util::GetDefaultMediaUrls().size();
+  for (int i = 0; i < num_media_hostnames_tested; i++) {
+    fake_tls_probe_results.push_back(
+        TlsProberReturnValue{net::OK, TlsProber::ProbeExitEnum::kSuccess});
+  }
+
+  SetUpAndRunRoutine(
+      std::move(fake_udp_probe_results), std::move(fake_tls_probe_results),
+      mojom::RoutineVerdict::kProblem,
+      {mojom::VideoConferencingProblem::kUdpFailure}, kSupportDetails);
+}
+
+// Tests the scenario where:
+// (1) No open port to the STUN server is found via TCP.
+TEST_F(VideoConferencingRoutineTest, TestTcpFailure) {
+  // Results corresponding to UDP requests to the STUN server.
+  int num_udp_ports_tested = util::GetUdpPortsForGoogleStunServer().size();
+  std::deque<UdpProberReturnValue> fake_udp_probe_results;
+  for (int i = 0; i < num_udp_ports_tested; i++) {
+    if (i < num_udp_ports_tested - 1) {
+      fake_udp_probe_results.push_back(UdpProberReturnValue{
+          net::ERR_FAILED, UdpProber::ProbeExitEnum::kNoDataReceivedFailure});
+      continue;
+    }
+    fake_udp_probe_results.push_back(
+        UdpProberReturnValue{net::OK, UdpProber::ProbeExitEnum::kSuccess});
+  }
+
+  // Tracks the results corresponding to TCP requests to the STUN server and
+  // TLS requests to media hostnames.
+  std::deque<TlsProberReturnValue> fake_tls_probe_results;
+
+  // Results corresponding to the STUN server via TCP.
+  int num_tcp_ports_tested = util::GetTcpPortsForGoogleStunServer().size();
+  for (int i = 0; i < num_tcp_ports_tested; i++) {
+    fake_tls_probe_results.push_back(TlsProberReturnValue{
+        net::ERR_FAILED, TlsProber::ProbeExitEnum::kTcpConnectionFailure});
+  }
+
+  // Results corresponding to the TLS requests to media hostnames.
+  int num_media_hostnames_tested = util::GetDefaultMediaUrls().size();
+  for (int i = 0; i < num_media_hostnames_tested; i++) {
+    fake_tls_probe_results.push_back(
+        TlsProberReturnValue{net::OK, TlsProber::ProbeExitEnum::kSuccess});
+  }
+
+  SetUpAndRunRoutine(
+      std::move(fake_udp_probe_results), std::move(fake_tls_probe_results),
+      mojom::RoutineVerdict::kProblem,
+      {mojom::VideoConferencingProblem::kTcpFailure}, kSupportDetails);
+}
+
+// Tests the scenario where:
+// (1) Requests to one or more media hostnames failed.
+TEST_F(VideoConferencingRoutineTest, TestMediaFailure) {
+  // Results corresponding to UDP requests to the STUN server.
+  int num_udp_ports_tested = util::GetUdpPortsForGoogleStunServer().size();
+  std::deque<UdpProberReturnValue> fake_udp_probe_results;
+  for (int i = 0; i < num_udp_ports_tested; i++) {
+    if (i < num_udp_ports_tested - 1) {
+      fake_udp_probe_results.push_back(UdpProberReturnValue{
+          net::ERR_FAILED, UdpProber::ProbeExitEnum::kNoDataReceivedFailure});
+      continue;
+    }
+    fake_udp_probe_results.push_back(
+        UdpProberReturnValue{net::OK, UdpProber::ProbeExitEnum::kSuccess});
+  }
+
+  // Tracks the results corresponding to TCP requests to the STUN server and
+  // TLS requests to media hostnames.
+  std::deque<TlsProberReturnValue> fake_tls_probe_results;
+
+  // Results corresponding to the STUN server via TCP.
+  int num_tcp_ports_tested = util::GetTcpPortsForGoogleStunServer().size();
+  for (int i = 0; i < num_tcp_ports_tested; i++) {
+    if (i < num_tcp_ports_tested - 1) {
+      fake_tls_probe_results.push_back(TlsProberReturnValue{
+          net::ERR_FAILED, TlsProber::ProbeExitEnum::kTcpConnectionFailure});
+      continue;
+    }
+    fake_tls_probe_results.push_back(
+        TlsProberReturnValue{net::OK, TlsProber::ProbeExitEnum::kSuccess});
+  }
+
+  // Results corresponding to the TLS requests to media hostnames.
+  fake_tls_probe_results.push_back(TlsProberReturnValue{
+      net::ERR_FAILED, TlsProber::ProbeExitEnum::kTlsUpgradeFailure});
+
+  SetUpAndRunRoutine(
+      std::move(fake_udp_probe_results), std::move(fake_tls_probe_results),
+      mojom::RoutineVerdict::kProblem,
+      {mojom::VideoConferencingProblem::kMediaFailure}, kSupportDetails);
+}
+
+// Tests the scenario where:
+// (1) No open port to the STUN server is found via UDP.
+// (2) No open port to the STUN server is found via TCP.
+TEST_F(VideoConferencingRoutineTest, TestUdpAndTcpFailure) {
+  // Results corresponding to UDP requests to the STUN server.
+  int num_udp_ports_tested = util::GetUdpPortsForGoogleStunServer().size();
+  std::deque<UdpProberReturnValue> fake_udp_probe_results;
+  for (int i = 0; i < num_udp_ports_tested; i++) {
+    fake_udp_probe_results.push_back(UdpProberReturnValue{
+        net::ERR_FAILED, UdpProber::ProbeExitEnum::kNoDataReceivedFailure});
+  }
+
+  // Tracks the results corresponding to TCP requests to the STUN server and
+  // TLS requests to media hostnames.
+  std::deque<TlsProberReturnValue> fake_tls_probe_results;
+
+  // Results corresponding to the STUN server via TCP.
+  int num_tcp_ports_tested = util::GetTcpPortsForGoogleStunServer().size();
+  for (int i = 0; i < num_tcp_ports_tested; i++) {
+    fake_tls_probe_results.push_back(TlsProberReturnValue{
+        net::ERR_FAILED, TlsProber::ProbeExitEnum::kTcpConnectionFailure});
+  }
+
+  // Results corresponding to the TLS requests to media hostnames.
+  int num_media_hostnames_tested = util::GetDefaultMediaUrls().size();
+  for (int i = 0; i < num_media_hostnames_tested; i++) {
+    fake_tls_probe_results.push_back(
+        TlsProberReturnValue{net::OK, TlsProber::ProbeExitEnum::kSuccess});
+  }
+
+  SetUpAndRunRoutine(std::move(fake_udp_probe_results),
+                     std::move(fake_tls_probe_results),
+                     mojom::RoutineVerdict::kProblem,
+                     {mojom::VideoConferencingProblem::kUdpFailure,
+                      mojom::VideoConferencingProblem::kTcpFailure},
+                     kSupportDetails);
+}
+
+// Tests the scenario where:
+// (1) No open port to the STUN server is found via UDP.
+// (2) Requests to one or more media hostnames failed.
+TEST_F(VideoConferencingRoutineTest, TestUdpAndMediaFailure) {
+  // Results corresponding to UDP requests to the STUN server.
+  int num_udp_ports_tested = util::GetUdpPortsForGoogleStunServer().size();
+  std::deque<UdpProberReturnValue> fake_udp_probe_results;
+  for (int i = 0; i < num_udp_ports_tested; i++) {
+    fake_udp_probe_results.push_back(UdpProberReturnValue{
+        net::ERR_FAILED, UdpProber::ProbeExitEnum::kNoDataReceivedFailure});
+  }
+
+  // Tracks the results corresponding to TCP requests to the STUN server and
+  // TLS requests to media hostnames.
+  std::deque<TlsProberReturnValue> fake_tls_probe_results;
+
+  // Results corresponding to the STUN server via TCP.
+  int num_tcp_ports_tested = util::GetTcpPortsForGoogleStunServer().size();
+  for (int i = 0; i < num_tcp_ports_tested; i++) {
+    if (i < num_tcp_ports_tested - 1) {
+      fake_tls_probe_results.push_back(TlsProberReturnValue{
+          net::ERR_FAILED, TlsProber::ProbeExitEnum::kTcpConnectionFailure});
+      continue;
+    }
+    fake_tls_probe_results.push_back(
+        TlsProberReturnValue{net::OK, TlsProber::ProbeExitEnum::kSuccess});
+  }
+
+  // Results corresponding to the TLS requests to media hostnames.
+  fake_tls_probe_results.push_back(TlsProberReturnValue{
+      net::ERR_FAILED, TlsProber::ProbeExitEnum::kTlsUpgradeFailure});
+
+  SetUpAndRunRoutine(std::move(fake_udp_probe_results),
+                     std::move(fake_tls_probe_results),
+                     mojom::RoutineVerdict::kProblem,
+                     {mojom::VideoConferencingProblem::kUdpFailure,
+                      mojom::VideoConferencingProblem::kMediaFailure},
+                     kSupportDetails);
+}
+
+// Tests the scenario where:
+// (1) No open port to the STUN server is found via TCP.
+// (2) Requests to one or more media hostnames failed.
+TEST_F(VideoConferencingRoutineTest, TestTcpAndMediaFailure) {
+  // Results corresponding to UDP requests to the STUN server.
+  int num_udp_ports_tested = util::GetUdpPortsForGoogleStunServer().size();
+  std::deque<UdpProberReturnValue> fake_udp_probe_results;
+  for (int i = 0; i < num_udp_ports_tested; i++) {
+    if (i < num_udp_ports_tested - 1) {
+      fake_udp_probe_results.push_back(UdpProberReturnValue{
+          net::ERR_FAILED, UdpProber::ProbeExitEnum::kNoDataReceivedFailure});
+      continue;
+    }
+    fake_udp_probe_results.push_back(
+        UdpProberReturnValue{net::OK, UdpProber::ProbeExitEnum::kSuccess});
+  }
+
+  // Tracks the results corresponding to TCP requests to the STUN server and
+  // TLS requests to media hostnames.
+  std::deque<TlsProberReturnValue> fake_tls_probe_results;
+
+  // Results corresponding to the STUN server via TCP.
+  int num_tcp_ports_tested = util::GetTcpPortsForGoogleStunServer().size();
+  for (int i = 0; i < num_tcp_ports_tested; i++) {
+    fake_tls_probe_results.push_back(TlsProberReturnValue{
+        net::ERR_FAILED, TlsProber::ProbeExitEnum::kTcpConnectionFailure});
+  }
+
+  // Results corresponding to the TLS requests to media hostnames.
+  fake_tls_probe_results.push_back(TlsProberReturnValue{
+      net::ERR_FAILED, TlsProber::ProbeExitEnum::kTlsUpgradeFailure});
+
+  SetUpAndRunRoutine(std::move(fake_udp_probe_results),
+                     std::move(fake_tls_probe_results),
+                     mojom::RoutineVerdict::kProblem,
+                     {mojom::VideoConferencingProblem::kTcpFailure,
+                      mojom::VideoConferencingProblem::kMediaFailure},
+                     kSupportDetails);
+}
+
+// Tests the scenario where:
+// (1) No open port to the STUN server is found via UDP.
+// (2) No open port to the STUN server is found via TCP.
+// (3) Requests to one or more media hostnames failed.
+TEST_F(VideoConferencingRoutineTest, TestTcpAndUdpAndMediaFailure) {
+  // Results corresponding to UDP requests to the STUN server.
+  int num_udp_ports_tested = util::GetUdpPortsForGoogleStunServer().size();
+  std::deque<UdpProberReturnValue> fake_udp_probe_results;
+  for (int i = 0; i < num_udp_ports_tested; i++) {
+    fake_udp_probe_results.push_back(UdpProberReturnValue{
+        net::ERR_FAILED, UdpProber::ProbeExitEnum::kNoDataReceivedFailure});
+  }
+
+  // Tracks the results corresponding to TCP requests to the STUN server and
+  // TLS requests to media hostnames.
+  std::deque<TlsProberReturnValue> fake_tls_probe_results;
+
+  // Results corresponding to the STUN server via TCP.
+  int num_tcp_ports_tested = util::GetTcpPortsForGoogleStunServer().size();
+  for (int i = 0; i < num_tcp_ports_tested; i++) {
+    fake_tls_probe_results.push_back(TlsProberReturnValue{
+        net::ERR_FAILED, TlsProber::ProbeExitEnum::kTcpConnectionFailure});
+  }
+
+  // Results corresponding to the TLS requests to media hostnames.
+  fake_tls_probe_results.push_back(TlsProberReturnValue{
+      net::ERR_FAILED, TlsProber::ProbeExitEnum::kTlsUpgradeFailure});
+
+  SetUpAndRunRoutine(std::move(fake_udp_probe_results),
+                     std::move(fake_tls_probe_results),
+                     mojom::RoutineVerdict::kProblem,
+                     {mojom::VideoConferencingProblem::kUdpFailure,
+                      mojom::VideoConferencingProblem::kTcpFailure,
+                      mojom::VideoConferencingProblem::kMediaFailure},
+                     kSupportDetails);
+}
+
+}  // namespace network_diagnostics
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
index 89cf7bd..081d12fb 100644
--- a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
@@ -71,7 +71,8 @@
                 POLICY_SOURCE_CLOUD, std::move(value_to_set),
                 std::move(external_data_fetcher));
   if (!error.empty())
-    policies->AddError(policy_name, error);
+    policies->AddError(policy_name, IDS_POLICY_PROTO_PARSING_ERROR,
+                       {base::UTF8ToUTF16(error)});
 }
 
 // Returns true and sets |level| to a PolicyLevel if the policy has been set
@@ -156,9 +157,8 @@
 void AddDeprecationWarning(const std::string& old_name,
                            const std::string& new_name,
                            PolicyMap* policies) {
-  policies->AddError(old_name,
-                     l10n_util::GetStringFUTF8(IDS_POLICY_MIGRATED_OLD_POLICY,
-                                               base::UTF8ToUTF16(new_name)));
+  policies->AddError(old_name, IDS_POLICY_MIGRATED_OLD_POLICY,
+                     {base::UTF8ToUTF16(new_name)});
 }
 
 void DecodeLoginPolicies(const em::ChromeDeviceSettingsProto& policy,
diff --git a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos_unittest.cc b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos_unittest.cc
index 83c715d7..8f2f09e 100644
--- a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos_unittest.cc
@@ -5,9 +5,11 @@
 #include "chrome/browser/chromeos/policy/device_policy_decoder_chromeos.h"
 
 #include "base/bind.h"
+#include "base/strings/utf_string_conversions.h"
 #include "components/policy/core/common/policy_bundle.h"
 #include "components/policy/policy_constants.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
+#include "components/strings/grit/components_strings.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -73,9 +75,13 @@
   std::string error;
   base::Optional<base::Value> decoded_json = DecodeJsonStringAndNormalize(
       kInvalidJson, key::kDeviceWallpaperImage, &error);
+  std::string localized_error = l10n_util::GetStringFUTF8(
+      IDS_POLICY_PROTO_PARSING_ERROR, base::UTF8ToUTF16(error));
   EXPECT_FALSE(decoded_json.has_value());
-  EXPECT_NE(std::string::npos,
-            error.find("Invalid JSON string: Line: 1, column: 14"));
+  EXPECT_EQ(
+      "Policy parsing error: Invalid JSON string: Line: 1, column: 14, Syntax "
+      "error.",
+      localized_error);
 }
 
 #if GTEST_HAS_DEATH_TEST
@@ -94,10 +100,12 @@
   base::Optional<base::Value> decoded_json = DecodeJsonStringAndNormalize(
       kWallpaperJsonInvalidValue, key::kDeviceWallpaperImage, &error);
   EXPECT_FALSE(decoded_json.has_value());
+  std::string localized_error = l10n_util::GetStringFUTF8(
+      IDS_POLICY_PROTO_PARSING_ERROR, base::UTF8ToUTF16(error));
   EXPECT_EQ(
-      "Invalid policy value: The value type doesn't match the schema type. (at "
-      "url)",
-      error);
+      "Policy parsing error: Invalid policy value: The value type doesn't "
+      "match the schema type. (at url)",
+      localized_error);
 }
 
 TEST_F(DevicePolicyDecoderChromeOSTest,
@@ -105,11 +113,13 @@
   std::string error;
   base::Optional<base::Value> decoded_json = DecodeJsonStringAndNormalize(
       kWallpaperJsonUnknownProperty, key::kDeviceWallpaperImage, &error);
+  std::string localized_error = l10n_util::GetStringFUTF8(
+      IDS_POLICY_PROTO_PARSING_ERROR, base::UTF8ToUTF16(error));
   EXPECT_EQ(*GetWallpaperDict(), decoded_json.value());
   EXPECT_EQ(
-      "Dropped unknown properties: Unknown property: unknown-field (at "
-      "toplevel)",
-      error);
+      "Policy parsing error: Dropped unknown properties: Unknown property: "
+      "unknown-field (at toplevel)",
+      localized_error);
 }
 
 TEST_F(DevicePolicyDecoderChromeOSTest, DecodeJsonStringAndNormalizeSuccess) {
diff --git a/chrome/browser/chromeos/policy/install_event_log_manager.cc b/chrome/browser/chromeos/policy/install_event_log_manager.cc
index 39165ff..8ace414 100644
--- a/chrome/browser/chromeos/policy/install_event_log_manager.cc
+++ b/chrome/browser/chromeos/policy/install_event_log_manager.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/check_op.h"
+#include "base/command_line.h"
 #include "base/location.h"
 #include "base/sequenced_task_runner.h"
 #include "base/task/post_task.h"
@@ -16,6 +17,7 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chromeos/constants/chromeos_switches.h"
 
 namespace policy {
 
@@ -24,6 +26,8 @@
 // Delay after which a change to the log contents is stored to disk. Further
 // changes during this time window are picked up by the same store operation.
 constexpr base::TimeDelta kStoreDelay = base::TimeDelta::FromSeconds(5);
+// Reduce store delay for integration tests.
+constexpr base::TimeDelta kFastStoreDelay = base::TimeDelta::FromSeconds(1);
 
 // Interval between subsequent uploads to the server, if the log is not empty.
 constexpr base::TimeDelta kUploadInterval = base::TimeDelta::FromHours(3);
@@ -33,12 +37,22 @@
 // getting full.
 constexpr base::TimeDelta kExpeditedUploadDelay =
     base::TimeDelta::FromMinutes(15);
+// Reduce upload delay for integration tests.
+constexpr base::TimeDelta kFastUploadDelay = base::TimeDelta::FromSeconds(30);
 
 // An expedited upload is scheduled whenever the total number of log entries
 // exceeds |kTotalSizeExpeditedUploadThreshold| or the number of log entries for
 // any single app exceeds |kMaxSizeExpeditedUploadThreshold|.
 constexpr int kTotalSizeExpeditedUploadThreshold = 2048;
 constexpr int kMaxSizeExpeditedUploadThreshold = 512;
+
+// If install-log-fast-upload-for-tests flag is enabled, upload logs with
+// reduced delay.
+bool FastUploadForTestsEnabled() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      chromeos::switches::kInstallLogFastUploadForTests);
+}
+
 }  // namespace
 
 InstallEventLogManagerBase::LogTaskRunnerWrapper::LogTaskRunnerWrapper() =
@@ -91,7 +105,7 @@
         FROM_HERE,
         base::BindOnce(&InstallEventLogManagerBase::LogUpload::StoreLog,
                        store_weak_factory_.GetWeakPtr()),
-        kStoreDelay);
+        FastUploadForTestsEnabled() ? kFastStoreDelay : kStoreDelay);
   }
 
   EnsureUpload(log_size.total_size > kTotalSizeExpeditedUploadThreshold ||
@@ -110,11 +124,16 @@
   upload_scheduled_ = true;
   expedited_upload_scheduled_ = expedited;
 
+  base::TimeDelta upload_delay =
+      expedited ? kExpeditedUploadDelay : kUploadInterval;
+  if (FastUploadForTestsEnabled())
+    upload_delay = kFastUploadDelay;
+
   base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&InstallEventLogManagerBase::LogUpload::RequestUpload,
                      upload_weak_factory_.GetWeakPtr()),
-      expedited ? kExpeditedUploadDelay : kUploadInterval);
+      upload_delay);
 }
 
 void InstallEventLogManagerBase::LogUpload::RequestUpload() {
diff --git a/chrome/browser/chromeos/policy/install_event_log_uploader_base.cc b/chrome/browser/chromeos/policy/install_event_log_uploader_base.cc
index 8f2dd1dd..d095fdd 100644
--- a/chrome/browser/chromeos/policy/install_event_log_uploader_base.cc
+++ b/chrome/browser/chromeos/policy/install_event_log_uploader_base.cc
@@ -6,6 +6,9 @@
 
 #include <algorithm>
 
+#include "base/command_line.h"
+#include "chromeos/constants/chromeos_switches.h"
+
 namespace policy {
 
 namespace {
@@ -17,6 +20,13 @@
 const int kMinRetryBackoffMs = 10 * 1000;            // 10 seconds
 const int kMaxRetryBackoffMs = 24 * 60 * 60 * 1000;  // 24 hours
 
+// If install-log-fast-upload-for-tests flag is enabled, do not increase retry
+// backoff.
+bool FastUploadForTestsEnabled() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      chromeos::switches::kInstallLogFastUploadForTests);
+}
+
 }  // namespace
 
 InstallEventLogUploaderBase::InstallEventLogUploaderBase(
@@ -81,6 +91,8 @@
     return;
   }
   PostTaskForStartSerialization();
+  if (FastUploadForTestsEnabled())
+    return;
   retry_backoff_ms_ = std::min(retry_backoff_ms_ << 1, kMaxRetryBackoffMs);
 }
 
diff --git a/chrome/browser/component_updater/component_updater_prefs.cc b/chrome/browser/component_updater/component_updater_prefs.cc
index e8ee2b9..b26e1ff 100644
--- a/chrome/browser/component_updater/component_updater_prefs.cc
+++ b/chrome/browser/component_updater/component_updater_prefs.cc
@@ -9,10 +9,6 @@
 #include "chrome/browser/component_updater/recovery_component_installer.h"
 #include "chrome/common/buildflags.h"
 
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-#include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
-#endif
-
 #if !defined(OS_ANDROID)
 #include "chrome/browser/component_updater/soda_component_installer.h"
 #endif
@@ -23,10 +19,6 @@
   RegisterPrefsForChromeComponentUpdaterConfigurator(registry);
   RegisterPrefsForRecoveryComponent(registry);
 
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-  SupervisedUserWhitelistInstaller::RegisterPrefs(registry);
-#endif
-
 #if !defined(OS_ANDROID)
   RegisterPrefsForSodaComponent(registry);
 #endif
diff --git a/chrome/browser/component_updater/registration.cc b/chrome/browser/component_updater/registration.cc
index 565d78c3..8bec7e3 100644
--- a/chrome/browser/component_updater/registration.cc
+++ b/chrome/browser/component_updater/registration.cc
@@ -79,10 +79,6 @@
 #include "ui/aura/env.h"
 #endif
 
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-#include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
-#endif
-
 namespace component_updater {
 
 void RegisterComponentsForUpdate(bool is_off_the_record_profile,
@@ -113,12 +109,6 @@
     RegisterPnaclComponent(cus);
 #endif  // BUILDFLAG(ENABLE_NACL) && !defined(OS_ANDROID)
 
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-  component_updater::SupervisedUserWhitelistInstaller* whitelist_installer =
-      g_browser_process->supervised_user_whitelist_installer();
-  whitelist_installer->RegisterComponents();
-#endif
-
   RegisterSubresourceFilterComponent(cus);
   RegisterFlocComponent(cus,
                         g_browser_process->floc_sorting_lsh_clusters_service());
diff --git a/chrome/browser/component_updater/supervised_user_whitelist_installer.cc b/chrome/browser/component_updater/supervised_user_whitelist_installer.cc
deleted file mode 100644
index c05e40e..0000000
--- a/chrome/browser/component_updater/supervised_user_whitelist_installer.cc
+++ /dev/null
@@ -1,674 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
-
-#include <stddef.h>
-
-#include <map>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/files/file_enumerator.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/important_file_writer.h"
-#include "base/location.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/metrics/user_metrics.h"
-#include "base/metrics/user_metrics_action.h"
-#include "base/path_service.h"
-#include "base/scoped_observer.h"
-#include "base/sequenced_task_runner.h"
-#include "base/stl_util.h"
-#include "base/strings/string_util.h"
-#include "base/task/task_traits.h"
-#include "base/task/thread_pool.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/util/values/values_util.h"
-#include "base/values.h"
-#include "chrome/browser/profiles/profile_attributes_storage.h"
-#include "chrome/browser/supervised_user/supervised_user_allowlist_service.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/common/pref_names.h"
-#include "components/component_updater/component_installer.h"
-#include "components/component_updater/component_updater_paths.h"
-#include "components/component_updater/component_updater_service.h"
-#include "components/crx_file/id_util.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/pref_service.h"
-#include "components/prefs/scoped_user_pref_update.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "services/data_decoder/public/cpp/json_sanitizer.h"
-
-namespace component_updater {
-
-namespace {
-
-const char kSanitizedWhitelistExtension[] = ".json";
-
-const char kWhitelistedContent[] = "whitelisted_content";
-const char kSites[] = "sites";
-
-const char kClients[] = "clients";
-const char kName[] = "name";
-
-// These are copies of extensions::manifest_keys::kName and kShortName. They
-// are duplicated here because we mustn't depend on code from extensions/
-// (since it's not built on Android).
-const char kExtensionName[] = "name";
-const char kExtensionShortName[] = "short_name";
-const char kExtensionIcons[] = "icons";
-const char kExtensionLargeIcon[] = "128";
-
-constexpr base::TaskTraits kThreadPoolTaskTraits = {
-    base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-    base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN};
-
-base::string16 GetWhitelistTitle(const base::DictionaryValue& manifest) {
-  base::string16 title;
-  if (!manifest.GetString(kExtensionShortName, &title))
-    manifest.GetString(kExtensionName, &title);
-  return title;
-}
-
-base::FilePath GetSafeFilePath(const base::DictionaryValue& dictionary,
-                               const std::string& key,
-                               const base::FilePath& install_dir) {
-  const base::Value* path_value = nullptr;
-  if (!dictionary.Get(key, &path_value))
-    return base::FilePath();
-  base::Optional<base::FilePath> path = util::ValueToFilePath(*path_value);
-  if (!path)
-    return base::FilePath();
-  // Path components ("..") are not allowed.
-  if (path->ReferencesParent())
-    return base::FilePath();
-
-  return install_dir.Append(*path);
-}
-
-base::FilePath GetLargeIconPath(const base::DictionaryValue& manifest,
-                                const base::FilePath& install_dir) {
-  const base::DictionaryValue* icons = nullptr;
-  if (!manifest.GetDictionary(kExtensionIcons, &icons))
-    return base::FilePath();
-
-  return GetSafeFilePath(*icons, kExtensionLargeIcon, install_dir);
-}
-
-base::FilePath GetRawWhitelistPath(const base::DictionaryValue& manifest,
-                                   const base::FilePath& install_dir) {
-  const base::DictionaryValue* whitelist_dict = nullptr;
-  if (!manifest.GetDictionary(kWhitelistedContent, &whitelist_dict))
-    return base::FilePath();
-
-  return GetSafeFilePath(*whitelist_dict, kSites, install_dir);
-}
-
-base::FilePath GetSanitizedWhitelistPath(const std::string& crx_id) {
-  base::FilePath base_dir;
-  base::PathService::Get(chrome::DIR_SUPERVISED_USER_INSTALLED_WHITELISTS,
-                         &base_dir);
-  return base_dir.empty()
-             ? base::FilePath()
-             : base_dir.AppendASCII(crx_id + kSanitizedWhitelistExtension);
-}
-
-void RecordUncleanUninstall() {
-  content::GetUIThreadTaskRunner({})->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &base::RecordAction,
-          base::UserMetricsAction("ManagedUsers_Whitelist_UncleanUninstall")));
-}
-
-void DeleteFileOnTaskRunner(const base::FilePath& path) {
-  if (!base::DeletePathRecursively(path))
-    DPLOG(ERROR) << "Couldn't delete " << path.value();
-}
-
-void OnWhitelistSanitizationResult(
-    const base::FilePath& whitelist_path,
-    const std::string& crx_id,
-    scoped_refptr<base::SequencedTaskRunner> task_runner,
-    base::OnceClosure callback,
-    data_decoder::JsonSanitizer::Result result) {
-  if (!result.value) {
-    LOG(WARNING) << "Invalid whitelist " << whitelist_path.value() << ": "
-                 << *result.error;
-    return;
-  }
-
-  const base::FilePath sanitized_whitelist_path =
-      GetSanitizedWhitelistPath(crx_id);
-  const base::FilePath install_directory = sanitized_whitelist_path.DirName();
-  if (!base::DirectoryExists(install_directory)) {
-    if (!base::CreateDirectory(install_directory)) {
-      PLOG(ERROR) << "Could't create directory " << install_directory.value();
-      return;
-    }
-  }
-
-  const int size = result.value->size();
-  if (base::WriteFile(sanitized_whitelist_path, result.value->data(), size) !=
-      size) {
-    PLOG(ERROR) << "Couldn't write file " << sanitized_whitelist_path.value();
-    return;
-  }
-  task_runner->PostTask(FROM_HERE, std::move(callback));
-}
-
-void CheckForSanitizedWhitelistOnTaskRunner(
-    const std::string& crx_id,
-    const base::FilePath& whitelist_path,
-    scoped_refptr<base::SequencedTaskRunner> task_runner,
-    base::OnceClosure callback) {
-  if (base::PathExists(GetSanitizedWhitelistPath(crx_id))) {
-    task_runner->PostTask(FROM_HERE, std::move(callback));
-    return;
-  }
-
-  std::string unsafe_json;
-  if (!base::ReadFileToString(whitelist_path, &unsafe_json)) {
-    PLOG(ERROR) << "Couldn't read file " << whitelist_path.value();
-    return;
-  }
-
-  data_decoder::JsonSanitizer::Sanitize(
-      unsafe_json,
-      base::BindOnce(&OnWhitelistSanitizationResult, whitelist_path, crx_id,
-                     task_runner, std::move(callback)));
-}
-
-void RemoveUnregisteredWhitelistsOnTaskRunner(
-    const std::set<std::string>& registered_whitelists) {
-  base::FilePath base_dir;
-  base::PathService::Get(DIR_SUPERVISED_USER_ALLOWLISTS, &base_dir);
-  if (!base_dir.empty()) {
-    base::FileEnumerator file_enumerator(base_dir, false,
-                                         base::FileEnumerator::DIRECTORIES);
-    for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
-         path = file_enumerator.Next()) {
-      const std::string crx_id = path.BaseName().MaybeAsASCII();
-
-      // Ignore folders that don't have valid CRX ID names. These folders are
-      // not managed by the component installer, so do not try to remove them.
-      if (!crx_file::id_util::IdIsValid(crx_id))
-        continue;
-
-      // Ignore folders that correspond to registered whitelists.
-      if (base::Contains(registered_whitelists, crx_id))
-        continue;
-
-      RecordUncleanUninstall();
-
-      DeleteFileOnTaskRunner(path);
-    }
-  }
-
-  base::PathService::Get(chrome::DIR_SUPERVISED_USER_INSTALLED_WHITELISTS,
-                         &base_dir);
-  if (!base_dir.empty()) {
-    base::FilePath pattern(FILE_PATH_LITERAL("*"));
-    pattern = pattern.AppendASCII(kSanitizedWhitelistExtension);
-    base::FileEnumerator file_enumerator(
-        base_dir, false, base::FileEnumerator::FILES, pattern.value());
-    for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
-         path = file_enumerator.Next()) {
-      // Ignore files that don't have valid CRX ID names. These files are not
-      // managed by the component installer, so do not try to remove them.
-      const std::string filename = path.BaseName().MaybeAsASCII();
-      DCHECK(base::EndsWith(filename, kSanitizedWhitelistExtension,
-                            base::CompareCase::SENSITIVE));
-
-      const std::string crx_id = filename.substr(
-          filename.size() - strlen(kSanitizedWhitelistExtension));
-
-      if (!crx_file::id_util::IdIsValid(crx_id))
-        continue;
-
-      // Ignore files that correspond to registered whitelists.
-      if (base::Contains(registered_whitelists, crx_id))
-        continue;
-
-      RecordUncleanUninstall();
-
-      DeleteFileOnTaskRunner(path);
-    }
-  }
-}
-
-class SupervisedUserWhitelistComponentInstallerPolicy
-    : public ComponentInstallerPolicy {
- public:
-  using RawWhitelistReadyCallback =
-      base::RepeatingCallback<void(const base::string16&, /* title */
-                                   const base::FilePath&, /* icon_path */
-                                   const base::FilePath& /* whitelist_path */)>;
-
-  SupervisedUserWhitelistComponentInstallerPolicy(
-      const std::string& crx_id,
-      const std::string& name,
-      RawWhitelistReadyCallback callback)
-      : crx_id_(crx_id), name_(name), callback_(callback) {}
-  ~SupervisedUserWhitelistComponentInstallerPolicy() override = default;
-
- private:
-  // ComponentInstallerPolicy overrides:
-  bool VerifyInstallation(const base::DictionaryValue& manifest,
-                          const base::FilePath& install_dir) const override;
-  bool SupportsGroupPolicyEnabledComponentUpdates() const override;
-  bool RequiresNetworkEncryption() const override;
-  update_client::CrxInstaller::Result OnCustomInstall(
-      const base::DictionaryValue& manifest,
-      const base::FilePath& install_dir) override;
-  void OnCustomUninstall() override;
-  void ComponentReady(const base::Version& version,
-                      const base::FilePath& install_dir,
-                      std::unique_ptr<base::DictionaryValue> manifest) override;
-  base::FilePath GetRelativeInstallDir() const override;
-  void GetHash(std::vector<uint8_t>* hash) const override;
-  std::string GetName() const override;
-  update_client::InstallerAttributes GetInstallerAttributes() const override;
-  std::vector<std::string> GetMimeTypes() const override;
-
-  std::string crx_id_;
-  std::string name_;
-  RawWhitelistReadyCallback callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(SupervisedUserWhitelistComponentInstallerPolicy);
-};
-
-bool SupervisedUserWhitelistComponentInstallerPolicy::VerifyInstallation(
-    const base::DictionaryValue& manifest,
-    const base::FilePath& install_dir) const {
-  // Check whether the whitelist exists at the path specified by the manifest.
-  // This does not check whether the whitelist is wellformed.
-  return base::PathExists(GetRawWhitelistPath(manifest, install_dir));
-}
-
-bool SupervisedUserWhitelistComponentInstallerPolicy::
-    SupportsGroupPolicyEnabledComponentUpdates() const {
-  return false;
-}
-
-bool SupervisedUserWhitelistComponentInstallerPolicy::
-    RequiresNetworkEncryption() const {
-  return true;
-}
-
-update_client::CrxInstaller::Result
-SupervisedUserWhitelistComponentInstallerPolicy::OnCustomInstall(
-    const base::DictionaryValue& manifest,
-    const base::FilePath& install_dir) {
-  // Delete the existing sanitized whitelist.
-  const bool success = base::DeleteFile(GetSanitizedWhitelistPath(crx_id_));
-  return update_client::CrxInstaller::Result(
-      success ? update_client::InstallError::NONE
-              : update_client::InstallError::GENERIC_ERROR);
-}
-
-void SupervisedUserWhitelistComponentInstallerPolicy::OnCustomUninstall() {}
-
-void SupervisedUserWhitelistComponentInstallerPolicy::ComponentReady(
-    const base::Version& version,
-    const base::FilePath& install_dir,
-    std::unique_ptr<base::DictionaryValue> manifest) {
-  // TODO(crbug.com/558387): Before getting the title, we should localize the
-  // manifest using extension_l10n_util::LocalizeExtension, but that doesn't
-  // exist on Android.
-  callback_.Run(GetWhitelistTitle(*manifest),
-                GetLargeIconPath(*manifest, install_dir),
-                GetRawWhitelistPath(*manifest, install_dir));
-}
-
-base::FilePath
-SupervisedUserWhitelistComponentInstallerPolicy::GetRelativeInstallDir() const {
-  return base::FilePath(component_updater::kSupervisedUserWhitelistDirName)
-      .AppendASCII(crx_id_);
-}
-
-void SupervisedUserWhitelistComponentInstallerPolicy::GetHash(
-    std::vector<uint8_t>* hash) const {
-  *hash = SupervisedUserWhitelistInstaller::GetHashFromCrxId(crx_id_);
-}
-
-std::string SupervisedUserWhitelistComponentInstallerPolicy::GetName() const {
-  return name_;
-}
-
-update_client::InstallerAttributes
-SupervisedUserWhitelistComponentInstallerPolicy::GetInstallerAttributes()
-    const {
-  return update_client::InstallerAttributes();
-}
-
-std::vector<std::string>
-SupervisedUserWhitelistComponentInstallerPolicy::GetMimeTypes() const {
-  return std::vector<std::string>();
-}
-
-class SupervisedUserWhitelistInstallerImpl
-    : public SupervisedUserWhitelistInstaller,
-      public ProfileAttributesStorage::Observer {
- public:
-  SupervisedUserWhitelistInstallerImpl(
-      ComponentUpdateService* cus,
-      ProfileAttributesStorage* profile_attributes_storage,
-      PrefService* local_state);
-  ~SupervisedUserWhitelistInstallerImpl() override = default;
-
- private:
-  void RegisterComponent(const std::string& crx_id,
-                         const std::string& name,
-                         base::OnceClosure callback);
-  void RegisterNewComponent(const std::string& crx_id, const std::string& name);
-  bool UnregisterWhitelistInternal(base::DictionaryValue* pref_dict,
-                                   const std::string& client_id,
-                                   const std::string& crx_id);
-
-  void OnRawWhitelistReady(const std::string& crx_id,
-                           const base::string16& title,
-                           const base::FilePath& large_icon_path,
-                           const base::FilePath& whitelist_path);
-  void OnSanitizedWhitelistReady(const std::string& crx_id,
-                                 const base::string16& title,
-                                 const base::FilePath& large_icon_path);
-
-  // SupervisedUserWhitelistInstaller overrides:
-  void RegisterComponents() override;
-  void Subscribe(WhitelistReadyCallback callback) override;
-  void RegisterWhitelist(const std::string& client_id,
-                         const std::string& crx_id,
-                         const std::string& name) override;
-  void UnregisterWhitelist(const std::string& client_id,
-                           const std::string& crx_id) override;
-
-  // ProfileAttributesStorage::Observer overrides:
-  void OnProfileWillBeRemoved(const base::FilePath& profile_path) override;
-
-  ComponentUpdateService* cus_ = nullptr;
-  PrefService* local_state_ = nullptr;
-
-  std::vector<WhitelistReadyCallback> callbacks_;
-
-  ScopedObserver<ProfileAttributesStorage, ProfileAttributesStorage::Observer>
-      observer_;
-
-  scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_ =
-      base::ThreadPool::CreateSequencedTaskRunner(kThreadPoolTaskTraits);
-
-  base::WeakPtrFactory<SupervisedUserWhitelistInstallerImpl> weak_ptr_factory_{
-      this};
-
-  DISALLOW_COPY_AND_ASSIGN(SupervisedUserWhitelistInstallerImpl);
-};
-
-SupervisedUserWhitelistInstallerImpl::SupervisedUserWhitelistInstallerImpl(
-    ComponentUpdateService* cus,
-    ProfileAttributesStorage* profile_attributes_storage,
-    PrefService* local_state)
-    : cus_(cus), local_state_(local_state), observer_(this) {
-  DCHECK(cus);
-  DCHECK(local_state);
-  observer_.Add(profile_attributes_storage);
-}
-
-void SupervisedUserWhitelistInstallerImpl::RegisterComponent(
-    const std::string& crx_id,
-    const std::string& name,
-    base::OnceClosure callback) {
-  std::unique_ptr<ComponentInstallerPolicy> policy =
-      std::make_unique<SupervisedUserWhitelistComponentInstallerPolicy>(
-          crx_id, name,
-          base::Bind(&SupervisedUserWhitelistInstallerImpl::OnRawWhitelistReady,
-                     weak_ptr_factory_.GetWeakPtr(), crx_id));
-  scoped_refptr<ComponentInstaller> installer =
-      base::MakeRefCounted<ComponentInstaller>(std::move(policy));
-  installer->Register(cus_, std::move(callback));
-}
-
-void SupervisedUserWhitelistInstallerImpl::RegisterNewComponent(
-    const std::string& crx_id,
-    const std::string& name) {
-  RegisterComponent(
-      crx_id, name,
-      base::BindOnce(&SupervisedUserWhitelistInstaller::TriggerComponentUpdate,
-                     &cus_->GetOnDemandUpdater(), crx_id));
-}
-
-bool SupervisedUserWhitelistInstallerImpl::UnregisterWhitelistInternal(
-    base::DictionaryValue* pref_dict,
-    const std::string& client_id,
-    const std::string& crx_id) {
-  base::DictionaryValue* whitelist_dict = nullptr;
-  bool success =
-      pref_dict->GetDictionaryWithoutPathExpansion(crx_id, &whitelist_dict);
-  DCHECK(success);
-  base::ListValue* clients = nullptr;
-  success = whitelist_dict->GetList(kClients, &clients);
-
-  const bool removed = clients->Remove(base::Value(client_id), nullptr);
-
-  if (!clients->empty())
-    return removed;
-
-  pref_dict->RemoveKey(crx_id);
-  const bool result = cus_->UnregisterComponent(crx_id);
-  DCHECK(result);
-
-  sequenced_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&DeleteFileOnTaskRunner,
-                                GetSanitizedWhitelistPath(crx_id)));
-  return removed;
-}
-
-void SupervisedUserWhitelistInstallerImpl::OnRawWhitelistReady(
-    const std::string& crx_id,
-    const base::string16& title,
-    const base::FilePath& large_icon_path,
-    const base::FilePath& whitelist_path) {
-  // TODO(sorin): avoid using a single thread task runner crbug.com/744718.
-  auto task_runner = base::ThreadPool::CreateSingleThreadTaskRunner(
-      kThreadPoolTaskTraits, base::SingleThreadTaskRunnerThreadMode::SHARED);
-  task_runner->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &CheckForSanitizedWhitelistOnTaskRunner, crx_id, whitelist_path,
-          base::ThreadTaskRunnerHandle::Get(),
-          base::BindOnce(
-              &SupervisedUserWhitelistInstallerImpl::OnSanitizedWhitelistReady,
-              weak_ptr_factory_.GetWeakPtr(), crx_id, title, large_icon_path)));
-}
-
-void SupervisedUserWhitelistInstallerImpl::OnSanitizedWhitelistReady(
-    const std::string& crx_id,
-    const base::string16& title,
-    const base::FilePath& large_icon_path) {
-  for (WhitelistReadyCallback callback : callbacks_)
-    callback.Run(crx_id, title, large_icon_path,
-                 GetSanitizedWhitelistPath(crx_id));
-}
-
-void SupervisedUserWhitelistInstallerImpl::RegisterComponents() {
-  const std::map<std::string, std::string> command_line_whitelists =
-      SupervisedUserAllowlistService::GetAllowlistsFromCommandLine();
-
-  std::set<std::string> registered_whitelists;
-  std::set<std::string> stale_whitelists;
-  DictionaryPrefUpdate update(local_state_,
-                              prefs::kRegisteredSupervisedUserWhitelists);
-  base::DictionaryValue* whitelists = update.Get();
-  for (base::DictionaryValue::Iterator it(*whitelists); !it.IsAtEnd();
-       it.Advance()) {
-    const base::DictionaryValue* dict = nullptr;
-    it.value().GetAsDictionary(&dict);
-
-    const std::string& id = it.key();
-
-    // Skip whitelists with no clients. This can happen when a whitelist was
-    // previously registered on the command line but isn't anymore.
-    const base::ListValue* clients = nullptr;
-    if ((!dict->GetList(kClients, &clients) || clients->empty()) &&
-        !base::Contains(command_line_whitelists, id)) {
-      stale_whitelists.insert(id);
-      continue;
-    }
-
-    std::string name;
-    const bool result = dict->GetString(kName, &name);
-    DCHECK(result);
-    RegisterComponent(id, name, base::OnceClosure());
-
-    registered_whitelists.insert(id);
-  }
-
-  // Clean up stale whitelists as determined above.
-  for (const std::string& id : stale_whitelists)
-    whitelists->RemoveKey(id);
-
-  sequenced_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&RemoveUnregisteredWhitelistsOnTaskRunner,
-                                registered_whitelists));
-}
-
-void SupervisedUserWhitelistInstallerImpl::Subscribe(
-    WhitelistReadyCallback callback) {
-  return callbacks_.push_back(callback);
-}
-
-void SupervisedUserWhitelistInstallerImpl::RegisterWhitelist(
-    const std::string& client_id,
-    const std::string& crx_id,
-    const std::string& name) {
-  DictionaryPrefUpdate update(local_state_,
-                              prefs::kRegisteredSupervisedUserWhitelists);
-  base::Value* pref_dict = update.Get();
-  base::Value* whitelist_dict =
-      pref_dict->FindKeyOfType(crx_id, base::Value::Type::DICTIONARY);
-  const bool newly_added = !whitelist_dict;
-  if (newly_added) {
-    whitelist_dict =
-        pref_dict->SetKey(crx_id, base::Value(base::Value::Type::DICTIONARY));
-    whitelist_dict->SetKey(kName, base::Value(name));
-  }
-
-  if (!client_id.empty()) {
-    base::Value* clients =
-        whitelist_dict->FindKeyOfType(kClients, base::Value::Type::LIST);
-    if (!clients) {
-      DCHECK(newly_added);
-      clients = whitelist_dict->SetKey(kClients,
-                                       base::Value(base::Value::Type::LIST));
-    }
-
-    base::Value client(client_id);
-    DCHECK(!base::Contains(clients->GetList(), client));
-    clients->Append(std::move(client));
-  }
-
-  if (!newly_added) {
-    // Sanity-check that the stored name is equal to the name passed in.
-    // In release builds this is a no-op.
-    DCHECK_EQ(name, whitelist_dict->FindKey(kName)->GetString());
-    return;
-  }
-
-  RegisterNewComponent(crx_id, name);
-}
-
-void SupervisedUserWhitelistInstallerImpl::UnregisterWhitelist(
-    const std::string& client_id,
-    const std::string& crx_id) {
-  DictionaryPrefUpdate update(local_state_,
-                              prefs::kRegisteredSupervisedUserWhitelists);
-  bool removed = UnregisterWhitelistInternal(update.Get(), client_id, crx_id);
-  DCHECK(removed);
-}
-
-void SupervisedUserWhitelistInstallerImpl::OnProfileWillBeRemoved(
-    const base::FilePath& profile_path) {
-  std::string client_id = ClientIdForProfilePath(profile_path);
-
-  // Go through all registered whitelists and possibly unregister them for this
-  // client. Because unregistering a whitelist might completely uninstall it, we
-  // need to make a copy of all the IDs before iterating over them.
-  DictionaryPrefUpdate update(local_state_,
-                              prefs::kRegisteredSupervisedUserWhitelists);
-  base::DictionaryValue* pref_dict = update.Get();
-
-  std::vector<std::string> crx_ids;
-  for (base::DictionaryValue::Iterator it(*pref_dict); !it.IsAtEnd();
-       it.Advance()) {
-    crx_ids.push_back(it.key());
-  }
-
-  for (const std::string& crx_id : crx_ids)
-    UnregisterWhitelistInternal(pref_dict, client_id, crx_id);
-}
-
-}  // namespace
-
-// static
-std::unique_ptr<SupervisedUserWhitelistInstaller>
-SupervisedUserWhitelistInstaller::Create(
-    ComponentUpdateService* cus,
-    ProfileAttributesStorage* profile_attributes_storage,
-    PrefService* local_state) {
-  return std::make_unique<SupervisedUserWhitelistInstallerImpl>(
-      cus, profile_attributes_storage, local_state);
-}
-
-// static
-void SupervisedUserWhitelistInstaller::RegisterPrefs(
-    PrefRegistrySimple* registry) {
-  registry->RegisterDictionaryPref(prefs::kRegisteredSupervisedUserWhitelists);
-}
-
-// static
-std::string SupervisedUserWhitelistInstaller::ClientIdForProfilePath(
-    const base::FilePath& profile_path) {
-  // See ProfileInfoCache::CacheKeyFromProfilePath().
-  // TODO(anthonyvd): update comment when the refactoring of ProfileInfoCache
-  // is completed.
-  return profile_path.BaseName().MaybeAsASCII();
-}
-
-// static
-std::vector<uint8_t> SupervisedUserWhitelistInstaller::GetHashFromCrxId(
-    const std::string& crx_id) {
-  DCHECK(crx_file::id_util::IdIsValid(crx_id));
-
-  std::vector<uint8_t> hash;
-  uint8_t byte = 0;
-  for (size_t i = 0; i < crx_id.size(); ++i) {
-    // Uppercase characters in IDs are technically legal.
-    int val = base::ToLowerASCII(crx_id[i]) - 'a';
-    DCHECK_GE(val, 0);
-    DCHECK_LT(val, 16);
-    if (i % 2 == 0) {
-      byte = val;
-    } else {
-      hash.push_back(16 * byte + val);
-      byte = 0;
-    }
-  }
-  return hash;
-}
-
-// static
-void SupervisedUserWhitelistInstaller::TriggerComponentUpdate(
-    OnDemandUpdater* updater,
-    const std::string& crx_id) {
-  // TODO(sorin): use a callback to check the result (crbug.com/639189).
-  updater->OnDemandUpdate(crx_id, OnDemandUpdater::Priority::FOREGROUND,
-                          component_updater::Callback());
-}
-
-}  // namespace component_updater
diff --git a/chrome/browser/component_updater/supervised_user_whitelist_installer.h b/chrome/browser/component_updater/supervised_user_whitelist_installer.h
deleted file mode 100644
index 7134062..0000000
--- a/chrome/browser/component_updater/supervised_user_whitelist_installer.h
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_COMPONENT_UPDATER_SUPERVISED_USER_WHITELIST_INSTALLER_H_
-#define CHROME_BROWSER_COMPONENT_UPDATER_SUPERVISED_USER_WHITELIST_INSTALLER_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/callback_forward.h"
-#include "base/strings/string16.h"
-
-namespace base {
-class FilePath;
-}
-
-class PrefRegistrySimple;
-class PrefService;
-class ProfileAttributesStorage;
-
-namespace component_updater {
-
-class ComponentUpdateService;
-class OnDemandUpdater;
-
-class SupervisedUserWhitelistInstaller {
- public:
-  using WhitelistReadyCallback =
-      base::RepeatingCallback<void(const std::string& crx_id,
-                                   const base::string16& title,
-                                   const base::FilePath& large_icon_path,
-                                   const base::FilePath& whitelist_path)>;
-
-  virtual ~SupervisedUserWhitelistInstaller() = default;
-
-  static std::unique_ptr<SupervisedUserWhitelistInstaller> Create(
-      ComponentUpdateService* cus,
-      ProfileAttributesStorage* profile_attributes_storage,
-      PrefService* local_state);
-
-  static void RegisterPrefs(PrefRegistrySimple* registry);
-
-  // Generates a client ID suitable for RegisterWhitelist() and
-  // UnregisterWhitelist() below from a profile path.
-  static std::string ClientIdForProfilePath(const base::FilePath& profile_path);
-
-  // Turns a CRX ID (which is derived from a hash) back into a hash.
-  // Note that the resulting hash will be only 16 bytes long instead of the
-  // usual 32 bytes, as the CRX ID is created from the first half of the
-  // original hash, but the component installer will still accept this.
-  // Public for testing.
-  static std::vector<uint8_t> GetHashFromCrxId(const std::string& crx_id);
-
-  // Starts registering all components with the ComponentUpdaterService.
-  // Also removes unregistered components on disk (which are most likely left
-  // over from a previous uninstallation that was interrupted, e.g. during
-  // shutdown or a crash).
-  virtual void RegisterComponents() = 0;
-
-  // Subscribes for notifications about available whitelists. Clients should
-  // filter out the whitelists they are interested in via the |crx_id|
-  // parameter.
-  virtual void Subscribe(WhitelistReadyCallback callback) = 0;
-
-  // Registers a new whitelist with the given |crx_id|.
-  // The |client_id| should be a unique identifier for the client that is stable
-  // across restarts. If it is empty, the registration will not be persisted in
-  // Local State.
-  virtual void RegisterWhitelist(const std::string& client_id,
-                                 const std::string& crx_id,
-                                 const std::string& name) = 0;
-
-  // Unregisters a whitelist.
-  virtual void UnregisterWhitelist(const std::string& client_id,
-                                   const std::string& crx_id) = 0;
-
- protected:
-  // Triggers an update for a whitelist to be installed. Protected so it can be
-  // called from the implementation subclass, and declared here so that the
-  // OnDemandUpdater can friend this class and the implementation subclass can
-  // live in an anonymous namespace.
-  static void TriggerComponentUpdate(OnDemandUpdater* updater,
-                                     const std::string& crx_id);
-};
-
-}  // namespace component_updater
-
-#endif  // CHROME_BROWSER_COMPONENT_UPDATER_SUPERVISED_USER_WHITELIST_INSTALLER_H_
diff --git a/chrome/browser/component_updater/supervised_user_whitelist_installer_unittest.cc b/chrome/browser/component_updater/supervised_user_whitelist_installer_unittest.cc
deleted file mode 100644
index 697d59fc..0000000
--- a/chrome/browser/component_updater/supervised_user_whitelist_installer_unittest.cc
+++ /dev/null
@@ -1,457 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stddef.h>
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/callback_helpers.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/json/json_file_value_serializer.h"
-#include "base/json/json_reader.h"
-#include "base/json/json_writer.h"
-#include "base/path_service.h"
-#include "base/run_loop.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/values.h"
-#include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
-#include "chrome/browser/profiles/profile_attributes_storage.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "chrome/test/base/testing_profile_manager.h"
-#include "components/account_id/account_id.h"
-#include "components/component_updater/component_updater_paths.h"
-#include "components/component_updater/component_updater_service.h"
-#include "components/crx_file/id_util.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/update_client/crx_update_item.h"
-#include "components/update_client/update_client.h"
-#include "components/update_client/utils.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/test_utils.h"
-#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using update_client::CrxComponent;
-using update_client::CrxUpdateItem;
-
-namespace component_updater {
-
-namespace {
-
-const char kClientId[] = "client-id";
-const char kCrxId[] = "abcdefghijklmnopponmlkjihgfedcba";
-const char kName[] = "Some Whitelist";
-const char kOtherClientId[] = "other-client-id";
-const char kVersion[] = "1.2.3.4";
-const char kWhitelistContents[] = "{\"foo\": \"bar\"}";
-const char kWhitelistFile[] = "whitelist.json";
-const char kLargeIconFile[] = "icon.png";
-
-std::string CrxIdToHashToCrxId(const std::string& kCrxId) {
-  CrxComponent component;
-  component.pk_hash =
-      SupervisedUserWhitelistInstaller::GetHashFromCrxId(kCrxId);
-  EXPECT_EQ(16u, component.pk_hash.size());
-  return GetCrxComponentID(component);
-}
-
-std::string JsonToString(const base::DictionaryValue& dict) {
-  std::string json;
-  base::JSONWriter::Write(dict, &json);
-  return json;
-}
-
-class MockComponentUpdateService : public ComponentUpdateService,
-                                   public OnDemandUpdater {
- public:
-  ~MockComponentUpdateService() override = default;
-
-  bool on_demand_update_called() const { return on_demand_update_called_; }
-
-  const CrxComponent* registered_component() { return component_.get(); }
-
-  void set_registration_callback(base::OnceClosure registration_callback) {
-    registration_callback_ = std::move(registration_callback);
-  }
-
-  // ComponentUpdateService implementation:
-  void AddObserver(Observer* observer) override { ADD_FAILURE(); }
-  void RemoveObserver(Observer* observer) override { ADD_FAILURE(); }
-
-  std::vector<std::string> GetComponentIDs() const override {
-    ADD_FAILURE();
-    return std::vector<std::string>();
-  }
-
-  bool RegisterComponent(const CrxComponent& component) override {
-    EXPECT_EQ(nullptr, component_.get());
-    component_ = std::make_unique<CrxComponent>(component);
-    if (!registration_callback_.is_null())
-      std::move(registration_callback_).Run();
-
-    return true;
-  }
-
-  bool UnregisterComponent(const std::string& crx_id) override {
-    if (!component_) {
-      ADD_FAILURE();
-      return false;
-    }
-
-    EXPECT_EQ(GetCrxComponentID(*component_), crx_id);
-    if (!component_->installer->Uninstall()) {
-      ADD_FAILURE();
-      return false;
-    }
-
-    component_.reset();
-    return true;
-  }
-
-  OnDemandUpdater& GetOnDemandUpdater() override { return *this; }
-
-  void MaybeThrottle(const std::string& kCrxId,
-                     const base::OnceClosure callback) override {
-    ADD_FAILURE();
-  }
-
-  bool GetComponentDetails(const std::string& component_id,
-                           CrxUpdateItem* item) const override {
-    ADD_FAILURE();
-    return false;
-  }
-
-  std::unique_ptr<ComponentInfo> GetComponentForMimeType(
-      const std::string& mime_type) const override {
-    return nullptr;
-  }
-
-  std::vector<ComponentInfo> GetComponents() const override {
-    return std::vector<ComponentInfo>();
-  }
-
-  // OnDemandUpdater implementation:
-  void OnDemandUpdate(const std::string& crx_id,
-                      Priority priority,
-                      Callback callback) override {
-    on_demand_update_called_ = true;
-
-    if (!component_) {
-      ADD_FAILURE() << "Trying to update unregistered component " << crx_id;
-      return;
-    }
-
-    EXPECT_EQ(OnDemandUpdater::Priority::FOREGROUND, priority);
-    EXPECT_EQ(GetCrxComponentID(*component_), crx_id);
-  }
-
- private:
-  std::unique_ptr<CrxComponent> component_;
-  base::OnceClosure registration_callback_;
-  bool on_demand_update_called_ = false;
-};
-
-class WhitelistLoadObserver {
- public:
-  explicit WhitelistLoadObserver(SupervisedUserWhitelistInstaller* installer) {
-    installer->Subscribe(
-        base::BindRepeating(&WhitelistLoadObserver::OnWhitelistReady,
-                            weak_ptr_factory_.GetWeakPtr()));
-  }
-
-  void Wait() { run_loop_.Run(); }
-  void Quit() { run_loop_.Quit(); }
-
-  const base::FilePath& large_icon_path() const { return large_icon_path_; }
-  const base::FilePath& whitelist_path() const { return whitelist_path_; }
-
- private:
-  void OnWhitelistReady(const std::string& crx_id,
-                        const base::string16& title,
-                        const base::FilePath& large_icon_path,
-                        const base::FilePath& whitelist_path) {
-    EXPECT_EQ(base::FilePath::StringType(), large_icon_path_.value());
-    EXPECT_EQ(base::FilePath::StringType(), whitelist_path_.value());
-    whitelist_path_ = whitelist_path;
-    large_icon_path_ = large_icon_path;
-    Quit();
-  }
-
-  base::FilePath large_icon_path_;
-  base::FilePath whitelist_path_;
-
-  base::RunLoop run_loop_;
-  base::WeakPtrFactory<WhitelistLoadObserver> weak_ptr_factory_{this};
-};
-
-}  // namespace
-
-class SupervisedUserWhitelistInstallerTest : public testing::Test {
- public:
-  SupervisedUserWhitelistInstallerTest()
-      : testing_profile_manager_(TestingBrowserProcess::GetGlobal()) {}
-
-  ~SupervisedUserWhitelistInstallerTest() override = default;
-
-  void SetUp() override {
-    SupervisedUserWhitelistInstaller::RegisterPrefs(local_state_.registry());
-
-    ASSERT_TRUE(testing_profile_manager_.SetUp());
-
-    profile_attributes_storage()->AddProfile(
-        GetProfilePath(kClientId), base::ASCIIToUTF16("A Profile"),
-        std::string(), base::string16(), false, 0, std::string(),
-        EmptyAccountId());
-    profile_attributes_storage()->AddProfile(
-        GetProfilePath(kOtherClientId), base::ASCIIToUTF16("Another Profile"),
-        std::string(), base::string16(), false, 0, std::string(),
-        EmptyAccountId());
-
-    installer_ = SupervisedUserWhitelistInstaller::Create(
-        &component_update_service_,
-        profile_attributes_storage(),
-        &local_state_);
-
-    ASSERT_TRUE(base::PathService::Get(DIR_SUPERVISED_USER_ALLOWLISTS,
-                                       &whitelist_base_directory_));
-    whitelist_directory_ = whitelist_base_directory_.AppendASCII(kCrxId);
-    whitelist_version_directory_ = whitelist_directory_.AppendASCII(kVersion);
-
-    ASSERT_TRUE(
-        base::PathService::Get(chrome::DIR_SUPERVISED_USER_INSTALLED_WHITELISTS,
-                               &installed_whitelist_directory_));
-    std::string crx_id(kCrxId);
-    whitelist_path_ =
-        installed_whitelist_directory_.AppendASCII(crx_id + ".json");
-    large_icon_path_ = whitelist_version_directory_.AppendASCII(kLargeIconFile);
-
-    auto crx_dict = std::make_unique<base::DictionaryValue>();
-    crx_dict->SetString("name", kName);
-    std::unique_ptr<base::ListValue> clients =
-        std::make_unique<base::ListValue>();
-    clients->AppendString(kClientId);
-    clients->AppendString(kOtherClientId);
-    crx_dict->Set("clients", std::move(clients));
-    pref_.Set(kCrxId, std::move(crx_dict));
-  }
-
- protected:
-  ProfileAttributesStorage* profile_attributes_storage() {
-    return testing_profile_manager_.profile_attributes_storage();
-  }
-
-  base::FilePath GetProfilePath(const std::string& profile_name) {
-    return testing_profile_manager_.profiles_dir().AppendASCII(profile_name);
-  }
-
-  void PrepareWhitelistFile(const base::FilePath& whitelist_path) {
-    size_t whitelist_contents_length = sizeof(kWhitelistContents) - 1;
-    ASSERT_EQ(static_cast<int>(whitelist_contents_length),
-              base::WriteFile(whitelist_path, kWhitelistContents,
-                              whitelist_contents_length));
-  }
-
-  void PrepareWhitelistDirectory(const base::FilePath& whitelist_directory) {
-    PrepareWhitelistFile(whitelist_directory.AppendASCII(kWhitelistFile));
-    base::FilePath manifest_file =
-        whitelist_directory.AppendASCII("manifest.json");
-
-    base::DictionaryValue manifest;
-
-    auto whitelist_dict = std::make_unique<base::DictionaryValue>();
-    whitelist_dict->SetString("sites", kWhitelistFile);
-    manifest.Set("whitelisted_content", std::move(whitelist_dict));
-
-    auto icons_dict = std::make_unique<base::DictionaryValue>();
-    icons_dict->SetString("128", kLargeIconFile);
-    manifest.Set("icons", std::move(icons_dict));
-
-    manifest.SetString("version", kVersion);
-
-    ASSERT_TRUE(JSONFileValueSerializer(manifest_file).Serialize(manifest));
-  }
-
-  void RegisterExistingComponents() {
-    local_state_.Set(prefs::kRegisteredSupervisedUserWhitelists, pref_);
-    installer_->RegisterComponents();
-    content::RunAllTasksUntilIdle();
-    base::RunLoop().RunUntilIdle();
-  }
-
-  void CheckRegisteredComponent(const char* version) {
-    const CrxComponent* component =
-        component_update_service_.registered_component();
-    ASSERT_TRUE(component);
-    EXPECT_EQ(kName, component->name);
-    EXPECT_EQ(kCrxId, GetCrxComponentID(*component));
-    EXPECT_EQ(version, component->version.GetString());
-  }
-
-  content::BrowserTaskEnvironment task_environment_;
-  TestingProfileManager testing_profile_manager_;
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
-  TestingPrefServiceSimple local_state_;
-  std::unique_ptr<SupervisedUserWhitelistInstaller> installer_;
-  base::FilePath whitelist_base_directory_;
-  base::FilePath whitelist_directory_;
-  base::FilePath whitelist_version_directory_;
-  base::FilePath installed_whitelist_directory_;
-  base::FilePath whitelist_path_;
-  base::FilePath large_icon_path_;
-  base::DictionaryValue pref_;
-  MockComponentUpdateService component_update_service_;
-};
-
-TEST_F(SupervisedUserWhitelistInstallerTest, GetHashFromCrxId) {
-  {
-    std::string extension_id = "abcdefghijklmnopponmlkjihgfedcba";
-    ASSERT_EQ(extension_id, CrxIdToHashToCrxId(extension_id));
-  }
-
-  {
-    std::string extension_id = "aBcDeFgHiJkLmNoPpOnMlKjIhGfEdCbA";
-    ASSERT_EQ(base::ToLowerASCII(extension_id),
-              CrxIdToHashToCrxId(extension_id));
-  }
-
-  {
-    std::string extension_id = crx_file::id_util::GenerateId("Moose");
-    ASSERT_EQ(extension_id, CrxIdToHashToCrxId(extension_id));
-  }
-}
-
-TEST_F(SupervisedUserWhitelistInstallerTest, InstallNewWhitelist) {
-  base::RunLoop registration_run_loop;
-  component_update_service_.set_registration_callback(
-      registration_run_loop.QuitClosure());
-
-  WhitelistLoadObserver observer(installer_.get());
-  installer_->RegisterWhitelist(kClientId, kCrxId, kName);
-  registration_run_loop.Run();
-
-  ASSERT_NO_FATAL_FAILURE(CheckRegisteredComponent("0.0.0.0"));
-  EXPECT_TRUE(component_update_service_.on_demand_update_called());
-
-  // Registering the same whitelist for another client should not do anything.
-  installer_->RegisterWhitelist(kOtherClientId, kCrxId, kName);
-
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-  base::FilePath unpacked_path = temp_dir.GetPath();
-  ASSERT_NO_FATAL_FAILURE(PrepareWhitelistDirectory(unpacked_path));
-
-  const CrxComponent* component =
-      component_update_service_.registered_component();
-  ASSERT_TRUE(component);
-
-  // The lambda function argument below is called by the ComponentInstaller
-  // implementation of the SupervisedUserWhitelistInstaller. Quit the observer
-  // in case of errors to allow the test to continue, since the component
-  // installer only calls |ComponentReady| if the install of the component
-  // has succeeded.
-  component->installer->Install(
-      unpacked_path, std::string(), nullptr, base::DoNothing(),
-      base::BindOnce(
-          [](WhitelistLoadObserver* observer,
-             const update_client::CrxInstaller::Result& result) {
-            EXPECT_EQ(0, result.error);
-            EXPECT_EQ(0, result.extended_error);
-            if (result.error)
-              observer->Quit();
-          },
-          &observer));
-
-  content::RunAllTasksUntilIdle();
-
-  observer.Wait();
-  EXPECT_EQ(whitelist_path_.value(), observer.whitelist_path().value());
-  EXPECT_EQ(large_icon_path_.value(), observer.large_icon_path().value());
-
-  std::string whitelist_contents;
-  ASSERT_TRUE(base::ReadFileToString(whitelist_path_, &whitelist_contents));
-
-  // The actual file contents don't have to be equal, but the parsed values
-  // should be.
-  EXPECT_TRUE(
-      base::JSONReader::ReadDeprecated(kWhitelistContents)
-          ->Equals(base::JSONReader::ReadDeprecated(whitelist_contents).get()))
-      << kWhitelistContents << " vs. " << whitelist_contents;
-
-  EXPECT_EQ(JsonToString(pref_),
-            JsonToString(*local_state_.GetDictionary(
-                prefs::kRegisteredSupervisedUserWhitelists)));
-}
-
-TEST_F(SupervisedUserWhitelistInstallerTest,
-       RegisterAndUninstallExistingWhitelist) {
-  ASSERT_TRUE(base::CreateDirectory(whitelist_version_directory_));
-  ASSERT_NO_FATAL_FAILURE(
-      PrepareWhitelistDirectory(whitelist_version_directory_));
-  ASSERT_TRUE(base::CreateDirectory(installed_whitelist_directory_));
-  ASSERT_NO_FATAL_FAILURE(PrepareWhitelistFile(whitelist_path_));
-
-  // Create another whitelist directory, with an ID that is not registered.
-  base::FilePath other_directory =
-      whitelist_base_directory_.AppendASCII("paobncmdlekfjgihhigjfkeldmcnboap");
-  ASSERT_TRUE(base::CreateDirectory(other_directory));
-  ASSERT_NO_FATAL_FAILURE(PrepareWhitelistDirectory(other_directory));
-
-  // Create a directory that is not a valid whitelist directory.
-  base::FilePath non_whitelist_directory =
-      whitelist_base_directory_.AppendASCII("Not a whitelist");
-  ASSERT_TRUE(base::CreateDirectory(non_whitelist_directory));
-
-  RegisterExistingComponents();
-
-  ASSERT_NO_FATAL_FAILURE(CheckRegisteredComponent(kVersion));
-  EXPECT_FALSE(component_update_service_.on_demand_update_called());
-
-  // Check that unregistered whitelists have been removed:
-  // The registered whitelist directory should still exist.
-  EXPECT_TRUE(base::DirectoryExists(whitelist_directory_));
-
-  // The other directory should be gone.
-  EXPECT_FALSE(base::DirectoryExists(other_directory));
-
-  // The non-whitelist directory should still exist as well.
-  EXPECT_TRUE(base::DirectoryExists(non_whitelist_directory));
-
-  // Unregistering for the first client should do nothing.
-  {
-    base::RunLoop run_loop;
-    installer_->UnregisterWhitelist(kClientId, kCrxId);
-    content::RunAllTasksUntilIdle();
-    run_loop.RunUntilIdle();
-  }
-  EXPECT_TRUE(component_update_service_.registered_component());
-  EXPECT_TRUE(base::DirectoryExists(whitelist_version_directory_));
-  EXPECT_TRUE(base::PathExists(whitelist_path_));
-
-  // Unregistering for the second client should uninstall the whitelist.
-  {
-    base::RunLoop run_loop;
-
-    // This does the same thing in our case as calling UnregisterWhitelist(),
-    // but it exercises a different code path.
-    profile_attributes_storage()->RemoveProfile(GetProfilePath(kOtherClientId));
-    content::RunAllTasksUntilIdle();
-    run_loop.RunUntilIdle();
-  }
-  EXPECT_FALSE(component_update_service_.registered_component());
-  EXPECT_FALSE(base::DirectoryExists(whitelist_directory_));
-  EXPECT_FALSE(base::PathExists(whitelist_path_));
-}
-
-}  // namespace component_updater
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/MediaStoreHelper.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/MediaStoreHelper.java
index ed24456..65f2882 100644
--- a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/MediaStoreHelper.java
+++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/MediaStoreHelper.java
@@ -4,12 +4,12 @@
 
 package org.chromium.chrome.browser.download;
 
+import android.os.Build;
 import android.provider.MediaStore;
 import android.text.TextUtils;
 
 import androidx.annotation.NonNull;
 
-import org.chromium.base.BuildInfo;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.task.AsyncTask;
@@ -37,7 +37,7 @@
     public static void addImageToGalleryOnSDCard(String filePath, String mimeType) {
         // TODO(xingliu): Support Android Q when we have available device with SD card slot.
         if (TextUtils.isEmpty(filePath) || mimeType == null || !mimeType.startsWith("image/")
-                || BuildInfo.isAtLeastQ()) {
+                || Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
             return;
         }
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index ed24e90..0ea7dedc 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1240,7 +1240,7 @@
   {
     "name": "enable-auto-select",
     "owners": ["croissant-eng"],
-    "expiry_milestone": 88
+    "expiry_milestone": 90
   },
   {
     "name": "enable-autofill-account-wallet-storage",
@@ -3370,7 +3370,7 @@
   {
     "name": "new-content-suggestions-feed",
     "owners": [ "adamta", "sczs", "gogerald", "bling-flags@google.com" ],
-    "expiry_milestone": 89
+    "expiry_milestone": 90
   },
   {
     "name": "new-shortcut-mapping",
@@ -4059,6 +4059,11 @@
     "expiry_milestone": 90
   },
   {
+    "name": "refactored-ntp",
+    "owners": [ "adamta", "sczs" ],
+    "expiry_milestone": 90
+  },
+  {
     "name": "related-searches",
     "owners": [ "ayman", "donnd" ],
     "expiry_milestone": 89
diff --git a/chrome/browser/nearby_sharing/nearby_share_delegate_impl.cc b/chrome/browser/nearby_sharing/nearby_share_delegate_impl.cc
index 720f5b7..365d40f4 100644
--- a/chrome/browser/nearby_sharing/nearby_share_delegate_impl.cc
+++ b/chrome/browser/nearby_sharing/nearby_share_delegate_impl.cc
@@ -59,6 +59,10 @@
   return nearby_share_service_ && nearby_share_service_->IsInHighVisibility();
 }
 
+bool NearbyShareDelegateImpl::IsEnableHighVisibilityRequestActive() const {
+  return is_enable_high_visibility_request_active_;
+}
+
 base::TimeTicks NearbyShareDelegateImpl::HighVisibilityShutoffTime() const {
   return shutoff_time_;
 }
@@ -72,6 +76,8 @@
   if (!nearby_share_service_->GetSettings()->GetEnabled()) {
     onboarding_wait_timer_.Reset();
   }
+
+  is_enable_high_visibility_request_active_ = true;
 }
 
 void NearbyShareDelegateImpl::DisableHighVisibility() {
@@ -127,6 +133,8 @@
 }
 
 void NearbyShareDelegateImpl::OnHighVisibilityChanged(bool high_visibility_on) {
+  is_enable_high_visibility_request_active_ = false;
+
   if (high_visibility_on) {
     shutoff_time_ = base::TimeTicks::Now() + kShutoffTimeout;
     shutoff_timer_.Reset();
diff --git a/chrome/browser/nearby_sharing/nearby_share_delegate_impl.h b/chrome/browser/nearby_sharing/nearby_share_delegate_impl.h
index 7c6cb86b..27fe748 100644
--- a/chrome/browser/nearby_sharing/nearby_share_delegate_impl.h
+++ b/chrome/browser/nearby_sharing/nearby_share_delegate_impl.h
@@ -56,6 +56,7 @@
   // ash::NearbyShareDelegate
   bool IsPodButtonVisible() override;
   bool IsHighVisibilityOn() override;
+  bool IsEnableHighVisibilityRequestActive() const override;
   base::TimeTicks HighVisibilityShutoffTime() const override;
   void EnableHighVisibility() override;
   void DisableHighVisibility() override;
@@ -92,6 +93,10 @@
   NearbySharingService* nearby_share_service_ = nullptr;
   std::unique_ptr<SettingsOpener> settings_opener_;
 
+  // Track if there is an outstanding request to enable high visibility. Reset
+  // to false once the request finishes (via OnHighVisibilityChanged());
+  bool is_enable_high_visibility_request_active_ = false;
+
   // This timer is used to automatically turn off high visibility after a
   // timeout.
   base::RetainingOneShotTimer shutoff_timer_;
diff --git a/chrome/browser/nearby_sharing/nearby_share_delegate_impl_unittest.cc b/chrome/browser/nearby_sharing/nearby_share_delegate_impl_unittest.cc
index 7bd556b..bf76c008 100644
--- a/chrome/browser/nearby_sharing/nearby_share_delegate_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_share_delegate_impl_unittest.cc
@@ -139,6 +139,18 @@
   SetHighVisibilityOn(false);
 }
 
+TEST_F(NearbyShareDelegateImplTest, TestIsEnableHighVisibilityRequestActive) {
+  settings()->SetEnabled(true);
+
+  EXPECT_CALL(*settings_opener_, ShowSettingsPage(_));
+  EXPECT_CALL(controller_, HighVisibilityEnabledChanged(true));
+
+  delegate_.EnableHighVisibility();
+  EXPECT_TRUE(delegate_.IsEnableHighVisibilityRequestActive());
+  SetHighVisibilityOn(true);
+  EXPECT_FALSE(delegate_.IsEnableHighVisibilityRequestActive());
+}
+
 TEST_F(NearbyShareDelegateImplTest, ShowOnboardingAndTurnOnHighVisibility) {
   settings()->SetEnabled(false);
 
diff --git a/chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.cc b/chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.cc
index d872e46..8348dfb 100644
--- a/chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.cc
+++ b/chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.cc
@@ -93,17 +93,8 @@
 
 std::vector<ntp_tiles::MostVisitedSitesSupervisor::Allowlist>
 SupervisorBridge::GetAllowlists() {
-  std::vector<MostVisitedSitesSupervisor::Allowlist> results;
-  SupervisedUserService* supervised_user_service =
-      SupervisedUserServiceFactory::GetForProfile(profile_);
-  for (const auto& allowlist : supervised_user_service->allowlists()) {
-    results.emplace_back(Allowlist{
-        allowlist->title(),
-        allowlist->entry_point(),
-        allowlist->large_icon_path(),
-    });
-  }
-  return results;
+  // TODO(crbug.com/1149782): Remove allowlists from New Tab Page.
+  return {};
 }
 
 bool SupervisorBridge::IsChildProfile() {
diff --git a/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl.cc b/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl.cc
index 5a69fe60..3fd4d8f 100644
--- a/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl.cc
+++ b/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl.cc
@@ -37,7 +37,8 @@
 }
 
 bool ShouldUseDownloadsCollection() {
-  return base::android::BuildInfo::GetInstance()->is_at_least_q();
+  return base::android::BuildInfo::GetInstance()->sdk_int() >=
+         base::android::SDK_VERSION_Q;
 }
 
 // Helper function to do the move and register synchronously. Make sure this is
diff --git a/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl_unittest.cc b/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl_unittest.cc
index f69e808..d3ed241 100644
--- a/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl_unittest.cc
+++ b/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl_unittest.cc
@@ -145,7 +145,8 @@
   EXPECT_EQ(kDownloadId, publish_archive_result().id.download_id);
 
   // The file move should not happen on Android Q and later.
-  if (!base::android::BuildInfo::GetInstance()->is_at_least_q()) {
+  if (base::android::BuildInfo::GetInstance()->sdk_int() <
+      base::android::SDK_VERSION_Q) {
     // Check there is a file in the new location.
     EXPECT_TRUE(public_archive_dir_path().IsParent(
         publish_archive_result().id.new_file_path));
diff --git a/chrome/browser/page_load_metrics/observers/back_forward_cache_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/back_forward_cache_page_load_metrics_observer_browsertest.cc
index 6fd660d0..df0ff27e 100644
--- a/chrome/browser/page_load_metrics/observers/back_forward_cache_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/back_forward_cache_page_load_metrics_observer_browsertest.cc
@@ -169,8 +169,17 @@
   }
 }
 
+#if defined(OS_MAC) && defined(ARCH_CPU_ARM64)
+// Bulk-disabled for arm64 bot stabilization: https://crbug.com/1154345
+#define MAYBE_FirstPaintAfterBackForwardCacheRestoreBackground \
+  DISABLED_FirstPaintAfterBackForwardCacheRestoreBackground
+#else
+#define MAYBE_FirstPaintAfterBackForwardCacheRestoreBackground \
+  FirstPaintAfterBackForwardCacheRestoreBackground
+#endif
+
 IN_PROC_BROWSER_TEST_F(BackForwardCachePageLoadMetricsObserverBrowserTest,
-                       FirstPaintAfterBackForwardCacheRestoreBackground) {
+                       MAYBE_FirstPaintAfterBackForwardCacheRestoreBackground) {
   Start();
   GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
   GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
@@ -218,8 +227,18 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(BackForwardCachePageLoadMetricsObserverBrowserTest,
-                       FirstInputDelayAfterBackForwardCacheRestoreBackground) {
+#if defined(OS_MAC) && defined(ARCH_CPU_ARM64)
+// Bulk-disabled for arm64 bot stabilization: https://crbug.com/1154345
+#define MAYBE_FirstInputDelayAfterBackForwardCacheRestoreBackground \
+  DISABLED_FirstInputDelayAfterBackForwardCacheRestoreBackground
+#else
+#define MAYBE_FirstInputDelayAfterBackForwardCacheRestoreBackground \
+  FirstInputDelayAfterBackForwardCacheRestoreBackground
+#endif
+
+IN_PROC_BROWSER_TEST_F(
+    BackForwardCachePageLoadMetricsObserverBrowserTest,
+    MAYBE_FirstInputDelayAfterBackForwardCacheRestoreBackground) {
   Start();
   GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
   GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 271e6b7..88264e1 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -1849,6 +1849,13 @@
           extensions::pref_names::kNativeMessagingBlocklist, true));
   handlers->AddHandler(
       std::make_unique<AutoLaunchProtocolsPolicyHandler>(chrome_schema));
+
+  handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>(
+      key::kProfilePickerOnStartupAvailability,
+      prefs::kBrowserProfilePickerAvailabilityOnStartup, chrome_schema,
+      SCHEMA_ALLOW_UNKNOWN,
+      SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED,
+      SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED));
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OS_ANDROID)
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/policy/messaging_layer/public/report_client.cc b/chrome/browser/policy/messaging_layer/public/report_client.cc
index e7ed7fa..9419e56 100644
--- a/chrome/browser/policy/messaging_layer/public/report_client.cc
+++ b/chrome/browser/policy/messaging_layer/public/report_client.cc
@@ -16,6 +16,7 @@
 #include "base/path_service.h"
 #include "base/strings/strcat.h"
 #include "base/task/post_task.h"
+#include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/net/system_network_context_manager.h"
@@ -27,11 +28,15 @@
 #include "chrome/browser/policy/messaging_layer/util/status_macros.h"
 #include "chrome/browser/policy/messaging_layer/util/statusor.h"
 #include "chrome/browser/policy/messaging_layer/util/task_runner_context.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/common/chrome_paths.h"
 #include "components/enterprise/browser/controller/browser_dm_token_storage.h"
 #include "components/policy/core/common/cloud/cloud_policy_client_registration_helper.h"
+#include "components/policy/core/common/cloud/cloud_policy_manager.h"
 #include "components/policy/core/common/cloud/device_management_service.h"
+#include "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
+#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
 #include "components/policy/proto/record.pb.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -52,96 +57,44 @@
 
 namespace {
 
-// policy::CloudPolicyClient is needed by the UploadClient, but is built in two
-// different ways for ChromeOS and non-ChromeOS browsers.
+// policy::CloudPolicyClient is needed by the UploadClient, but is retrieved in
+// two different ways for ChromeOS and non-ChromeOS browsers.
 // NOT THREAD SAFE - these functions must be called on the main thread.
 // TODO(chromium:1078512) Wrap CloudPolicyClient in a new object so that its
-// methods, constructor, and destructor are accessed on the correct thread.
+// methods and retrieval are accessed on the correct thread.
+void GetCloudPolicyClient(
+    base::OnceCallback<void(StatusOr<policy::CloudPolicyClient*>)>
+        get_client_cb) {
 #ifdef OS_CHROMEOS
-void BuildCloudPolicyClient(
-    base::OnceCallback<
-        void(StatusOr<std::unique_ptr<policy::CloudPolicyClient>>)> build_cb) {
-  auto* const browser_policy_connector =
-      g_browser_process->browser_policy_connector();
-  if (!browser_policy_connector) {
-    std::move(build_cb).Run(
-        Status(error::FAILED_PRECONDITION, "This is not a managed device."));
-    return;
-  }
-
-  policy::DeviceManagementService* const device_management_service =
-      browser_policy_connector->device_management_service();
-  if (!device_management_service) {
-    std::move(build_cb).Run(
-        Status(error::FAILED_PRECONDITION, "This is not a managed device."));
-    return;
-  }
-
-  if (!chromeos::DeviceSettingsService::IsInitialized()) {
-    chromeos::DeviceSettingsService::Initialize();
-  }
-  const enterprise_management::PolicyData* policy_data =
-      chromeos::DeviceSettingsService::Get()->policy_data();
-  if (!policy_data || !policy_data->has_request_token() ||
-      !policy_data->has_device_id()) {
-    std::move(build_cb).Run(
-        Status(error::UNAVAILABLE, "PolicyData is unavailable."));
-    return;
-  }
-
-  scoped_refptr<network::SharedURLLoaderFactory>
-      signin_profile_url_loader_factory =
-          g_browser_process->system_network_context_manager()
-              ->GetSharedURLLoaderFactory();
-
-  auto* user_manager_ptr = g_browser_process->platform_part()->user_manager();
-  auto* primary_user = user_manager_ptr->GetPrimaryUser();
-  auto dm_token_getter = chromeos::GetDeviceDMTokenForUserPolicyGetter(
-      primary_user->GetAccountId());
-
-  auto client = std::make_unique<policy::CloudPolicyClient>(
-      device_management_service, signin_profile_url_loader_factory,
-      dm_token_getter);
-
-  std::vector<std::string> affiliation_ids(
-      policy_data->user_affiliation_ids().begin(),
-      policy_data->user_affiliation_ids().end());
-  client->SetupRegistration(policy_data->request_token(),
-                            policy_data->device_id(), affiliation_ids);
-  if (!client->is_registered()) {
-    std::move(build_cb).Run(
-        Status(error::UNAVAILABLE, "Unable to start CloudPolicyClient."));
-    return;
-  }
-  std::move(build_cb).Run(std::move(client));
-}
+  policy::CloudPolicyManager* cloud_policy_manager =
+      g_browser_process->platform_part()
+          ->browser_policy_connector_chromeos()
+          ->GetDeviceCloudPolicyManager();
+#elif defined(OS_ANDROID)
+  // Android doesn't have access to a device level CloudPolicyClient, so get the
+  // PrimaryUserProfile CloudPolicyClient.
+  policy::CloudPolicyManager* cloud_policy_manager =
+      ProfileManager::GetPrimaryUserProfile()->GetUserCloudPolicyManager();
 #else
-void BuildCloudPolicyClient(
-    base::OnceCallback<
-        void(StatusOr<std::unique_ptr<policy::CloudPolicyClient>>)> build_cb) {
-  policy::DeviceManagementService* const device_management_service =
+  policy::CloudPolicyManager* cloud_policy_manager =
       g_browser_process->browser_policy_connector()
-          ->device_management_service();
-
-  scoped_refptr<network::SharedURLLoaderFactory>
-      signin_profile_url_loader_factory =
-          g_browser_process->system_network_context_manager()
-              ->GetSharedURLLoaderFactory();
-
-  auto client = std::make_unique<policy::CloudPolicyClient>(
-      device_management_service, signin_profile_url_loader_factory,
-      policy::CloudPolicyClient::DeviceDMTokenCallback());
-
-  policy::DMToken browser_dm_token =
-      policy::BrowserDMTokenStorage::Get()->RetrieveDMToken();
-  std::string client_id =
-      policy::BrowserDMTokenStorage::Get()->RetrieveClientId();
-
-  client->SetupRegistration(browser_dm_token.value(), client_id,
-                            std::vector<std::string>());
-  std::move(build_cb).Run(std::move(client));
-}
+          ->machine_level_user_cloud_policy_manager();
 #endif
+  if (cloud_policy_manager == nullptr) {
+    std::move(get_client_cb)
+        .Run(
+            Status(error::FAILED_PRECONDITION, "This is not a managed device"));
+    return;
+  }
+  auto* cloud_policy_client = cloud_policy_manager->core()->client();
+  if (cloud_policy_client == nullptr) {
+    std::move(get_client_cb)
+        .Run(Status(error::FAILED_PRECONDITION,
+                    "Cloud Policy Client is not available"));
+    return;
+  }
+  std::move(get_client_cb).Run(cloud_policy_client);
+}
 
 const base::FilePath::CharType kReportingDirectory[] =
     FILE_PATH_LITERAL("reporting");
@@ -244,17 +197,7 @@
 }
 
 ReportingClient::Configuration::Configuration() = default;
-ReportingClient::Configuration::~Configuration() {
-  if (cloud_policy_client) {
-    base::PostTask(
-        FROM_HERE, {content::BrowserThread::UI},
-        base::BindOnce(
-            [](std::unique_ptr<policy::CloudPolicyClient> cloud_policy_client) {
-              cloud_policy_client.reset();
-            },
-            std::move(cloud_policy_client)));
-  }
-}
+ReportingClient::Configuration::~Configuration() = default;
 
 ReportingClient::InitializationStateTracker::InitializationStateTracker()
     : sequenced_task_runner_(base::ThreadPool::CreateSequencedTaskRunner({})) {}
@@ -360,7 +303,7 @@
 }
 
 ReportingClient::InitializingContext::InitializingContext(
-    BuildCloudPolicyClientCallback build_client_cb,
+    GetCloudPolicyClientCallback get_client_cb,
     Storage::StartUploadCb start_upload_cb,
     UpdateConfigurationCallback update_config_cb,
     InitCompleteCallback init_complete_cb,
@@ -369,7 +312,7 @@
     scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner)
     : TaskRunnerContext<Status>(std::move(init_complete_cb),
                                 sequenced_task_runner),
-      build_client_cb_(std::move(build_client_cb)),
+      get_client_cb_(std::move(get_client_cb)),
       start_upload_cb_(std::move(start_upload_cb)),
       update_config_cb_(std::move(update_config_cb)),
       init_state_tracker_(init_state_tracker),
@@ -408,20 +351,19 @@
   base::PostTask(
       FROM_HERE, {content::BrowserThread::UI},
       base::BindOnce(
-          [](BuildCloudPolicyClientCallback build_client_cb,
-             base::OnceCallback<void(
-                 StatusOr<std::unique_ptr<policy::CloudPolicyClient>>)>
+          [](GetCloudPolicyClientCallback get_client_cb,
+             base::OnceCallback<void(StatusOr<policy::CloudPolicyClient*>)>
                  on_client_configured) {
-            std::move(build_client_cb).Run(std::move(on_client_configured));
+            std::move(get_client_cb).Run(std::move(on_client_configured));
           },
-          std::move(build_client_cb_),
+          std::move(get_client_cb_),
           base::BindOnce(&ReportingClient::InitializingContext::
                              OnCloudPolicyClientConfigured,
                          base::Unretained(this))));
 }
 
 void ReportingClient::InitializingContext::OnCloudPolicyClientConfigured(
-    StatusOr<std::unique_ptr<policy::CloudPolicyClient>> client_result) {
+    StatusOr<policy::CloudPolicyClient*> client_result) {
   if (!client_result.ok()) {
     Complete(Status(error::FAILED_PRECONDITION,
                     base::StrCat({"Unable to build CloudPolicyClient: ",
@@ -511,7 +453,7 @@
     : create_request_queue_(SharedQueue<CreateReportQueueRequest>::Create()),
       init_state_tracker_(
           ReportingClient::InitializationStateTracker::Create()),
-      build_cloud_policy_client_cb_(base::BindOnce(&BuildCloudPolicyClient)) {}
+      build_cloud_policy_client_cb_(base::BindOnce(&GetCloudPolicyClient)) {}
 
 ReportingClient::~ReportingClient() = default;
 
@@ -633,17 +575,15 @@
 }
 
 ReportingClient::TestEnvironment::TestEnvironment(
-    std::unique_ptr<policy::CloudPolicyClient> client)
+    policy::CloudPolicyClient* client)
     : saved_build_cloud_policy_client_cb_(std::move(
           ReportingClient::GetInstance()->build_cloud_policy_client_cb_)) {
-  ReportingClient::GetInstance()
-      ->build_cloud_policy_client_cb_ = base::BindOnce(
-      [](std::unique_ptr<policy::CloudPolicyClient> client,
-         base::OnceCallback<void(
-             StatusOr<std::unique_ptr<policy::CloudPolicyClient>>)> build_cb) {
-        std::move(build_cb).Run(std::move(client));
-      },
-      std::move(client));
+  ReportingClient::GetInstance()->build_cloud_policy_client_cb_ =
+      base::BindOnce(
+          [](policy::CloudPolicyClient* client,
+             base::OnceCallback<void(StatusOr<policy::CloudPolicyClient*>)>
+                 build_cb) { std::move(build_cb).Run(std::move(client)); },
+          std::move(client));
 }
 
 ReportingClient::TestEnvironment::~TestEnvironment() {
diff --git a/chrome/browser/policy/messaging_layer/public/report_client.h b/chrome/browser/policy/messaging_layer/public/report_client.h
index 72664dcf..bdfd055 100644
--- a/chrome/browser/policy/messaging_layer/public/report_client.h
+++ b/chrome/browser/policy/messaging_layer/public/report_client.h
@@ -70,7 +70,9 @@
     Configuration();
     ~Configuration();
 
-    std::unique_ptr<policy::CloudPolicyClient> cloud_policy_client;
+    // TODO(chromium:1078512) Passing around a raw pointer is unsafe. Wrap
+    // CloudPolicyClient and guard access.
+    policy::CloudPolicyClient* cloud_policy_client;
     scoped_refptr<StorageModule> storage;
   };
 
@@ -82,9 +84,8 @@
   using UpdateConfigurationCallback =
       base::OnceCallback<void(std::unique_ptr<Configuration>,
                               base::OnceCallback<void(Status)>)>;
-  using BuildCloudPolicyClientCallback = base::OnceCallback<void(
-      base::OnceCallback<void(
-          StatusOr<std::unique_ptr<policy::CloudPolicyClient>>)>)>;
+  using GetCloudPolicyClientCallback = base::OnceCallback<void(
+      base::OnceCallback<void(StatusOr<policy::CloudPolicyClient*>)>)>;
 
   using InitCompleteCallback = base::OnceCallback<void(Status)>;
 
@@ -138,7 +139,7 @@
   class InitializingContext : public TaskRunnerContext<Status> {
    public:
     InitializingContext(
-        BuildCloudPolicyClientCallback build_client_cb,
+        GetCloudPolicyClientCallback get_client_cb,
         Storage::StartUploadCb start_upload_cb,
         UpdateConfigurationCallback update_config_cb,
         InitCompleteCallback init_complete_cb,
@@ -156,7 +157,7 @@
 
     void ConfigureCloudPolicyClient();
     void OnCloudPolicyClientConfigured(
-        StatusOr<std::unique_ptr<policy::CloudPolicyClient>> client_result);
+        StatusOr<policy::CloudPolicyClient*> client_result);
 
     // ConfigureStorageModule will build a StorageModule and add it to the
     // |client_config_|.
@@ -173,7 +174,7 @@
     // Complete calls response with |client_config_|
     void Complete(Status status);
 
-    BuildCloudPolicyClientCallback build_client_cb_;
+    GetCloudPolicyClientCallback get_client_cb_;
     Storage::StartUploadCb start_upload_cb_;
     UpdateConfigurationCallback update_config_cb_;
     scoped_refptr<InitializationStateTracker> init_state_tracker_;
@@ -186,13 +187,13 @@
   // builder to return given client and resets it when destructed.
   class TestEnvironment {
    public:
-    explicit TestEnvironment(std::unique_ptr<policy::CloudPolicyClient> client);
+    explicit TestEnvironment(policy::CloudPolicyClient* client);
     TestEnvironment(const TestEnvironment& other) = delete;
     TestEnvironment& operator=(const TestEnvironment& other) = delete;
     ~TestEnvironment();
 
    private:
-    ReportingClient::BuildCloudPolicyClientCallback
+    ReportingClient::GetCloudPolicyClientCallback
         saved_build_cloud_policy_client_cb_;
   };
 
@@ -288,7 +289,7 @@
   // initializing.
   scoped_refptr<SharedQueue<CreateReportQueueRequest>> create_request_queue_;
   scoped_refptr<InitializationStateTracker> init_state_tracker_;
-  BuildCloudPolicyClientCallback build_cloud_policy_client_cb_;
+  GetCloudPolicyClientCallback build_cloud_policy_client_cb_;
 
   scoped_refptr<StorageModule> storage_;
   std::unique_ptr<UploadClient> upload_client_;
diff --git a/chrome/browser/policy/messaging_layer/public/report_client_unittest.cc b/chrome/browser/policy/messaging_layer/public/report_client_unittest.cc
index ad2d6e2..04641d3 100644
--- a/chrome/browser/policy/messaging_layer/public/report_client_unittest.cc
+++ b/chrome/browser/policy/messaging_layer/public/report_client_unittest.cc
@@ -89,11 +89,11 @@
         std::move(mock_user_manager));
 #endif  // OS_CHROMEOS
     // Provide a mock cloud policy client.
-    auto client = std::make_unique<policy::MockCloudPolicyClient>();
-    client->SetDMToken(
+    auto client_ = std::make_unique<policy::MockCloudPolicyClient>();
+    client_->SetDMToken(
         policy::DMToken::CreateValidTokenForTesting("FAKE_DM_TOKEN").value());
     test_reporting_ =
-        std::make_unique<ReportingClient::TestEnvironment>(std::move(client));
+        std::make_unique<ReportingClient::TestEnvironment>(client_.get());
   }
 
   void TearDown() override {
@@ -110,6 +110,7 @@
   std::unique_ptr<TestingProfile> profile_;
   std::unique_ptr<user_manager::ScopedUserManager> user_manager_;
 #endif  // OS_CHROMEOS
+  std::unique_ptr<policy::MockCloudPolicyClient> client_;
   const DMToken dm_token_ = DMToken::CreateValidTokenForTesting("TOKEN");
   const Destination destination_ = Destination::UPLOAD_EVENTS;
   ReportQueueConfiguration::PolicyCheckCallback policy_checker_callback_ =
diff --git a/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service.cc b/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service.cc
index 51b26e9..4f0314b 100644
--- a/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service.cc
+++ b/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service.cc
@@ -169,7 +169,7 @@
 }
 
 void DmServerUploadService::Create(
-    std::unique_ptr<policy::CloudPolicyClient> client,
+    policy::CloudPolicyClient* client,
     ReportSuccessfulUploadCallback report_upload_success_cb,
     base::OnceCallback<void(StatusOr<std::unique_ptr<DmServerUploadService>>)>
         created_cb) {
@@ -185,23 +185,13 @@
 }
 
 DmServerUploadService::DmServerUploadService(
-    std::unique_ptr<policy::CloudPolicyClient> client,
+    policy::CloudPolicyClient* client,
     ReportSuccessfulUploadCallback upload_cb)
     : client_(std::move(client)),
       upload_cb_(upload_cb),
       sequenced_task_runner_(base::ThreadPool::CreateSequencedTaskRunner({})) {}
 
-DmServerUploadService::~DmServerUploadService() {
-  if (client_) {
-    base::PostTask(
-        FROM_HERE, {content::BrowserThread::UI},
-        base::BindOnce(
-            [](std::unique_ptr<policy::CloudPolicyClient> cloud_policy_client) {
-              cloud_policy_client.reset();
-            },
-            std::move(client_)));
-  }
-}
+DmServerUploadService::~DmServerUploadService() = default;
 
 Status DmServerUploadService::EnqueueUpload(
     std::unique_ptr<std::vector<EncryptedRecord>> records) {
@@ -239,7 +229,7 @@
 }
 
 CloudPolicyClient* DmServerUploadService::GetClient() {
-  return client_.get();
+  return client_;
 }
 
 }  // namespace reporting
diff --git a/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service.h b/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service.h
index 98ff270..07bf40b 100644
--- a/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service.h
+++ b/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service.h
@@ -131,14 +131,13 @@
   // Will asynchronously create a DMServerUploadService with handlers.
   // On successful completion will call |created_cb| with DMServerUploadService.
   // If |client| is null, will call |created_cb| with error::INVALID_ARGUMENT.
-  // If any handlers fail to create, or the policy::CloudPolicyClient is null,
-  // will call |created_cb| with error::UNAVAILABLE.
+  // If any handlers fail to create, will call |created_cb| with
+  // error::UNAVAILABLE.
   //
-  // |client| must not be null.
   // |report_upload_success_cb| should report back to the holder of the created
   // object whenever a record set is successfully uploaded.
   static void Create(
-      std::unique_ptr<policy::CloudPolicyClient> client,
+      policy::CloudPolicyClient* client,
       ReportSuccessfulUploadCallback report_upload_success_cb,
       base::OnceCallback<void(StatusOr<std::unique_ptr<DmServerUploadService>>)>
           created_cb);
@@ -147,7 +146,7 @@
   Status EnqueueUpload(std::unique_ptr<std::vector<EncryptedRecord>> record);
 
  private:
-  DmServerUploadService(std::unique_ptr<policy::CloudPolicyClient> client,
+  DmServerUploadService(policy::CloudPolicyClient* client,
                         ReportSuccessfulUploadCallback completion_cb);
 
   static void InitRecordHandler(
@@ -159,7 +158,7 @@
 
   policy::CloudPolicyClient* GetClient();
 
-  std::unique_ptr<policy::CloudPolicyClient> client_;
+  policy::CloudPolicyClient* client_;
   ReportSuccessfulUploadCallback upload_cb_;
   std::unique_ptr<RecordHandler> handler_;
 
diff --git a/chrome/browser/policy/messaging_layer/upload/upload_client.cc b/chrome/browser/policy/messaging_layer/upload/upload_client.cc
index 7dbc9fe..0deaa9f 100644
--- a/chrome/browser/policy/messaging_layer/upload/upload_client.cc
+++ b/chrome/browser/policy/messaging_layer/upload/upload_client.cc
@@ -18,7 +18,7 @@
 
 // static
 void UploadClient::Create(
-    std::unique_ptr<policy::CloudPolicyClient> cloud_policy_client,
+    policy::CloudPolicyClient* cloud_policy_client,
     ReportSuccessfulUploadCallback report_upload_success_cb,
     base::OnceCallback<void(StatusOr<std::unique_ptr<UploadClient>>)>
         created_cb) {
diff --git a/chrome/browser/policy/messaging_layer/upload/upload_client.h b/chrome/browser/policy/messaging_layer/upload/upload_client.h
index 969a6c82..afeade24 100644
--- a/chrome/browser/policy/messaging_layer/upload/upload_client.h
+++ b/chrome/browser/policy/messaging_layer/upload/upload_client.h
@@ -27,7 +27,7 @@
       base::RepeatingCallback<void(SequencingInformation)>;
 
   static void Create(
-      std::unique_ptr<policy::CloudPolicyClient> cloud_policy_client,
+      policy::CloudPolicyClient* cloud_policy_client,
       ReportSuccessfulUploadCallback report_upload_success_cb,
       base::OnceCallback<void(StatusOr<std::unique_ptr<UploadClient>>)>
           created_cb);
diff --git a/chrome/browser/policy/messaging_layer/upload/upload_client_unittest.cc b/chrome/browser/policy/messaging_layer/upload/upload_client_unittest.cc
index 4b85cb7..723d8dc 100644
--- a/chrome/browser/policy/messaging_layer/upload/upload_client_unittest.cc
+++ b/chrome/browser/policy/messaging_layer/upload/upload_client_unittest.cc
@@ -212,7 +212,7 @@
           records->back().sequencing_information());
 
   TestEvent<StatusOr<std::unique_ptr<UploadClient>>> e;
-  UploadClient::Create(std::move(client), completion_cb, e.cb());
+  UploadClient::Create(client.get(), completion_cb, e.cb());
   StatusOr<std::unique_ptr<UploadClient>> upload_client_result = e.result();
   ASSERT_OK(upload_client_result) << upload_client_result.status();
 
diff --git a/chrome/browser/prefetch/search_prefetch/base_search_prefetch_request.cc b/chrome/browser/prefetch/search_prefetch/base_search_prefetch_request.cc
index eb9995aa..5d4882fa 100644
--- a/chrome/browser/prefetch/search_prefetch/base_search_prefetch_request.cc
+++ b/chrome/browser/prefetch/search_prefetch/base_search_prefetch_request.cc
@@ -4,18 +4,80 @@
 
 #include "chrome/browser/prefetch/search_prefetch/base_search_prefetch_request.h"
 
+#include <vector>
+
+#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/variations/net/variations_http_headers.h"
 #include "content/public/browser/client_hints.h"
 #include "content/public/browser/frame_accept_header.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/url_loader_throttles.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/common/content_constants.h"
 #include "net/base/load_flags.h"
+#include "net/cookies/site_for_cookies.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/mojom/fetch_api.mojom.h"
+#include "third_party/blink/public/common/loader/url_loader_throttle.h"
+#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
+#include "url/origin.h"
+
+namespace {
+
+// A custom URLLoaderThrottle delegate that is very sensitive. Anything that
+// would delay or cancel the request is treated the same, which would prevent
+// the prefetch request.
+class CheckForCancelledOrPausedDelegate
+    : public blink::URLLoaderThrottle::Delegate {
+ public:
+  CheckForCancelledOrPausedDelegate() = default;
+  ~CheckForCancelledOrPausedDelegate() override = default;
+
+  CheckForCancelledOrPausedDelegate(const CheckForCancelledOrPausedDelegate&) =
+      delete;
+  CheckForCancelledOrPausedDelegate& operator=(
+      const CheckForCancelledOrPausedDelegate&) = delete;
+
+  // URLLoaderThrottle::Delegate:
+  void CancelWithError(int error_code,
+                       base::StringPiece custom_reason) override {
+    cancelled_or_paused_ = true;
+  }
+
+  void Resume() override {}
+
+  void PauseReadingBodyFromNet() override { cancelled_or_paused_ = true; }
+
+  void RestartWithFlags(int additional_load_flags) override {
+    cancelled_or_paused_ = true;
+  }
+
+  void RestartWithURLResetAndFlags(int additional_load_flags) override {
+    cancelled_or_paused_ = true;
+  }
+
+  void RestartWithURLResetAndFlagsNow(int additional_load_flags) override {
+    cancelled_or_paused_ = true;
+  }
+
+  void RestartWithModifiedHeadersNow(
+      const net::HttpRequestHeaders& modified_headers) override {
+    cancelled_or_paused_ = true;
+  }
+
+  bool cancelled_or_paused() const { return cancelled_or_paused_; }
+
+ private:
+  bool cancelled_or_paused_ = false;
+};
+
+}  // namespace
 
 BaseSearchPrefetchRequest::BaseSearchPrefetchRequest(
     const GURL& prefetch_url,
@@ -25,7 +87,7 @@
 
 BaseSearchPrefetchRequest::~BaseSearchPrefetchRequest() = default;
 
-void BaseSearchPrefetchRequest::StartPrefetchRequest(Profile* profile) {
+bool BaseSearchPrefetchRequest::StartPrefetchRequest(Profile* profile) {
   net::NetworkTrafficAnnotationTag network_traffic_annotation =
       net::DefineNetworkTrafficAnnotation("search_prefetch_service", R"(
         semantics {
@@ -61,6 +123,8 @@
           }
         })");
 
+  url::Origin prefetch_origin = url::Origin::Create(prefetch_url_);
+
   auto resource_request = std::make_unique<network::ResourceRequest>();
   resource_request->load_flags |= net::LOAD_PREFETCH;
   resource_request->url = prefetch_url_;
@@ -70,8 +134,26 @@
   resource_request->report_raw_headers = true;
   resource_request->credentials_mode =
       network::mojom::CredentialsMode::kInclude;
-  variations::AppendVariationsHeaderUnknownSignedIn(
-      prefetch_url_, variations::InIncognito::kNo, resource_request.get());
+  resource_request->method = "GET";
+  resource_request->mode = network::mojom::RequestMode::kNavigate;
+  resource_request->site_for_cookies =
+      net::SiteForCookies::FromUrl(prefetch_url_);
+  resource_request->destination = network::mojom::RequestDestination::kDocument;
+  resource_request->resource_type =
+      static_cast<int>(blink::mojom::ResourceType::kMainFrame);
+  resource_request->trusted_params = network::ResourceRequest::TrustedParams();
+  // We don't handle redirects, so |kOther| makes sense here.
+  resource_request->trusted_params->isolation_info = net::IsolationInfo::Create(
+      net::IsolationInfo::RequestType::kOther, prefetch_origin, prefetch_origin,
+      resource_request->site_for_cookies);
+  resource_request->referrer_policy = net::ReferrerPolicy::NO_REFERRER;
+
+  // Tack an 'Upgrade-Insecure-Requests' header to outgoing navigational
+  // requests, as described in
+  // https://w3c.github.io/webappsec/specs/upgrade/#feature-detect
+  resource_request->headers.SetHeader("Upgrade-Insecure-Requests", "1");
+  resource_request->headers.SetHeader(net::HttpRequestHeaders::kUserAgent,
+                                      GetUserAgent());
   resource_request->headers.SetHeader(content::kCorsExemptPurposeHeaderName,
                                       "prefetch");
   resource_request->headers.SetHeader(
@@ -86,13 +168,37 @@
       profile->GetClientHintsControllerDelegate(),
       /*is_ua_override_on=*/false, js_enabled);
 
-  // TODO(ryansturm): Find other headers that may need to be set.
-  // https://crbug.com/1138648
+  // Before sending out the request, allow throttles to modify the request (not
+  // the URL). The rest of the URL Loader throttle calls are captured in the
+  // navigation stack. Headers can be added by throttles at this point, which we
+  // want to capture.
+  auto wc_getter =
+      base::BindRepeating([]() -> content::WebContents* { return nullptr; });
+  std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles =
+      content::CreateContentBrowserURLLoaderThrottles(
+          *resource_request, profile, std::move(wc_getter),
+          /*navigation_ui_data=*/nullptr,
+          content::RenderFrameHost::kNoFrameTreeNodeId);
+
+  bool should_defer = false;
+  for (auto& throttle : throttles) {
+    CheckForCancelledOrPausedDelegate cancel_or_pause_delegate;
+    throttle->set_delegate(&cancel_or_pause_delegate);
+    throttle->WillStartRequest(resource_request.get(), &should_defer);
+    // Make sure throttles are deleted before |cancel_or_pause_delegate| in case
+    // they call into the delegate in the destructor.
+    throttle.reset();
+    if (should_defer || resource_request->url != prefetch_url_ ||
+        cancel_or_pause_delegate.cancelled_or_paused()) {
+      return false;
+    }
+  }
 
   current_status_ = SearchPrefetchStatus::kInFlight;
 
   StartPrefetchRequestInternal(profile, std::move(resource_request),
                                network_traffic_annotation);
+  return true;
 }
 
 void BaseSearchPrefetchRequest::CancelPrefetch() {
diff --git a/chrome/browser/prefetch/search_prefetch/base_search_prefetch_request.h b/chrome/browser/prefetch/search_prefetch/base_search_prefetch_request.h
index 3316725..8abc563c 100644
--- a/chrome/browser/prefetch/search_prefetch/base_search_prefetch_request.h
+++ b/chrome/browser/prefetch/search_prefetch/base_search_prefetch_request.h
@@ -49,8 +49,10 @@
       delete;
 
   // Starts the network request to prefetch |prefetch_url_|. Sets various fields
-  // on a resource request and calls |StartPrefetchRequestInternal()|.
-  void StartPrefetchRequest(Profile* profile);
+  // on a resource request and calls |StartPrefetchRequestInternal()|. Returns
+  // |false| if the request is not started (i.e., it would be deferred by
+  // throttles).
+  bool StartPrefetchRequest(Profile* profile);
 
   // Marks a prefetch as canceled and stops any ongoing fetch.
   void CancelPrefetch();
diff --git a/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc b/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc
index f018dca..27d93ca9 100644
--- a/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc
+++ b/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc
@@ -127,6 +127,9 @@
   }
 
   DCHECK(prefetch_request);
+  if (!prefetch_request->StartPrefetchRequest(profile_)) {
+    return false;
+  }
 
   prefetches_.emplace(search_terms, std::move(prefetch_request));
   prefetches_[search_terms]->StartPrefetchRequest(profile_);
diff --git a/chrome/browser/prefetch/search_prefetch/search_prefetch_service_browsertest.cc b/chrome/browser/prefetch/search_prefetch/search_prefetch_service_browsertest.cc
index 2203c62..bd81b3d 100644
--- a/chrome/browser/prefetch/search_prefetch/search_prefetch_service_browsertest.cc
+++ b/chrome/browser/prefetch/search_prefetch/search_prefetch_service_browsertest.cc
@@ -59,6 +59,8 @@
 constexpr char kOmniboxSuggestNonPrefetchQuery[] = "puffins";
 constexpr char kLoadInSubframe[] = "/load_in_subframe";
 constexpr char kClientHintsURL[] = "/accept_ch_with_lifetime.html";
+constexpr char kThrottleHeader[] = "porgs-header";
+constexpr char kThrottleHeaderValue[] = "porgs-header-value";
 }  // namespace
 
 // A response that hangs after serving the start of the response.
@@ -73,6 +75,99 @@
   }
 };
 
+// A delegate to cancel prefetch requests by setting |defer| to true.
+class DeferringThrottle : public blink::URLLoaderThrottle {
+ public:
+  DeferringThrottle() = default;
+  ~DeferringThrottle() override = default;
+
+  void WillStartRequest(network::ResourceRequest* request,
+                        bool* defer) override {
+    *defer = true;
+  }
+};
+
+class ThrottleAllContentBrowserClient : public ChromeContentBrowserClient {
+ public:
+  ThrottleAllContentBrowserClient() = default;
+  ~ThrottleAllContentBrowserClient() override = default;
+
+  // ContentBrowserClient overrides:
+  std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
+  CreateURLLoaderThrottles(
+      const network::ResourceRequest& request,
+      content::BrowserContext* browser_context,
+      const base::RepeatingCallback<content::WebContents*()>& wc_getter,
+      content::NavigationUIData* navigation_ui_data,
+      int frame_tree_node_id) override {
+    std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles;
+    throttles.push_back(std::make_unique<DeferringThrottle>());
+    return throttles;
+  }
+};
+
+// A delegate to cancel prefetch requests by calling cancel on |delegate_|.
+class CancellingThrottle : public blink::URLLoaderThrottle {
+ public:
+  CancellingThrottle() = default;
+  ~CancellingThrottle() override = default;
+
+  void WillStartRequest(network::ResourceRequest* request,
+                        bool* defer) override {
+    delegate_->CancelWithError(net::ERR_ABORTED);
+  }
+};
+
+class CancelAllContentBrowserClient : public ChromeContentBrowserClient {
+ public:
+  CancelAllContentBrowserClient() = default;
+  ~CancelAllContentBrowserClient() override = default;
+
+  // ContentBrowserClient overrides:
+  std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
+  CreateURLLoaderThrottles(
+      const network::ResourceRequest& request,
+      content::BrowserContext* browser_context,
+      const base::RepeatingCallback<content::WebContents*()>& wc_getter,
+      content::NavigationUIData* navigation_ui_data,
+      int frame_tree_node_id) override {
+    std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles;
+    throttles.push_back(std::make_unique<CancellingThrottle>());
+    return throttles;
+  }
+};
+
+// A delegate to add a custom header to prefetches.
+class AddHeaderModifyingThrottle : public blink::URLLoaderThrottle {
+ public:
+  AddHeaderModifyingThrottle() = default;
+  ~AddHeaderModifyingThrottle() override = default;
+
+  void WillStartRequest(network::ResourceRequest* request,
+                        bool* defer) override {
+    request->headers.SetHeader(kThrottleHeader, kThrottleHeaderValue);
+  }
+};
+
+class AddHeaderContentBrowserClient : public ChromeContentBrowserClient {
+ public:
+  AddHeaderContentBrowserClient() = default;
+  ~AddHeaderContentBrowserClient() override = default;
+
+  // ContentBrowserClient overrides:
+  std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
+  CreateURLLoaderThrottles(
+      const network::ResourceRequest& request,
+      content::BrowserContext* browser_context,
+      const base::RepeatingCallback<content::WebContents*()>& wc_getter,
+      content::NavigationUIData* navigation_ui_data,
+      int frame_tree_node_id) override {
+    std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles;
+    throttles.push_back(std::make_unique<AddHeaderModifyingThrottle>());
+    return throttles;
+  }
+};
+
 class SearchPrefetchBaseBrowserTest : public InProcessBrowserTest {
  public:
   SearchPrefetchBaseBrowserTest() {
@@ -538,6 +633,9 @@
   EXPECT_EQ(1u, search_server_prefetch_request_count());
   // Make sure we don't get client hints headers by default.
   EXPECT_FALSE(base::Contains(headers, "viewport-width"));
+  EXPECT_TRUE(base::Contains(headers, "User-Agent"));
+  ASSERT_TRUE(base::Contains(headers, "Upgrade-Insecure-Requests"));
+  EXPECT_TRUE(base::Contains(headers["Upgrade-Insecure-Requests"], "1"));
 
   prefetch_status = search_prefetch_service->GetSearchPrefetchStatusForTesting(
       base::ASCIIToUTF16(search_terms));
@@ -545,6 +643,79 @@
   EXPECT_EQ(SearchPrefetchStatus::kCanBeServed, prefetch_status.value());
 }
 
+IN_PROC_BROWSER_TEST_P(SearchPrefetchServiceEnabledBrowserTest,
+                       PrefetchThrottled) {
+  ThrottleAllContentBrowserClient browser_client;
+  auto* old_client = content::SetBrowserClientForTesting(&browser_client);
+  auto* search_prefetch_service =
+      SearchPrefetchServiceFactory::GetForProfile(browser()->profile());
+  EXPECT_NE(nullptr, search_prefetch_service);
+
+  std::string search_terms = "prefetch_content";
+
+  GURL prefetch_url = GetSearchServerQueryURL(search_terms);
+
+  EXPECT_FALSE(search_prefetch_service->MaybePrefetchURL(prefetch_url));
+  auto prefetch_status =
+      search_prefetch_service->GetSearchPrefetchStatusForTesting(
+          base::ASCIIToUTF16(search_terms));
+  EXPECT_FALSE(prefetch_status.has_value());
+  content::SetBrowserClientForTesting(old_client);
+}
+
+IN_PROC_BROWSER_TEST_P(SearchPrefetchServiceEnabledBrowserTest,
+                       PrefetchCancelledByThrottle) {
+  CancelAllContentBrowserClient browser_client;
+  auto* old_client = content::SetBrowserClientForTesting(&browser_client);
+  auto* search_prefetch_service =
+      SearchPrefetchServiceFactory::GetForProfile(browser()->profile());
+  EXPECT_NE(nullptr, search_prefetch_service);
+
+  std::string search_terms = "prefetch_content";
+
+  GURL prefetch_url = GetSearchServerQueryURL(search_terms);
+
+  EXPECT_FALSE(search_prefetch_service->MaybePrefetchURL(prefetch_url));
+  auto prefetch_status =
+      search_prefetch_service->GetSearchPrefetchStatusForTesting(
+          base::ASCIIToUTF16(search_terms));
+  EXPECT_FALSE(prefetch_status.has_value());
+  content::SetBrowserClientForTesting(old_client);
+}
+
+IN_PROC_BROWSER_TEST_P(SearchPrefetchServiceEnabledBrowserTest,
+                       PrefetchThrottleAddsHeader) {
+  AddHeaderContentBrowserClient browser_client;
+  auto* old_client = content::SetBrowserClientForTesting(&browser_client);
+  auto* search_prefetch_service =
+      SearchPrefetchServiceFactory::GetForProfile(browser()->profile());
+  EXPECT_NE(nullptr, search_prefetch_service);
+
+  std::string search_terms = "prefetch_content";
+
+  GURL prefetch_url = GetSearchServerQueryURL(search_terms);
+
+  EXPECT_TRUE(search_prefetch_service->MaybePrefetchURL(prefetch_url));
+  auto prefetch_status =
+      search_prefetch_service->GetSearchPrefetchStatusForTesting(
+          base::ASCIIToUTF16(search_terms));
+  ASSERT_TRUE(prefetch_status.has_value());
+  EXPECT_EQ(SearchPrefetchStatus::kInFlight, prefetch_status.value());
+
+  WaitUntilStatusChanges(base::ASCIIToUTF16(search_terms));
+
+  auto headers = search_server_requests()[0].headers;
+  EXPECT_EQ(1u, search_server_requests().size());
+  ASSERT_TRUE(base::Contains(headers, kThrottleHeader));
+  EXPECT_TRUE(base::Contains(headers[kThrottleHeader], kThrottleHeaderValue));
+
+  prefetch_status = search_prefetch_service->GetSearchPrefetchStatusForTesting(
+      base::ASCIIToUTF16(search_terms));
+  ASSERT_TRUE(prefetch_status.has_value());
+  EXPECT_EQ(SearchPrefetchStatus::kCanBeServed, prefetch_status.value());
+  content::SetBrowserClientForTesting(old_client);
+}
+
 class HeaderObserverContentBrowserClient : public ChromeContentBrowserClient {
  public:
   HeaderObserverContentBrowserClient() = default;
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 1a181d49..58a0fba 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -200,7 +200,6 @@
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
 #include "chrome/browser/supervised_user/child_accounts/child_account_service.h"
-#include "chrome/browser/supervised_user/supervised_user_allowlist_service.h"
 #include "chrome/browser/supervised_user/supervised_user_service.h"
 #endif
 
@@ -458,6 +457,13 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // Deprecated 4/2020
 const char kSupervisedUsersNextId[] = "LocallyManagedUsersNextId";
+
+// Deprecated 11/2020
+const char kRegisteredSupervisedUserAllowlists[] =
+    "supervised_users.whitelists";
+
+// Deprecated 11/2020
+const char kSupervisedUserAllowlists[] = "profile.managed.whitelists";
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 // Deprecated 6/2020
@@ -509,6 +515,7 @@
   registry->RegisterStringPref(kInvalidatorClientId, std::string());
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+  registry->RegisterDictionaryPref(kRegisteredSupervisedUserAllowlists);
   registry->RegisterIntegerPref(kSupervisedUsersNextId, 0);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 }
@@ -525,6 +532,7 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   registry->RegisterBooleanPref(
       kDisplayRotationAcceleratorDialogHasBeenAccepted, false);
+  registry->RegisterDictionaryPref(kSupervisedUserAllowlists);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   registry->RegisterBooleanPref(kBlacklistedCredentialsNormalized, false);
@@ -893,7 +901,6 @@
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   ChildAccountService::RegisterProfilePrefs(registry);
   SupervisedUserService::RegisterProfilePrefs(registry);
-  SupervisedUserAllowlistService::RegisterProfilePrefs(registry);
 #endif
 
 #if defined(OS_ANDROID)
@@ -1096,6 +1103,9 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // Added 4/2020.
   local_state->ClearPref(kSupervisedUsersNextId);
+
+  // Added 11/2020.
+  local_state->ClearPref(kRegisteredSupervisedUserAllowlists);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   // Please don't delete the following line. It is used by PRESUBMIT.py.
@@ -1198,6 +1208,11 @@
   // Added 11/2020
   profile_prefs->ClearPref(kDRMSalt);
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // Added 11/2020.
+  profile_prefs->ClearPref(kSupervisedUserAllowlists);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
   // Please don't delete the following line. It is used by PRESUBMIT.py.
   // END_MIGRATE_OBSOLETE_PROFILE_PREFS
 }
diff --git a/chrome/browser/profiles/profiles_state.cc b/chrome/browser/profiles/profiles_state.cc
index 355a51a..5257906 100644
--- a/chrome/browser/profiles/profiles_state.cc
+++ b/chrome/browser/profiles/profiles_state.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/profile_picker.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
@@ -79,6 +80,9 @@
   registry->RegisterBooleanPref(prefs::kForceBrowserSignin, false);
   registry->RegisterBooleanPref(prefs::kBrowserShowProfilePickerOnStartup,
                                 true);
+  registry->RegisterIntegerPref(
+      prefs::kBrowserProfilePickerAvailabilityOnStartup,
+      static_cast<int>(ProfilePicker::AvailabilityOnStartup::kEnabled));
 }
 
 void SetLastUsedProfile(const std::string& profile_dir) {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index d5de3fa..4c1b56c1 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -469,6 +469,7 @@
       "options/options_test.js",
       "panel/i_search_test.js",
       "panel/panel_test.js",
+      "panel/panel_test_base.js",
       "panel/tutorial_test.js",
     ]
     gen_include_files = [
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test.js
index 18b7155..81082ab 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test.js
@@ -3,38 +3,12 @@
 // found in the LICENSE file.
 
 // Include test fixture.
-GEN_INCLUDE([
-  '//chrome/browser/resources/chromeos/accessibility/chromevox/testing/chromevox_next_e2e_test_base.js'
-]);
+GEN_INCLUDE(['panel_test_base.js']);
 
 /**
  * Test fixture for Panel.
  */
-ChromeVoxPanelTest = class extends ChromeVoxNextE2ETest {
-  /** @override */
-  testGenCppIncludes() {
-    ChromeVoxE2ETest.prototype.testGenCppIncludes.call(this);
-  }
-
-  getPanelWindow() {
-    let panelWindow = null;
-    while (!panelWindow) {
-      panelWindow = chrome.extension.getViews().find(function(view) {
-        return view.location.href.indexOf('chromevox/panel/panel.html') > 0;
-      });
-    }
-    return panelWindow;
-  }
-
-  /**
-   * Gets the Panel object in the panel.html window. Note that the extension
-   * system destroys our reference to this object unpredictably so always ask
-   * chrome.extension.getViews for it.
-   */
-  getPanel() {
-    return this.getPanelWindow().Panel;
-  }
-
+ChromeVoxPanelTest = class extends ChromeVoxPanelTestBase {
   fireMockEvent(key) {
     return function() {
       const obj = {};
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test_base.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test_base.js
new file mode 100644
index 0000000..feecaea
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test_base.js
@@ -0,0 +1,35 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Include test fixture.
+GEN_INCLUDE(['../testing/chromevox_next_e2e_test_base.js']);
+
+/**
+ * Base class for Panel tests.
+ */
+ChromeVoxPanelTestBase = class extends ChromeVoxNextE2ETest {
+  /** @override */
+  setUp() {
+    window.doCmd = this.doCmd;
+  }
+
+  getPanelWindow() {
+    let panelWindow = null;
+    while (!panelWindow) {
+      panelWindow = chrome.extension.getViews().find(function(view) {
+        return view.location.href.indexOf('chromevox/panel/panel.html') > 0;
+      });
+    }
+    return panelWindow;
+  }
+
+  /**
+   * Gets the Panel object in the panel.html window. Note that the extension
+   * system destroys our reference to this object unpredictably so always ask
+   * chrome.extension.getViews for it.
+   */
+  getPanel() {
+    return this.getPanelWindow().Panel;
+  }
+};
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js
index 1da2ab7c..abfa3d1 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js
@@ -3,18 +3,13 @@
 // found in the LICENSE file.
 
 // Include test fixture.
-GEN_INCLUDE(['../testing/chromevox_next_e2e_test_base.js']);
+GEN_INCLUDE(['panel_test_base.js']);
 GEN_INCLUDE(['../testing/mock_feedback.js']);
 
 /**
  * Test fixture for the interactive tutorial.
  */
-ChromeVoxTutorialTest = class extends ChromeVoxNextE2ETest {
-  /** @override */
-  setUp() {
-    window.doCmd = this.doCmd;
-  }
-
+ChromeVoxTutorialTest = class extends ChromeVoxPanelTestBase {
   assertActiveLessonIndex(expectedIndex) {
     assertEquals(expectedIndex, this.getPanel().iTutorial.activeLessonIndex);
   }
@@ -23,20 +18,6 @@
     assertEquals(expectedScreen, this.getPanel().iTutorial.activeScreen);
   }
 
-  getPanelWindow() {
-    let panelWindow = null;
-    while (!panelWindow) {
-      panelWindow = chrome.extension.getViews().find(function(view) {
-        return view.location.href.indexOf('chromevox/panel/panel.html') > 0;
-      });
-    }
-    return panelWindow;
-  }
-
-  getPanel() {
-    return this.getPanelWindow().Panel;
-  }
-
   async launchAndWaitForTutorial() {
     new PanelCommand(PanelCommandType.TUTORIAL).send();
     await this.waitForTutorial();
diff --git a/chrome/browser/resources/nearby_share/nearby_discovery_page.html b/chrome/browser/resources/nearby_share/nearby_discovery_page.html
index fb3b52d..2adc689 100644
--- a/chrome/browser/resources/nearby_share/nearby_discovery_page.html
+++ b/chrome/browser/resources/nearby_share/nearby_discovery_page.html
@@ -108,6 +108,7 @@
   cr-lottie {
     bottom: 0;
     height: 100px;
+    pointer-events: none;
     position: absolute;
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn
index d8aab1a..2a80838 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn
@@ -74,6 +74,7 @@
     "../../controls:password_prompt_dialog",
     "../../prefs:prefs_behavior",
     "../localized_link:localized_link",
+    "//ui/webui/resources/js:assert",
     "//ui/webui/resources/js:cr",
     "//ui/webui/resources/js:web_ui_listener_behavior",
   ]
@@ -399,10 +400,8 @@
   migrated_imports = os_settings_migrated_imports
   namespace_rewrites = os_settings_namespace_rewrites
   auto_imports =
-      os_settings_auto_imports + [
-        "ui/webui/resources/html/polymer.html|Polymer,html,beforeNextRender",
-        "ui/webui/resources/html/assert.html|assert",
-      ]
+      os_settings_auto_imports +
+      [ "ui/webui/resources/html/polymer.html|Polymer,html,beforeNextRender" ]
 }
 
 polymer_modulizer("multidevice_notification_access_setup_dialog") {
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_constants.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_constants.js
index 4a64be28..02b6330e 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_constants.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_constants.js
@@ -66,6 +66,17 @@
   };
 
   /**
+   * Possible states of Phone Hub's notification access. Access can be
+   * prohibited if the user is using a work profile on their phone.
+   * @enum {number}
+   */
+  /* #export */ const PhoneHubNotificationAccessStatus = {
+    PROHIBITED: 0,
+    AVAILABLE_BUT_NOT_GRANTED: 1,
+    ACCESS_GRANTED: 2,
+  };
+
+  /**
    * Container for the initial data that the page requires in order to display
    * the correct content. It is also used for receiving status updates during
    * use. Note that the host device may be verified (enabled or disabled),
@@ -90,7 +101,7 @@
    *   phoneHubTaskContinuationState: !settings.MultiDeviceFeatureState,
    *   wifiSyncState: !settings.MultiDeviceFeatureState,
    *   isAndroidSmsPairingComplete: boolean,
-   *   isNotificationAccessGranted: boolean
+   *   notificationAccessStatus: !settings.PhoneHubNotificationAccessStatus
    * }}
    */
   /* #export */ let MultiDevicePageContentData;
@@ -101,6 +112,7 @@
     MultiDeviceFeature,
     MultiDeviceFeatureState,
     MultiDevicePageContentData,
+    PhoneHubNotificationAccessStatus,
     SmartLockSignInEnabledState
   };
 });
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.js
index 841cfec..96fc77a3 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // clang-format off
-// #import {MultiDeviceSettingsMode, MultiDeviceFeature, MultiDeviceFeatureState, MultiDevicePageContentData } from './multidevice_constants.m.js';
+// #import {MultiDeviceSettingsMode, MultiDeviceFeature, MultiDeviceFeatureState, MultiDevicePageContentData, PhoneHubNotificationAccessStatus} from './multidevice_constants.m.js';
 // #import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
 // clang-format on
 
@@ -72,6 +72,16 @@
   },
 
   /**
+   * @return {boolean} Whether or not Phone Hub notification access is
+   *     prohibited (i.e., due to the user having a work profile).
+   */
+  isPhoneHubNotificationAccessProhibited() {
+    return this.pageContentData &&
+        this.pageContentData.notificationAccessStatus ===
+        settings.PhoneHubNotificationAccessStatus.PROHIBITED;
+  },
+
+  /**
    * Whether the user is prevented from attempted to change a given feature. In
    * the UI this corresponds to a disabled toggle.
    * @param {!settings.MultiDeviceFeature} feature
@@ -85,6 +95,13 @@
       return false;
     }
 
+    // Cannot edit the Phone Hub notification toggle if notification access is
+    // prohibited.
+    if (feature === settings.MultiDeviceFeature.PHONE_HUB_NOTIFICATIONS &&
+        this.isPhoneHubNotificationAccessProhibited()) {
+      return false;
+    }
+
     return [
       settings.MultiDeviceFeatureState.DISABLED_BY_USER,
       settings.MultiDeviceFeatureState.ENABLED_BY_USER
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.html b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.html
index aabbb54e..84ded70 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.html
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.html
@@ -17,7 +17,7 @@
 <dom-module id="settings-multidevice-feature-item">
   <template>
     <style include="settings-shared">
-      :host([is-sub-feature]) iron-icon {
+      :host([is-sub-feature]) #feature-icon {
         display: none;
       }
 
@@ -31,7 +31,7 @@
         padding: var(--feature-item-row-padding);
       }
 
-      iron-icon {
+      #feature-icon {
         padding: 2px;
       }
 
@@ -45,7 +45,8 @@
                feature, pageContentData, subpageRoute)]]"
            on-click="handleItemClick_">
         <slot name="icon">
-          <iron-icon icon="[[getIconName(feature)]]"></iron-icon>
+          <iron-icon id="feature-icon" icon="[[getIconName(feature)]]">
+          </iron-icon>
         </slot>
         <div id="item-text-container" class="middle">
           <div id="featureName">[[getFeatureName(feature)]]</div>
@@ -58,18 +59,24 @@
           </slot>
         </div>
         <template is="dom-if"
-                  if="[[hasSubpageClickHandler_(
-              feature, pageContentData, subpageRoute)]]"
+            if="[[hasSubpageClickHandler_(feature, pageContentData,
+                      subpageRoute)]]"
                   restamp>
           <cr-icon-button id="subpageButton" class="subpage-arrow"
               aria-labelledby="featureName" aria-describedby="featureSecondary"
               aria-roledescription="$i18n{subpageArrowRoleDescription}">
           </cr-icon-button>
         </template>
+        <template is="dom-if" if="[[infoTooltip]]" restamp>
+          <iron-icon id="help-icon" icon="cr:help-outline"></iron-icon>
+          <paper-tooltip for="help-icon" position="top" fit-to-visible-bounds>
+            [[infoTooltip]]
+          </paper-tooltip>
+        </template>
       </div>
       <template is="dom-if"
-                if="[[hasSubpageClickHandler_(
-              feature, pageContentData, subpageRoute)]]"
+          if="[[shouldShowSeparator_(
+                    feature, pageContentData, subpageRoute)]]"
                 restamp>
         <div class="separator"></div>
       </template>
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js
index 158004da..c02e27f 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js
@@ -28,6 +28,11 @@
     subpageRoute: Object,
 
     /**
+     * A tooltip to show over an info icon. If unset, no info icon is shown.
+     */
+    infoTooltip: String,
+
+    /**
      * URLSearchParams for subpage route. No param is provided if it is
      * undefined.
      * @type {URLSearchParams|undefined}
@@ -76,6 +81,14 @@
     return !!this.subpageRoute && this.isFeatureAllowedByPolicy(this.feature);
   },
 
+  /**
+   * @return {boolean}
+   * @private
+   */
+  shouldShowSeparator_() {
+    return this.hasSubpageClickHandler_() || !!this.infoTooltip;
+  },
+
   /** @private */
   handleItemClick_(event) {
     // We do not navigate away if the click was on a link.
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_toggle.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_toggle.js
index 7a66d3e..3fbcfb7 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_toggle.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_toggle.js
@@ -62,6 +62,13 @@
    * @private
    */
   resetChecked_() {
+    // If Phone Hub notification access is prohibited, the toggle is always off.
+    if (this.feature === settings.MultiDeviceFeature.PHONE_HUB_NOTIFICATIONS &&
+        this.isPhoneHubNotificationAccessProhibited()) {
+      this.checked_ = false;
+      return;
+    }
+
     this.checked_ = this.getFeatureState(this.feature) ===
         settings.MultiDeviceFeatureState.ENABLED_BY_USER;
   },
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_notification_access_setup_dialog.html b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_notification_access_setup_dialog.html
index 45ff6453..0cf9769 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_notification_access_setup_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_notification_access_setup_dialog.html
@@ -8,6 +8,7 @@
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="multidevice_browser_proxy.html">
+<link rel="import" href="../localized_link/localized_link.html">
 <link rel="import" href="../os_icons.html">
 <link rel="import" href="../../settings_shared_css.html">
 
@@ -85,16 +86,18 @@
       <div id="dialogBody" slot="body">
         <div id="illustration"></div>
         <div id="description">
-          <div hidden="[[isSetupAttemptInProgress_]]">
-            [[description_]]
-          </div>
+          <template is="dom-if" if="[[description_]]" restamp>
+            <settings-localized-link localized-string="[[description_]]">
+            </settings-localized-link>
+          </template>
           <div hidden="[[!shouldShowSetupInstructionsSeparately_]]">
             $i18n{multideviceNotificationAccessSetupInstructions}
           </div>
         </div>
       </div>
       <div id="buttonContainer" slot="button-container">
-        <template is="dom-if" if="[[!hasCompletedSetupSuccessfully_]]" restamp>
+        <template is="dom-if" if="[[shouldShowCancelButton_(setupState_)]]"
+            restamp>
           <cr-button id="cancelButton" class="cancel-button"
               on-click="onCancelClicked_">
             $i18n{cancel}
@@ -102,17 +105,24 @@
         </template>
         <template is="dom-if" if="[[hasCompletedSetupSuccessfully_]]" restamp>
           <cr-button id="doneButton" class="action-button"
-              on-click="onDoneButtonClicked_">
+              on-click="onDoneOrCloseButtonClicked_">
             $i18n{done}
           </cr-button>
         </template>
+        <template is="dom-if" if="[[isNotificationAccessProhibited_]]" restamp>
+          <cr-button id="closeButton" class="action-button"
+              on-click="onDoneOrCloseButtonClicked_">
+            $i18n{close}
+          </cr-button>
+        </template>
         <template is="dom-if" if="[[hasNotStartedSetupAttempt_]]" restamp>
           <cr-button id="getStartedButton" class="action-button"
               on-click="attemptNotificationSetup_">
             $i18n{multideviceNotificationAccessSetupGetStarted}
           </cr-button>
         </template>
-        <template is="dom-if" if="[[didSetupAttemptFail_]]" restamp>
+        <template is="dom-if" if="[[shouldShowTryAgainButton_(setupState_)]]"
+            restamp>
           <cr-button id="tryAgainButton" class="action-button"
               on-click="attemptNotificationSetup_">
             $i18n{multideviceNotificationAccessSetupTryAgain}
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_notification_access_setup_dialog.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_notification_access_setup_dialog.js
index 21ae6422..de740222 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_notification_access_setup_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_notification_access_setup_dialog.js
@@ -22,6 +22,7 @@
   CONNECTION_DISCONNECTED: 3,
   SENT_MESSAGE_TO_PHONE_AND_WAITING_FOR_RESPONSE: 4,
   COMPLETED_SUCCESSFULLY: 5,
+  NOTIFICATION_ACCESS_PROHIBITED: 6,
 };
 
 Polymer({
@@ -83,6 +84,12 @@
     },
 
     /** @private */
+    isNotificationAccessProhibited_: {
+      type: Boolean,
+      computed: 'computeIsNotificationAccessProhibited_(setupState_)',
+    },
+
+    /** @private */
     shouldShowSetupInstructionsSeparately_: {
       type: Boolean,
       computed: 'computeShouldShowSetupInstructionsSeparately_(' +
@@ -149,12 +156,23 @@
   /**
    * @return {boolean}
    * @private
+   */
+  computeIsNotificationAccessProhibited_() {
+    return this.setupState_ ===
+        NotificationAccessSetupOperationStatus.NOTIFICATION_ACCESS_PROHIBITED;
+  },
+
+  /**
+   * @return {boolean}
+   * @private
    * */
   computeDidSetupAttemptFail_() {
     return this.setupState_ ===
         NotificationAccessSetupOperationStatus.TIMED_OUT_CONNECTING ||
         this.setupState_ ===
-        NotificationAccessSetupOperationStatus.CONNECTION_DISCONNECTED;
+        NotificationAccessSetupOperationStatus.CONNECTION_DISCONNECTED ||
+        this.setupState_ ===
+        NotificationAccessSetupOperationStatus.NOTIFICATION_ACCESS_PROHIBITED;
   },
 
   /**
@@ -179,7 +197,7 @@
   },
 
   /** @private */
-  onDoneButtonClicked_() {
+  onDoneOrCloseButtonClicked_() {
     this.$.dialog.close();
   },
 
@@ -206,6 +224,9 @@
       case Status.CONNECTION_DISCONNECTED:
         return this.i18n(
             'multideviceNotificationAccessSetupConnectionLostWithPhoneTitle');
+      case Status.NOTIFICATION_ACCESS_PROHIBITED:
+        return this.i18n(
+            'multideviceNotificationAccessSetupAccessProhibitedTitle');
       default:
         return '';
     }
@@ -230,6 +251,9 @@
       case Status.CONNECTION_DISCONNECTED:
         return this.i18n(
             'multideviceNotificationAccessSetupMaintainFailureSummary');
+      case Status.NOTIFICATION_ACCESS_PROHIBITED:
+        return this.i18nAdvanced(
+            'multideviceNotificationAccessSetupAccessProhibitedSummary');
 
       // Only setup instructions will be shown.
       case Status.CONNECTION_REQUESTED:
@@ -239,4 +263,26 @@
         return '';
     }
   },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  shouldShowCancelButton_() {
+    return this.setupState_ !==
+        NotificationAccessSetupOperationStatus.COMPLETED_SUCCESSFULLY &&
+        this.setupState_ !==
+        NotificationAccessSetupOperationStatus.NOTIFICATION_ACCESS_PROHIBITED;
+  },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  shouldShowTryAgainButton_() {
+    return this.setupState_ ===
+        NotificationAccessSetupOperationStatus.TIMED_OUT_CONNECTING ||
+        this.setupState_ ===
+        NotificationAccessSetupOperationStatus.CONNECTION_DISCONNECTED;
+  },
 });
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.html b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.html
index bdf8382..3d957597 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.html
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.html
@@ -3,10 +3,10 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
+<link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
-<link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="../../i18n_setup.html">
 <link rel="import" href="../deep_linking_behavior.html">
@@ -34,6 +34,10 @@
       cr-policy-indicator {
         padding: 0 var(--cr-controlled-by-spacing);
       }
+
+      #nearbySetUp {
+        margin: 0 16px;
+      }
     </style>
     <settings-animated-pages id="pages" section="multidevice"
         focus-config="[[focusConfig_]]">
@@ -133,10 +137,21 @@
               </template>
             </div>
             <div class="separator"></div>
-            <settings-toggle-button id="nearbySharingToggleButton"
-                pref="{{prefs.nearby_sharing.enabled}}"
-                deep-link-focus-id$="[[Setting.kNearbyShareOnOff]]">
-            </settings-toggle-button>
+            <template is="dom-if"
+                if="[[prefs.nearby_sharing.onboarding_complete.value]]"
+                restamp>
+              <settings-toggle-button id="nearbySharingToggleButton"
+                  pref="{{prefs.nearby_sharing.enabled}}"
+                  deep-link-focus-id$="[[Setting.kNearbyShareOnOff]]">
+              </settings-toggle-button>
+            </template>
+            <template is="dom-if"
+                if="[[!prefs.nearby_sharing.onboarding_complete.value]]"
+                restamp>
+              <cr-button id="nearbySetUp" on-click="handleNearbySetUpClick_">
+                $i18n{nearbyShareSetUpButtonTitle}
+              </cr-button>
+            </template>
           </div>
         </template>
       </div>
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
index bcbc403..6cc4d1d2 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
@@ -351,9 +351,19 @@
     // If the feature to enable is Phone Hub Notifications, notification access
     // must have been granted before the feature can be enabled.
     if (feature === settings.MultiDeviceFeature.PHONE_HUB_NOTIFICATIONS &&
-        enabled && !this.pageContentData.isNotificationAccessGranted) {
-      this.showNotificationAccessSetupDialog_ = true;
-      return;
+        enabled) {
+      switch (this.pageContentData.notificationAccessStatus) {
+        case settings.PhoneHubNotificationAccessStatus.PROHIBITED:
+          assertNotReached('Cannot enable notification access; prohibited');
+          return;
+        case settings.PhoneHubNotificationAccessStatus
+            .AVAILABLE_BUT_NOT_GRANTED:
+          this.showNotificationAccessSetupDialog_ = true;
+          return;
+        default:
+          // Fall through and attempt to toggle feature.
+          break;
+      }
     }
 
     // Disabling any feature does not require authentication, and enable some
@@ -440,7 +450,8 @@
   onInitialPageContentDataFetched_(newData) {
     this.onPageContentDataChanged_(newData);
 
-    if (this.pageContentData.isNotificationAccessGranted) {
+    if (this.pageContentData.notificationAccessStatus !==
+        settings.PhoneHubNotificationAccessStatus.AVAILABLE_BUT_NOT_GRANTED) {
       return;
     }
 
@@ -511,4 +522,12 @@
   onHideNotificationSetupAccessDialog_() {
     this.showNotificationAccessSetupDialog_ = false;
   },
+
+  /** @private */
+  handleNearbySetUpClick_() {
+    const params = new URLSearchParams();
+    params.set('onboarding', '');
+    settings.Router.getInstance().navigateTo(
+        settings.routes.NEARBY_SHARE, params);
+  },
 });
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.html b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.html
index df05809..0d61af8 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.html
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.html
@@ -124,6 +124,8 @@
             restamp>
           <settings-multidevice-feature-item id="phoneHubNotificationsItem"
               feature="[[MultiDeviceFeature.PHONE_HUB_NOTIFICATIONS]]"
+              info-tooltip="[[getPhoneHubNotificationsTooltip_(
+                                  pageContentData)]]"
               page-content-data="[[pageContentData]]" is-sub-feature
               deep-link-focus-id$="[[Setting.kPhoneHubNotificationsOnOff]]">
           </settings-multidevice-feature-item>
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.js
index b291cad..bc3df4d 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.js
@@ -157,5 +157,13 @@
     return !this.isSuiteOn() ||
         messagesFeatureState ===
         settings.MultiDeviceFeatureState.PROHIBITED_BY_POLICY;
-  }
+  },
+
+  getPhoneHubNotificationsTooltip_() {
+    if (!this.isPhoneHubNotificationAccessProhibited()) {
+      return '';
+    }
+
+    return this.i18n('multideviceNotificationAccessProhibitedTooltip');
+  },
 });
diff --git a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_high_visibility_page.html b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_high_visibility_page.html
index ada90bc6..2409441 100644
--- a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_high_visibility_page.html
+++ b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_high_visibility_page.html
@@ -46,6 +46,7 @@
       cr-lottie {
         bottom: 0;
         height: 100px;
+        pointer-events: none;
         position: absolute;
       }
     </style>
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_action_assignment_dialog.html b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_action_assignment_dialog.html
index 4ac1fd49..116d18e1 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_action_assignment_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_action_assignment_dialog.html
@@ -16,6 +16,10 @@
         width: 420px;
       }
 
+      #prompt {
+        height: 40px;
+      }
+
       #switchAssignments {
         background-color: var(--google-grey-refresh-100);
         border-radius: 4px;
@@ -39,7 +43,7 @@
     <cr-dialog id="switchAccessActionAssignmentDialog" show-on-attach>
       <div slot="title">[[dialogTitle_]]</div>
       <div slot="body">
-        <div aria-live="polite">
+        <div id="prompt" aria-live="polite">
           [[promptText_]]
         </div>
         <div id="switchAssignments">
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.gni b/chrome/browser/resources/settings/chromeos/os_settings.gni
index b7869bf..5564ca33 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.gni
+++ b/chrome/browser/resources/settings/chromeos/os_settings.gni
@@ -65,6 +65,7 @@
                                    "settings.OsResetBrowserProxy|OsResetBrowserProxy",
                                    "settings.OsSyncBrowserProxy|OsSyncBrowserProxy",
                                    "settings.OsSyncPrefs|OsSyncPrefs",
+                                   "settings.PhoneHubNotificationAccessStatus|PhoneHubNotificationAccessStatus",
                                    "settings.recordLockScreenProgress|recordLockScreenProgress",
                                    "settings.recordSettingChange|recordSettingChange",
                                    "settings.Route|Route",
@@ -106,7 +107,7 @@
                              "chrome/browser/resources/settings/chromeos/google_assistant_page/google_assistant_browser_proxy.html|GoogleAssistantBrowserProxy,GoogleAssistantBrowserProxyImpl",
                              "chrome/browser/resources/settings/chromeos/metrics_recorder.html|recordSettingChange",
                              "chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_browser_proxy.html|MultiDeviceBrowserProxy,MultiDeviceBrowserProxyImpl",
-                             "chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_constants.html|MultiDeviceSettingsMode,MultiDeviceFeature,MultiDeviceFeatureState,MultiDevicePageContentData,SmartLockSignInEnabledState",
+                             "chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_constants.html|MultiDeviceSettingsMode,MultiDeviceFeature,MultiDeviceFeatureState,MultiDevicePageContentData,PhoneHubNotificationAccessStatus,SmartLockSignInEnabledState",
                              "chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.html|MultiDeviceFeatureBehavior",
                              "chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_browser_proxy.html|MultiDeviceBrowserProxy,MultiDeviceBrowserProxyImpl",
                              "chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_account_manager_browser_proxy.html|NearbyAccountManagerBrowserProxy,NearbyAccountManagerBrowserProxyImpl",
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.js b/chrome/browser/resources/settings/chromeos/os_settings.js
index 754c28c..57853fc 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings.js
@@ -56,7 +56,7 @@
 export {ConsentStatus, DspHotwordState} from './google_assistant_page/google_assistant_page.m.js';
 export {InternetPageBrowserProxy, InternetPageBrowserProxyImpl} from './internet_page/internet_page_browser_proxy.m.js';
 export {MultiDeviceBrowserProxy, MultiDeviceBrowserProxyImpl} from './multidevice_page/multidevice_browser_proxy.m.js';
-export {MultiDeviceFeature, MultiDeviceFeatureState, MultiDevicePageContentData, MultiDeviceSettingsMode, SmartLockSignInEnabledState} from './multidevice_page/multidevice_constants.m.js';
+export {MultiDeviceFeature, MultiDeviceFeatureState, MultiDevicePageContentData, MultiDeviceSettingsMode, PhoneHubNotificationAccessStatus, SmartLockSignInEnabledState} from './multidevice_page/multidevice_constants.m.js';
 export {Account, NearbyAccountManagerBrowserProxy, NearbyAccountManagerBrowserProxyImpl} from './nearby_share_page/nearby_account_manager_browser_proxy.m.js';
 export {getReceiveManager, observeReceiveManager, setReceiveManagerForTesting} from './nearby_share_page/nearby_share_receive_manager.m.js';
 export {dataUsageStringToEnum, NearbyShareDataUsage} from './nearby_share_page/types.m.js';
diff --git a/chrome/browser/resources/settings/site_settings/site_list.html b/chrome/browser/resources/settings/site_settings/site_list.html
index 0da5b6b..8d71f93 100644
--- a/chrome/browser/resources/settings/site_settings/site_list.html
+++ b/chrome/browser/resources/settings/site_settings/site_list.html
@@ -42,7 +42,8 @@
           hidden$="[[!showNoSearchResults_(searchFilter, sites.*)]]">
         <div class="list-item secondary">$i18n{searchNoResults}</div>
       </div>
-      <div class="list-frame menu-content vertical-list" id="listContainer">
+      <div class="list-frame menu-content vertical-list" id="listContainer"
+          hidden$="[[!hasSites_(sites.*)]]">
         <iron-list items="[[getFilteredSites_(searchFilter, sites.*)]]"
             preserve-focus risk-selection>
           <template>
@@ -56,9 +57,8 @@
         </iron-list>
       </div>
     </div>
-    <paper-tooltip id="tooltip"
-        fit-to-visible-bounds manual-mode
-        position="top">
+    <paper-tooltip id="tooltip" hidden="[[!tooltipText_]]]"
+        fit-to-visible-bounds manual-mode position="top">
       [[tooltipText_]]
     </paper-tooltip>
     <template is="dom-if" if="[[showEditExceptionDialog_]]" restamp>
diff --git a/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.js b/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.js
index b8e3cfd..b0812f1 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.js
+++ b/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.js
@@ -58,6 +58,14 @@
         return loadTimeData.getBoolean('askOnStartup');
       }
     },
+
+    /** @private */
+    disableAskOnStartup_: {
+      type: Boolean,
+      value() {
+        return loadTimeData.getBoolean('disableAskOnStartup');
+      }
+    },
   },
 
   /** @private {?ManageProfilesBrowserProxy} */
@@ -153,6 +161,7 @@
    * @private
    */
   computeHideAskOnStartup_() {
-    return !this.profilesList_ || (this.profilesList_.length < 2);
+    return this.disableAskOnStartup_ || !this.profilesList_ ||
+        this.profilesList_.length < 2;
   },
 });
diff --git a/chrome/browser/supervised_user/supervised_user_allowlist_service.cc b/chrome/browser/supervised_user/supervised_user_allowlist_service.cc
deleted file mode 100644
index d0dcd25e..0000000
--- a/chrome/browser/supervised_user/supervised_user_allowlist_service.cc
+++ /dev/null
@@ -1,356 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/supervised_user/supervised_user_allowlist_service.h"
-
-#include <stddef.h>
-
-#include <string>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/metrics/user_metrics.h"
-#include "base/metrics/user_metrics_action.h"
-#include "base/strings/string_split.h"
-#include "base/values.h"
-#include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
-#include "chrome/browser/supervised_user/supervised_user_site_list.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/common/pref_names.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/prefs/pref_service.h"
-#include "components/prefs/scoped_user_pref_update.h"
-#include "components/sync/model/sync_change.h"
-#include "components/sync/model/sync_change_processor.h"
-#include "components/sync/model/sync_data.h"
-#include "components/sync/model/sync_error.h"
-#include "components/sync/model/sync_error_factory.h"
-#include "components/sync/protocol/sync.pb.h"
-
-const char kName[] = "name";
-
-SupervisedUserAllowlistService::SupervisedUserAllowlistService(
-    PrefService* prefs,
-    component_updater::SupervisedUserWhitelistInstaller* installer,
-    const std::string& client_id)
-    : prefs_(prefs), installer_(installer), client_id_(client_id) {
-  DCHECK(prefs);
-}
-
-SupervisedUserAllowlistService::~SupervisedUserAllowlistService() {}
-
-// static
-void SupervisedUserAllowlistService::RegisterProfilePrefs(
-    user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterDictionaryPref(prefs::kSupervisedUserAllowlists);
-}
-
-void SupervisedUserAllowlistService::Init() {
-  const base::DictionaryValue* allowlists =
-      prefs_->GetDictionary(prefs::kSupervisedUserAllowlists);
-  for (base::DictionaryValue::Iterator it(*allowlists); !it.IsAtEnd();
-       it.Advance()) {
-    registered_allowlists_.insert(it.key());
-  }
-  UMA_HISTOGRAM_COUNTS_100("ManagedUsers.Whitelist.Count", allowlists->size());
-
-  // The installer can be null in some unit tests.
-  if (!installer_)
-    return;
-
-  installer_->Subscribe(
-      base::BindRepeating(&SupervisedUserAllowlistService::OnAllowlistReady,
-                          weak_ptr_factory_.GetWeakPtr()));
-
-  // Register allowlists specified on the command line.
-  for (const auto& allowlist : GetAllowlistsFromCommandLine())
-    RegisterAllowlist(allowlist.first, allowlist.second, FROM_COMMAND_LINE);
-}
-
-void SupervisedUserAllowlistService::AddSiteListsChangedCallback(
-    const SiteListsChangedCallback& callback) {
-  site_lists_changed_callbacks_.push_back(callback);
-
-  std::vector<scoped_refptr<SupervisedUserSiteList>> allowlists;
-  GetLoadedAllowlists(&allowlists);
-  callback.Run(allowlists);
-}
-
-// static
-std::map<std::string, std::string>
-SupervisedUserAllowlistService::GetAllowlistsFromCommandLine() {
-  std::map<std::string, std::string> allowlists;
-  const base::CommandLine* command_line =
-      base::CommandLine::ForCurrentProcess();
-  std::string command_line_allowlists = command_line->GetSwitchValueASCII(
-      switches::kInstallSupervisedUserAllowlists);
-  std::vector<base::StringPiece> string_pieces =
-      base::SplitStringPiece(command_line_allowlists, ",",
-                             base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-  for (const base::StringPiece& allowlist : string_pieces) {
-    std::string id;
-    std::string name;
-    size_t separator = allowlist.find(':');
-    if (separator != base::StringPiece::npos) {
-      id = std::string(allowlist.substr(0, separator));
-      name = std::string(allowlist.substr(separator + 1));
-    } else {
-      id = std::string(allowlist);
-    }
-
-    const bool result = allowlists.insert(std::make_pair(id, name)).second;
-    DCHECK(result);
-  }
-
-  return allowlists;
-}
-
-void SupervisedUserAllowlistService::LoadAllowlistForTesting(
-    const std::string& id,
-    const base::string16& title,
-    const base::FilePath& path) {
-  bool result = registered_allowlists_.insert(id).second;
-  DCHECK(result);
-  OnAllowlistReady(id, title, base::FilePath(), path);
-}
-
-void SupervisedUserAllowlistService::UnloadAllowlist(const std::string& id) {
-  bool result = registered_allowlists_.erase(id) > 0u;
-  DCHECK(result);
-  loaded_allowlists_.erase(id);
-  NotifyAllowlistsChanged();
-}
-
-// static
-syncer::SyncData SupervisedUserAllowlistService::CreateAllowlistSyncData(
-    const std::string& id,
-    const std::string& name) {
-  sync_pb::EntitySpecifics specifics;
-  sync_pb::ManagedUserWhitelistSpecifics* allowlist =
-      specifics.mutable_managed_user_whitelist();
-  allowlist->set_id(id);
-  allowlist->set_name(name);
-
-  return syncer::SyncData::CreateLocalData(id, name, specifics);
-}
-
-void SupervisedUserAllowlistService::WaitUntilReadyToSync(
-    base::OnceClosure done) {
-  // This service handles sync events at any time.
-  std::move(done).Run();
-}
-
-base::Optional<syncer::ModelError>
-SupervisedUserAllowlistService::MergeDataAndStartSyncing(
-    syncer::ModelType type,
-    const syncer::SyncDataList& initial_sync_data,
-    std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
-    std::unique_ptr<syncer::SyncErrorFactory> error_handler) {
-  DCHECK_EQ(syncer::DEPRECATED_SUPERVISED_USER_ALLOWLISTS, type);
-
-  syncer::SyncChangeList change_list;
-
-  DictionaryPrefUpdate update(prefs_, prefs::kSupervisedUserAllowlists);
-  base::DictionaryValue* pref_dict = update.Get();
-  std::set<std::string> seen_ids;
-
-  for (const syncer::SyncData& sync_data : initial_sync_data) {
-    DCHECK_EQ(syncer::DEPRECATED_SUPERVISED_USER_ALLOWLISTS,
-              sync_data.GetDataType());
-    const sync_pb::ManagedUserWhitelistSpecifics& allowlist =
-        sync_data.GetSpecifics().managed_user_whitelist();
-    std::string id = allowlist.id();
-    std::string name = allowlist.name();
-    seen_ids.insert(id);
-    base::DictionaryValue* dict = nullptr;
-    if (pref_dict->GetDictionary(id, &dict)) {
-      std::string old_name;
-      bool result = dict->GetString(kName, &old_name);
-      DCHECK(result);
-      if (name != old_name) {
-        SetAllowlistProperties(dict, allowlist);
-      }
-    } else {
-      AddNewAllowlist(pref_dict, allowlist);
-    }
-  }
-
-  std::set<std::string> ids_to_remove;
-  for (base::DictionaryValue::Iterator it(*pref_dict); !it.IsAtEnd();
-       it.Advance()) {
-    if (seen_ids.find(it.key()) == seen_ids.end())
-      ids_to_remove.insert(it.key());
-  }
-
-  for (const std::string& id : ids_to_remove)
-    RemoveAllowlist(pref_dict, id);
-
-  // Notify if allowlists have been uninstalled. We will notify about newly
-  // added allowlists later, when they are actually available
-  // (in OnAllowlistLoaded).
-  if (!ids_to_remove.empty())
-    NotifyAllowlistsChanged();
-
-  // The function does not generate any errors, so it can always return
-  // base::nullopt.
-  return base::nullopt;
-}
-
-void SupervisedUserAllowlistService::StopSyncing(syncer::ModelType type) {
-  DCHECK_EQ(syncer::DEPRECATED_SUPERVISED_USER_ALLOWLISTS, type);
-}
-
-syncer::SyncDataList SupervisedUserAllowlistService::GetAllSyncDataForTesting(
-    syncer::ModelType type) const {
-  syncer::SyncDataList sync_data;
-  const base::DictionaryValue* allowlists =
-      prefs_->GetDictionary(prefs::kSupervisedUserAllowlists);
-  for (base::DictionaryValue::Iterator it(*allowlists); !it.IsAtEnd();
-       it.Advance()) {
-    const std::string& id = it.key();
-    const base::DictionaryValue* dict = nullptr;
-    it.value().GetAsDictionary(&dict);
-    std::string name;
-    bool result = dict->GetString(kName, &name);
-    DCHECK(result);
-    sync_pb::EntitySpecifics specifics;
-    sync_pb::ManagedUserWhitelistSpecifics* allowlist =
-        specifics.mutable_managed_user_whitelist();
-    allowlist->set_id(id);
-    allowlist->set_name(name);
-    sync_data.push_back(syncer::SyncData::CreateLocalData(id, name, specifics));
-  }
-  return sync_data;
-}
-
-base::Optional<syncer::ModelError>
-SupervisedUserAllowlistService::ProcessSyncChanges(
-    const base::Location& from_here,
-    const syncer::SyncChangeList& change_list) {
-  bool allowlists_removed = false;
-  DictionaryPrefUpdate update(prefs_, prefs::kSupervisedUserAllowlists);
-  base::DictionaryValue* pref_dict = update.Get();
-  for (const syncer::SyncChange& sync_change : change_list) {
-    syncer::SyncData data = sync_change.sync_data();
-    DCHECK_EQ(syncer::DEPRECATED_SUPERVISED_USER_ALLOWLISTS,
-              data.GetDataType());
-    const sync_pb::ManagedUserWhitelistSpecifics& allowlist =
-        data.GetSpecifics().managed_user_whitelist();
-    std::string id = allowlist.id();
-    switch (sync_change.change_type()) {
-      case syncer::SyncChange::ACTION_ADD: {
-        DCHECK(!pref_dict->HasKey(id)) << id;
-        AddNewAllowlist(pref_dict, allowlist);
-        break;
-      }
-      case syncer::SyncChange::ACTION_UPDATE: {
-        base::DictionaryValue* dict = nullptr;
-        pref_dict->GetDictionaryWithoutPathExpansion(id, &dict);
-        SetAllowlistProperties(dict, allowlist);
-        break;
-      }
-      case syncer::SyncChange::ACTION_DELETE: {
-        DCHECK(pref_dict->HasKey(id)) << id;
-        RemoveAllowlist(pref_dict, id);
-        allowlists_removed = true;
-        break;
-      }
-    }
-  }
-
-  if (allowlists_removed)
-    NotifyAllowlistsChanged();
-
-  return base::nullopt;
-}
-
-void SupervisedUserAllowlistService::AddNewAllowlist(
-    base::DictionaryValue* pref_dict,
-    const sync_pb::ManagedUserWhitelistSpecifics& allowlist) {
-  base::RecordAction(base::UserMetricsAction("ManagedUsers_Whitelist_Added"));
-
-  RegisterAllowlist(allowlist.id(), allowlist.name(), FROM_SYNC);
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
-  SetAllowlistProperties(dict.get(), allowlist);
-  pref_dict->SetWithoutPathExpansion(allowlist.id(), std::move(dict));
-}
-
-void SupervisedUserAllowlistService::SetAllowlistProperties(
-    base::DictionaryValue* dict,
-    const sync_pb::ManagedUserWhitelistSpecifics& allowlist) {
-  dict->SetString(kName, allowlist.name());
-}
-
-void SupervisedUserAllowlistService::RemoveAllowlist(
-    base::DictionaryValue* pref_dict,
-    const std::string& id) {
-  base::RecordAction(base::UserMetricsAction("ManagedUsers_Whitelist_Removed"));
-
-  pref_dict->RemoveKey(id);
-  installer_->UnregisterWhitelist(client_id_, id);
-  UnloadAllowlist(id);
-}
-
-void SupervisedUserAllowlistService::RegisterAllowlist(const std::string& id,
-                                                       const std::string& name,
-                                                       AllowlistSource source) {
-  bool result = registered_allowlists_.insert(id).second;
-  DCHECK(result);
-
-  // Using an empty client ID for allowlists installed from the command line
-  // causes the installer to not persist the installation, so the allowlist will
-  // be removed the next time the browser is started without the command line
-  // flag.
-  installer_->RegisterWhitelist(
-      source == FROM_COMMAND_LINE ? std::string() : client_id_, id, name);
-}
-
-void SupervisedUserAllowlistService::GetLoadedAllowlists(
-    std::vector<scoped_refptr<SupervisedUserSiteList>>* allowlists) {
-  for (const auto& allowlist : loaded_allowlists_)
-    allowlists->push_back(allowlist.second);
-}
-
-void SupervisedUserAllowlistService::NotifyAllowlistsChanged() {
-  std::vector<scoped_refptr<SupervisedUserSiteList>> allowlists;
-  GetLoadedAllowlists(&allowlists);
-
-  for (const auto& callback : site_lists_changed_callbacks_)
-    callback.Run(allowlists);
-}
-
-void SupervisedUserAllowlistService::OnAllowlistReady(
-    const std::string& id,
-    const base::string16& title,
-    const base::FilePath& large_icon_path,
-    const base::FilePath& allowlist_path) {
-  // If we did not register the allowlist or it has been unregistered in the
-  // mean time, ignore it.
-  if (registered_allowlists_.count(id) == 0u)
-    return;
-
-  SupervisedUserSiteList::Load(
-      id, title, large_icon_path, allowlist_path,
-      base::Bind(&SupervisedUserAllowlistService::OnAllowlistLoaded,
-                 weak_ptr_factory_.GetWeakPtr(), id));
-}
-
-void SupervisedUserAllowlistService::OnAllowlistLoaded(
-    const std::string& id,
-    const scoped_refptr<SupervisedUserSiteList>& allowlist) {
-  if (!allowlist) {
-    LOG(WARNING) << "Couldn't load allowlist " << id;
-    return;
-  }
-  // If the allowlist has been unregistered in the mean time, ignore it.
-  if (registered_allowlists_.count(id) == 0u)
-    return;
-
-  loaded_allowlists_[id] = allowlist;
-  NotifyAllowlistsChanged();
-}
diff --git a/chrome/browser/supervised_user/supervised_user_allowlist_service.h b/chrome/browser/supervised_user/supervised_user_allowlist_service.h
deleted file mode 100644
index 3be917c..0000000
--- a/chrome/browser/supervised_user/supervised_user_allowlist_service.h
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_ALLOWLIST_SERVICE_H_
-#define CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_ALLOWLIST_SERVICE_H_
-
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/strings/string16.h"
-#include "chrome/browser/supervised_user/supervised_users.h"
-#include "components/sync/model/syncable_service.h"
-
-class PrefService;
-class SupervisedUserSiteList;
-
-namespace base {
-class DictionaryValue;
-class FilePath;
-}  // namespace base
-
-namespace component_updater {
-class SupervisedUserWhitelistInstaller;
-}
-
-namespace user_prefs {
-class PrefRegistrySyncable;
-}
-
-namespace sync_pb {
-class ManagedUserWhitelistSpecifics;
-}
-
-class SupervisedUserAllowlistService : public syncer::SyncableService {
- public:
-  typedef base::Callback<void(
-      const std::vector<scoped_refptr<SupervisedUserSiteList>>&)>
-      SiteListsChangedCallback;
-
-  SupervisedUserAllowlistService(
-      PrefService* prefs,
-      component_updater::SupervisedUserWhitelistInstaller* installer,
-      const std::string& client_id);
-  ~SupervisedUserAllowlistService() override;
-
-  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
-
-  void Init();
-
-  // Adds a callback to be called when the list of loaded site lists changes.
-  // The callback will also be called immediately, to get the current
-  // site lists.
-  void AddSiteListsChangedCallback(const SiteListsChangedCallback& callback);
-
-  // Returns a map (from CRX ID to name) of allowlists to be installed,
-  // specified on the command line.
-  static std::map<std::string, std::string> GetAllowlistsFromCommandLine();
-
-  // Loads an already existing allowlist on disk (i.e. without downloading it as
-  // a component).
-  void LoadAllowlistForTesting(const std::string& id,
-                               const base::string16& title,
-                               const base::FilePath& path);
-
-  // Unloads a allowlist. Public for testing.
-  void UnloadAllowlist(const std::string& id);
-
-  // Creates Sync data for a allowlist with the given |id| and |name|.
-  // Public for testing.
-  static syncer::SyncData CreateAllowlistSyncData(const std::string& id,
-                                                  const std::string& name);
-
-  // SyncableService implementation:
-  void WaitUntilReadyToSync(base::OnceClosure done) override;
-  base::Optional<syncer::ModelError> MergeDataAndStartSyncing(
-      syncer::ModelType type,
-      const syncer::SyncDataList& initial_sync_data,
-      std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
-      std::unique_ptr<syncer::SyncErrorFactory> error_handler) override;
-  void StopSyncing(syncer::ModelType type) override;
-  base::Optional<syncer::ModelError> ProcessSyncChanges(
-      const base::Location& from_here,
-      const syncer::SyncChangeList& change_list) override;
-
-  syncer::SyncDataList GetAllSyncDataForTesting(syncer::ModelType type) const;
-
- private:
-  // The following methods handle allowlist additions, updates and removals,
-  // usually coming from Sync.
-  void AddNewAllowlist(base::DictionaryValue* pref_dict,
-                       const sync_pb::ManagedUserWhitelistSpecifics& allowlist);
-  void SetAllowlistProperties(
-      base::DictionaryValue* pref_dict,
-      const sync_pb::ManagedUserWhitelistSpecifics& allowlist);
-  void RemoveAllowlist(base::DictionaryValue* pref_dict, const std::string& id);
-
-  enum AllowlistSource {
-    FROM_SYNC,
-    FROM_COMMAND_LINE,
-  };
-
-  // Registers a new or existing allowlist.
-  void RegisterAllowlist(const std::string& id,
-                         const std::string& name,
-                         AllowlistSource source);
-
-  void GetLoadedAllowlists(
-      std::vector<scoped_refptr<SupervisedUserSiteList>>* allowlists);
-
-  void NotifyAllowlistsChanged();
-
-  void OnAllowlistReady(const std::string& id,
-                        const base::string16& title,
-                        const base::FilePath& large_icon_path,
-                        const base::FilePath& allowlist_path);
-  void OnAllowlistLoaded(
-      const std::string& id,
-      const scoped_refptr<SupervisedUserSiteList>& allowlist);
-
-  PrefService* prefs_;
-  component_updater::SupervisedUserWhitelistInstaller* installer_;
-
-  std::string client_id_;
-  std::vector<SiteListsChangedCallback> site_lists_changed_callbacks_;
-
-  // The set of registered allowlists. A allowlist might be registered but not
-  // loaded yet, in which case it will not be in |loaded_allowlists_| yet.
-  // On the other hand, every loaded allowlist has to be registered.
-  std::set<std::string> registered_allowlists_;
-  std::map<std::string, scoped_refptr<SupervisedUserSiteList>>
-      loaded_allowlists_;
-
-  base::WeakPtrFactory<SupervisedUserAllowlistService> weak_ptr_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(SupervisedUserAllowlistService);
-};
-
-#endif  // CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_ALLOWLIST_SERVICE_H_
diff --git a/chrome/browser/supervised_user/supervised_user_allowlist_service_unittest.cc b/chrome/browser/supervised_user/supervised_user_allowlist_service_unittest.cc
deleted file mode 100644
index 0201aea..0000000
--- a/chrome/browser/supervised_user/supervised_user_allowlist_service_unittest.cc
+++ /dev/null
@@ -1,273 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/supervised_user/supervised_user_allowlist_service.h"
-
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/path_service.h"
-#include "base/run_loop.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "build/build_config.h"
-#include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
-#include "chrome/browser/supervised_user/supervised_user_site_list.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/prefs/scoped_user_pref_update.h"
-#include "components/sync/model/sync_change.h"
-#include "components/sync/model/sync_change_processor.h"
-#include "components/sync/model/sync_error_factory.h"
-#include "components/sync/protocol/sync.pb.h"
-#include "content/public/test/browser_task_environment.h"
-#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-const char kClientId[] = "client-id";
-
-class MockSupervisedUserWhitelistInstaller
-    : public component_updater::SupervisedUserWhitelistInstaller {
- public:
-  MockSupervisedUserWhitelistInstaller() {}
-  ~MockSupervisedUserWhitelistInstaller() override {}
-
-  const std::set<std::string>& registered_allowlists() {
-    return registered_allowlists_;
-  }
-
-  void NotifyAllowlistReady(const std::string& crx_id,
-                            const base::string16& title,
-                            const base::FilePath& large_icon_path,
-                            const base::FilePath& allowlist_path) {
-    for (const auto& callback : ready_callbacks_)
-      callback.Run(crx_id, title, large_icon_path, allowlist_path);
-  }
-
-  // SupervisedUserWhitelistInstaller implementation:
-  void RegisterComponents() override {}
-
-  void Subscribe(WhitelistReadyCallback callback) override {
-    ready_callbacks_.push_back(callback);
-  }
-
-  void RegisterWhitelist(const std::string& client_id,
-                         const std::string& crx_id,
-                         const std::string& name) override {
-    EXPECT_EQ(kClientId, client_id);
-    EXPECT_FALSE(AllowlistIsRegistered(crx_id)) << crx_id;
-    registered_allowlists_.insert(crx_id);
-  }
-
-  void UnregisterWhitelist(const std::string& client_id,
-                           const std::string& crx_id) override {
-    EXPECT_EQ(kClientId, client_id);
-    EXPECT_TRUE(AllowlistIsRegistered(crx_id)) << crx_id;
-    registered_allowlists_.erase(crx_id);
-  }
-
- private:
-  bool AllowlistIsRegistered(const std::string& crx_id) {
-    return registered_allowlists_.count(crx_id) > 0;
-  }
-
-  std::set<std::string> registered_allowlists_;
-  std::vector<WhitelistReadyCallback> ready_callbacks_;
-};
-
-}  // namespace
-
-class SupervisedUserAllowlistServiceTest : public testing::Test {
- public:
-  SupervisedUserAllowlistServiceTest()
-      : installer_(new MockSupervisedUserWhitelistInstaller),
-        service_(new SupervisedUserAllowlistService(profile_.GetPrefs(),
-                                                    installer_.get(),
-                                                    kClientId)) {
-    service_->AddSiteListsChangedCallback(
-        base::Bind(&SupervisedUserAllowlistServiceTest::OnSiteListsChanged,
-                   base::Unretained(this)));
-  }
-
- protected:
-  void PrepareInitialStateAndPreferences() {
-    // Create two allowlists.
-    DictionaryPrefUpdate update(profile_.GetPrefs(),
-                                prefs::kSupervisedUserAllowlists);
-    base::DictionaryValue* dict = update.Get();
-
-    std::unique_ptr<base::DictionaryValue> allowlist_dict(
-        new base::DictionaryValue);
-    allowlist_dict->SetString("name", "Allowlist A");
-    dict->Set("aaaa", std::move(allowlist_dict));
-
-    allowlist_dict.reset(new base::DictionaryValue);
-    allowlist_dict->SetString("name", "Allowlist B");
-    dict->Set("bbbb", std::move(allowlist_dict));
-
-    installer_->RegisterWhitelist(kClientId, "aaaa", "Allowlist A");
-    installer_->RegisterWhitelist(kClientId, "bbbb", "Allowlist B");
-  }
-
-  void CheckFinalStateAndPreferences() {
-    EXPECT_EQ(2u, installer_->registered_allowlists().size());
-    EXPECT_EQ(1u, installer_->registered_allowlists().count("bbbb"));
-    EXPECT_EQ(1u, installer_->registered_allowlists().count("cccc"));
-
-    const base::DictionaryValue* dict =
-        profile_.GetPrefs()->GetDictionary(prefs::kSupervisedUserAllowlists);
-    EXPECT_EQ(2u, dict->size());
-    const base::DictionaryValue* allowlist_dict = nullptr;
-    ASSERT_TRUE(dict->GetDictionary("bbbb", &allowlist_dict));
-    std::string name;
-    ASSERT_TRUE(allowlist_dict->GetString("name", &name));
-    EXPECT_EQ("Allowlist B New", name);
-    ASSERT_TRUE(dict->GetDictionary("cccc", &allowlist_dict));
-    ASSERT_TRUE(allowlist_dict->GetString("name", &name));
-    EXPECT_EQ("Allowlist C", name);
-  }
-
-  const sync_pb::ManagedUserWhitelistSpecifics* FindAllowlist(
-      const syncer::SyncDataList& data_list,
-      const std::string& id) {
-    for (const syncer::SyncData& data : data_list) {
-      const sync_pb::ManagedUserWhitelistSpecifics& allowlist =
-          data.GetSpecifics().managed_user_whitelist();
-      if (allowlist.id() == id)
-        return &allowlist;
-    }
-    return nullptr;
-  }
-
-  void OnSiteListsChanged(
-      const std::vector<scoped_refptr<SupervisedUserSiteList>>& site_lists) {
-    site_lists_ = site_lists;
-    if (!site_lists_changed_callback_.is_null())
-      site_lists_changed_callback_.Run();
-  }
-
-  content::BrowserTaskEnvironment task_environment_;
-  TestingProfile profile_;
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
-
-  std::unique_ptr<MockSupervisedUserWhitelistInstaller> installer_;
-  std::unique_ptr<SupervisedUserAllowlistService> service_;
-
-  std::vector<scoped_refptr<SupervisedUserSiteList>> site_lists_;
-  base::Closure site_lists_changed_callback_;
-};
-
-TEST_F(SupervisedUserAllowlistServiceTest, MergeEmpty) {
-  service_->Init();
-
-  ASSERT_TRUE(service_
-                  ->GetAllSyncDataForTesting(
-                      syncer::DEPRECATED_SUPERVISED_USER_ALLOWLISTS)
-                  .empty());
-  base::Optional<syncer::ModelError> error = service_->MergeDataAndStartSyncing(
-      syncer::DEPRECATED_SUPERVISED_USER_ALLOWLISTS, syncer::SyncDataList(),
-      std::unique_ptr<syncer::SyncChangeProcessor>(),
-      std::unique_ptr<syncer::SyncErrorFactory>());
-  EXPECT_TRUE(service_
-                  ->GetAllSyncDataForTesting(
-                      syncer::DEPRECATED_SUPERVISED_USER_ALLOWLISTS)
-                  .empty());
-  EXPECT_FALSE(error.has_value());
-
-  EXPECT_EQ(0u, installer_->registered_allowlists().size());
-}
-
-TEST_F(SupervisedUserAllowlistServiceTest, MergeExisting) {
-  PrepareInitialStateAndPreferences();
-
-  // Initialize the service. The allowlists should not be ready yet.
-  service_->Init();
-  EXPECT_EQ(0u, site_lists_.size());
-
-  // Notify that allowlist A is ready.
-  base::RunLoop run_loop;
-  site_lists_changed_callback_ = run_loop.QuitClosure();
-  base::FilePath test_data_dir;
-  ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
-  base::FilePath allowlist_path =
-      test_data_dir.AppendASCII("allowlists/content_pack/site_list.json");
-  installer_->NotifyAllowlistReady("aaaa", base::ASCIIToUTF16("Title"),
-                                   base::FilePath(), allowlist_path);
-  run_loop.Run();
-
-  ASSERT_EQ(1u, site_lists_.size());
-  EXPECT_EQ(base::ASCIIToUTF16("Title"), site_lists_[0]->title());
-  EXPECT_EQ(4u, site_lists_[0]->patterns().size());
-
-  // Do the initial merge. One item should be added (allowlist C), one should be
-  // modified (allowlist B), and one item should be removed (allowlist A).
-  syncer::SyncDataList initial_data;
-  initial_data.push_back(
-      SupervisedUserAllowlistService::CreateAllowlistSyncData(
-          "bbbb", "Allowlist B New"));
-  initial_data.push_back(
-      SupervisedUserAllowlistService::CreateAllowlistSyncData("cccc",
-                                                              "Allowlist C"));
-  ASSERT_EQ(2u, service_
-                    ->GetAllSyncDataForTesting(
-                        syncer::DEPRECATED_SUPERVISED_USER_ALLOWLISTS)
-                    .size());
-  base::Optional<syncer::ModelError> error = service_->MergeDataAndStartSyncing(
-      syncer::DEPRECATED_SUPERVISED_USER_ALLOWLISTS, initial_data,
-      std::unique_ptr<syncer::SyncChangeProcessor>(),
-      std::unique_ptr<syncer::SyncErrorFactory>());
-  EXPECT_EQ(2u, service_
-                    ->GetAllSyncDataForTesting(
-                        syncer::DEPRECATED_SUPERVISED_USER_ALLOWLISTS)
-                    .size());
-  EXPECT_FALSE(error.has_value());
-
-  // Allowlist A (which was previously ready) should be removed now, and
-  // allowlist B was never ready.
-  EXPECT_EQ(0u, site_lists_.size());
-
-  CheckFinalStateAndPreferences();
-}
-
-TEST_F(SupervisedUserAllowlistServiceTest, ApplyChanges) {
-  PrepareInitialStateAndPreferences();
-
-  service_->Init();
-
-  // Process some changes.
-  syncer::SyncChangeList changes;
-  changes.push_back(syncer::SyncChange(
-      FROM_HERE, syncer::SyncChange::ACTION_ADD,
-      SupervisedUserAllowlistService::CreateAllowlistSyncData("cccc",
-                                                              "Allowlist C")));
-  changes.push_back(syncer::SyncChange(
-      FROM_HERE, syncer::SyncChange::ACTION_UPDATE,
-      SupervisedUserAllowlistService::CreateAllowlistSyncData(
-          "bbbb", "Allowlist B New")));
-  changes.push_back(syncer::SyncChange(
-      FROM_HERE, syncer::SyncChange::ACTION_DELETE,
-      SupervisedUserAllowlistService::CreateAllowlistSyncData("aaaa",
-                                                              "Ignored")));
-  base::Optional<syncer::ModelError> error =
-      service_->ProcessSyncChanges(FROM_HERE, changes);
-  EXPECT_FALSE(error.has_value());
-
-  EXPECT_EQ(0u, site_lists_.size());
-
-  // If allowlist A now becomes ready, it should be ignored.
-  installer_->NotifyAllowlistReady(
-      "aaaa", base::ASCIIToUTF16("Title"), base::FilePath(),
-      base::FilePath(FILE_PATH_LITERAL("/path/to/aaaa")));
-  EXPECT_EQ(0u, site_lists_.size());
-
-  CheckFinalStateAndPreferences();
-}
diff --git a/chrome/browser/supervised_user/supervised_user_service.cc b/chrome/browser/supervised_user/supervised_user_service.cc
index 6c42234..cfe0f03 100644
--- a/chrome/browser/supervised_user/supervised_user_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_service.cc
@@ -25,11 +25,9 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_key.h"
 #include "chrome/browser/supervised_user/permission_request_creator.h"
-#include "chrome/browser/supervised_user/supervised_user_allowlist_service.h"
 #include "chrome/browser/supervised_user/supervised_user_constants.h"
 #include "chrome/browser/supervised_user/supervised_user_features.h"
 #include "chrome/browser/supervised_user/supervised_user_filtering_switches.h"
@@ -163,10 +161,6 @@
       base::Bind(&SupervisedUserService::OnSupervisedUserIdChanged,
           base::Unretained(this)));
 
-  allowlist_service_->AddSiteListsChangedCallback(
-      base::Bind(&SupervisedUserService::OnSiteListsChanged,
-                 weak_ptr_factory_.GetWeakPtr()));
-
   SetActive(IsChild());
 }
 
@@ -186,9 +180,6 @@
   return &url_filter_;
 }
 
-SupervisedUserAllowlistService* SupervisedUserService::GetAllowlistService() {
-  return allowlist_service_.get();
-}
 
 bool SupervisedUserService::AccessRequestsEnabled() {
   return FindEnabledPermissionRequestCreator(0) < permissions_creators_.size();
@@ -327,12 +318,6 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   registry_observer_.Add(extensions::ExtensionRegistry::Get(profile));
 #endif
-
-  std::string client_id = component_updater::SupervisedUserWhitelistInstaller::
-      ClientIdForProfilePath(profile_->GetPath());
-  allowlist_service_ = std::make_unique<SupervisedUserAllowlistService>(
-      profile_->GetPrefs(),
-      g_browser_process->supervised_user_whitelist_installer(), client_id);
 }
 
 void SupervisedUserService::SetPrimaryPermissionCreatorForTest(
@@ -487,7 +472,6 @@
     // Initialize the filter.
     OnDefaultFilteringBehaviorChanged();
     OnSafeSitesSettingChanged();
-    allowlist_service_->Init();
     UpdateManualHosts();
     UpdateManualURLs();
 
@@ -636,12 +620,6 @@
   }
 }
 
-void SupervisedUserService::OnSiteListsChanged(
-    const std::vector<scoped_refptr<SupervisedUserSiteList> >& site_lists) {
-  allowlists_ = site_lists;
-  url_filter_.LoadAllowlists(site_lists);
-}
-
 void SupervisedUserService::LoadDenylist(const base::FilePath& path,
                                          const GURL& url) {
   DCHECK(denylist_state_ == DenylistLoadState::NOT_LOADED);
diff --git a/chrome/browser/supervised_user/supervised_user_service.h b/chrome/browser/supervised_user/supervised_user_service.h
index 38672ab..69316ad 100644
--- a/chrome/browser/supervised_user/supervised_user_service.h
+++ b/chrome/browser/supervised_user/supervised_user_service.h
@@ -45,9 +45,7 @@
 class Profile;
 class SupervisedUserServiceObserver;
 class SupervisedUserSettingsService;
-class SupervisedUserSiteList;
 class SupervisedUserURLFilter;
-class SupervisedUserAllowlistService;
 
 namespace base {
 class FilePath;
@@ -69,8 +67,8 @@
 #endif  // !defined(OS_ANDROID)
 
 // This class handles all the information related to a given supervised profile
-// (e.g. the installed content packs, the default URL filtering behavior, or
-// manual allowlist/denylist overrides).
+// (e.g. the default URL filtering behavior, or manual allowlist/denylist
+// overrides).
 class SupervisedUserService : public KeyedService,
 #if BUILDFLAG(ENABLE_EXTENSIONS)
                               public extensions::ExtensionRegistryObserver,
@@ -118,13 +116,6 @@
   // on the UI thread.
   SupervisedUserURLFilter* GetURLFilter();
 
-  // Returns the allowlist service.
-  SupervisedUserAllowlistService* GetAllowlistService();
-
-  const std::vector<scoped_refptr<SupervisedUserSiteList>>& allowlists() const {
-    return allowlists_;
-  }
-
   // Whether the user can request to get access to blocked URLs or to new
   // extensions.
   bool AccessRequestsEnabled();
@@ -343,9 +334,6 @@
 
   void UpdateAsyncUrlChecker();
 
-  void OnSiteListsChanged(
-      const std::vector<scoped_refptr<SupervisedUserSiteList>>& site_lists);
-
   // Asynchronously loads a denylist from a binary file at |path| and applies
   // it to the URL filters. If no file exists at |path| yet, downloads a file
   // from |url| and stores it at |path| first.
@@ -406,10 +394,6 @@
   SupervisedUserDenylist denylist_;
   std::unique_ptr<FileDownloader> denylist_downloader_;
 
-  std::unique_ptr<SupervisedUserAllowlistService> allowlist_service_;
-
-  std::vector<scoped_refptr<SupervisedUserSiteList>> allowlists_;
-
   // Used to create permission requests.
   std::vector<std::unique_ptr<PermissionRequestCreator>> permissions_creators_;
 
diff --git a/chrome/browser/supervised_user/supervised_user_service_unittest.cc b/chrome/browser/supervised_user/supervised_user_service_unittest.cc
index 74ae409..739091c 100644
--- a/chrome/browser/supervised_user/supervised_user_service_unittest.cc
+++ b/chrome/browser/supervised_user/supervised_user_service_unittest.cc
@@ -23,14 +23,10 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/browser/supervised_user/permission_request_creator.h"
-#include "chrome/browser/supervised_user/supervised_user_allowlist_service.h"
 #include "chrome/browser/supervised_user/supervised_user_features.h"
 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/prefs/pref_service.h"
-#include "components/prefs/scoped_user_pref_update.h"
 #include "components/version_info/version_info.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_utils.h"
@@ -116,37 +112,6 @@
   DISALLOW_COPY_AND_ASSIGN(SupervisedUserURLFilterObserver);
 };
 
-class SiteListObserver : public AsyncTestHelper {
- public:
-  SiteListObserver() {}
-  ~SiteListObserver() {}
-
-  void Init(SupervisedUserAllowlistService* service) {
-    service->AddSiteListsChangedCallback(base::Bind(
-        &SiteListObserver::OnSiteListsChanged, base::Unretained(this)));
-
-    // The initial call to AddSiteListsChangedCallback will call
-    // OnSiteListsChanged(), so we balance it out by calling Wait().
-    Wait();
-  }
-
-  const std::vector<scoped_refptr<SupervisedUserSiteList>>& site_lists() {
-    return site_lists_;
-  }
-
- private:
-  void OnSiteListsChanged(
-      const std::vector<scoped_refptr<SupervisedUserSiteList>>& site_lists) {
-    site_lists_ = site_lists;
-
-    QuitRunLoop();
-  }
-
-  std::vector<scoped_refptr<SupervisedUserSiteList>> site_lists_;
-
-  DISALLOW_COPY_AND_ASSIGN(SiteListObserver);
-};
-
 class AsyncResultHolder {
  public:
   AsyncResultHolder() : result_(false) {}
@@ -351,15 +316,11 @@
     SupervisedUserService* service =
         SupervisedUserServiceFactory::GetForProfile(profile_.get());
     service->Init();
-    site_list_observer_.Init(service->GetAllowlistService());
 
     SupervisedUserURLFilter* url_filter = service->GetURLFilter();
     url_filter->SetBlockingTaskRunnerForTesting(
         base::ThreadTaskRunnerHandle::Get());
     url_filter_observer_.Init(url_filter);
-
-    // Wait for the initial update to finish.
-    url_filter_observer_.Wait();
   }
 
   void TearDown() override {
@@ -395,7 +356,6 @@
 
   bool is_supervised_;
   extensions::ScopedCurrentChannel channel_;
-  SiteListObserver site_list_observer_;
   SupervisedUserURLFilterObserver url_filter_observer_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -520,106 +480,4 @@
   EXPECT_FALSE(supervised_user_service->GetDebugPolicyProviderName().empty());
 #endif
 }
-
-TEST_F(SupervisedUserServiceExtensionTest, NoContentPacks) {
-  InitSupervisedUserInitiatedExtensionInstallFeature(true);
-
-  SupervisedUserService* supervised_user_service =
-      SupervisedUserServiceFactory::GetForProfile(profile_.get());
-  SupervisedUserURLFilter* url_filter = supervised_user_service->GetURLFilter();
-
-  // ASSERT_EQ instead of ASSERT_TRUE([...].empty()) so that the error
-  // message contains the size in case of failure.
-  ASSERT_EQ(0u, site_list_observer_.site_lists().size());
-
-  GURL url("http://youtube.com");
-  EXPECT_EQ(SupervisedUserURLFilter::ALLOW,
-            url_filter->GetFilteringBehaviorForURL(url));
-}
-
-TEST_F(SupervisedUserServiceExtensionTest, InstallContentPacks) {
-  InitSupervisedUserInitiatedExtensionInstallFeature(true);
-
-  SupervisedUserService* supervised_user_service =
-      SupervisedUserServiceFactory::GetForProfile(profile_.get());
-  SupervisedUserURLFilter* url_filter = supervised_user_service->GetURLFilter();
-
-  const std::string id1 = "ID 1";
-  const base::string16 title1 = base::ASCIIToUTF16("Title 1");
-  const std::string id2 = "ID 2";
-  const base::string16 title2 = base::ASCIIToUTF16("Title 2");
-
-  GURL youtube_url("http://www.youtube.com");
-  GURL moose_url("http://moose.org");
-  EXPECT_EQ(SupervisedUserURLFilter::ALLOW,
-            url_filter->GetFilteringBehaviorForURL(youtube_url));
-
-  profile_->GetPrefs()->SetInteger(
-      prefs::kDefaultSupervisedUserFilteringBehavior,
-      SupervisedUserURLFilter::BLOCK);
-  EXPECT_EQ(SupervisedUserURLFilter::BLOCK,
-            url_filter->GetFilteringBehaviorForURL(youtube_url));
-
-  profile_->GetPrefs()->SetInteger(
-      prefs::kDefaultSupervisedUserFilteringBehavior,
-      SupervisedUserURLFilter::WARN);
-  EXPECT_EQ(SupervisedUserURLFilter::WARN,
-            url_filter->GetFilteringBehaviorForURL(youtube_url));
-
-  // Load a allowlist.
-  base::FilePath test_data_dir;
-  ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
-  SupervisedUserAllowlistService* allowlist_service =
-      supervised_user_service->GetAllowlistService();
-  base::FilePath allowlist_path =
-      test_data_dir.AppendASCII("allowlists/content_pack/site_list.json");
-  allowlist_service->LoadAllowlistForTesting(id1, title1, allowlist_path);
-  site_list_observer_.Wait();
-
-  ASSERT_EQ(1u, site_list_observer_.site_lists().size());
-  EXPECT_EQ(id1, site_list_observer_.site_lists()[0]->id());
-  EXPECT_EQ(title1, site_list_observer_.site_lists()[0]->title());
-  EXPECT_EQ(youtube_url, site_list_observer_.site_lists()[0]->entry_point());
-
-  url_filter_observer_.Wait();
-  EXPECT_EQ(SupervisedUserURLFilter::ALLOW,
-            url_filter->GetFilteringBehaviorForURL(youtube_url));
-  EXPECT_EQ(SupervisedUserURLFilter::WARN,
-            url_filter->GetFilteringBehaviorForURL(moose_url));
-
-  // Load a second allowlist.
-  allowlist_path =
-      test_data_dir.AppendASCII("allowlists/content_pack_2/site_list.json");
-  allowlist_service->LoadAllowlistForTesting(id2, title2, allowlist_path);
-  site_list_observer_.Wait();
-
-  ASSERT_EQ(2u, site_list_observer_.site_lists().size());
-  EXPECT_EQ(id1, site_list_observer_.site_lists()[0]->id());
-  EXPECT_EQ(title1, site_list_observer_.site_lists()[0]->title());
-  EXPECT_EQ(youtube_url, site_list_observer_.site_lists()[0]->entry_point());
-  EXPECT_EQ(id2, site_list_observer_.site_lists()[1]->id());
-  EXPECT_EQ(title2, site_list_observer_.site_lists()[1]->title());
-  EXPECT_TRUE(site_list_observer_.site_lists()[1]->entry_point().is_empty());
-
-  url_filter_observer_.Wait();
-  EXPECT_EQ(SupervisedUserURLFilter::ALLOW,
-            url_filter->GetFilteringBehaviorForURL(youtube_url));
-  EXPECT_EQ(SupervisedUserURLFilter::ALLOW,
-            url_filter->GetFilteringBehaviorForURL(moose_url));
-
-  // Unload the first allowlist.
-  allowlist_service->UnloadAllowlist(id1);
-  site_list_observer_.Wait();
-
-  ASSERT_EQ(1u, site_list_observer_.site_lists().size());
-  EXPECT_EQ(id2, site_list_observer_.site_lists()[0]->id());
-  EXPECT_EQ(title2, site_list_observer_.site_lists()[0]->title());
-  EXPECT_TRUE(site_list_observer_.site_lists()[0]->entry_point().is_empty());
-
-  url_filter_observer_.Wait();
-  EXPECT_EQ(SupervisedUserURLFilter::WARN,
-            url_filter->GetFilteringBehaviorForURL(youtube_url));
-  EXPECT_EQ(SupervisedUserURLFilter::ALLOW,
-            url_filter->GetFilteringBehaviorForURL(moose_url));
-}
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/sync/chrome_sync_client.cc b/chrome/browser/sync/chrome_sync_client.cc
index ba24ff2..6e4cd8a 100644
--- a/chrome/browser/sync/chrome_sync_client.cc
+++ b/chrome/browser/sync/chrome_sync_client.cc
@@ -105,7 +105,6 @@
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
 #include "chrome/browser/profiles/profile_key.h"
-#include "chrome/browser/supervised_user/supervised_user_allowlist_service.h"
 #include "chrome/browser/supervised_user/supervised_user_service.h"
 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
 #include "chrome/browser/supervised_user/supervised_user_settings_service.h"
@@ -382,11 +381,6 @@
       syncer::SUPERVISED_USER_SETTINGS, profile_, dump_stack,
       model_type_store_factory,
       GetSyncableServiceForType(syncer::SUPERVISED_USER_SETTINGS)));
-  controllers.push_back(std::make_unique<SupervisedUserSyncModelTypeController>(
-      syncer::DEPRECATED_SUPERVISED_USER_ALLOWLISTS, profile_, dump_stack,
-      model_type_store_factory,
-      GetSyncableServiceForType(
-          syncer::DEPRECATED_SUPERVISED_USER_ALLOWLISTS)));
 #endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
@@ -611,10 +605,6 @@
       return SupervisedUserSettingsServiceFactory::GetForKey(
                  profile_->GetProfileKey())
           ->AsWeakPtr();
-    case syncer::DEPRECATED_SUPERVISED_USER_ALLOWLISTS:
-      return SupervisedUserServiceFactory::GetForProfile(profile_)
-          ->GetAllowlistService()
-          ->AsWeakPtr();
 #endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     case syncer::ARC_PACKAGE:
diff --git a/chrome/browser/sync/profile_sync_service_factory_unittest.cc b/chrome/browser/sync/profile_sync_service_factory_unittest.cc
index 3925844..9f0d624 100644
--- a/chrome/browser/sync/profile_sync_service_factory_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_factory_unittest.cc
@@ -92,7 +92,6 @@
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
     datatypes.push_back(syncer::SUPERVISED_USER_SETTINGS);
-    datatypes.push_back(syncer::DEPRECATED_SUPERVISED_USER_ALLOWLISTS);
 #endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/translate/translate_manager_browsertest.cc b/chrome/browser/translate/translate_manager_browsertest.cc
index cb56c77..60598f7 100644
--- a/chrome/browser/translate/translate_manager_browsertest.cc
+++ b/chrome/browser/translate/translate_manager_browsertest.cc
@@ -402,9 +402,9 @@
 }
 
 // Tests that the language detection / HTML attribute override works correctly.
-// For languages in the whitelist, the detected language should override the
-// HTML attribute. For all other languages, the HTML attribute should be used.
-// Flaky on all platforms. https://crbug.com/1148703
+// For languages in the always-translate list, the detected language should
+// override the HTML attribute. For all other languages, the HTML attribute
+// should be used. Flaky on all platforms. https://crbug.com/1148703
 IN_PROC_BROWSER_TEST_F(TranslateManagerBrowserTest,
                        DISABLED_PageLanguageDetectionConflict) {
   ChromeTranslateClient* chrome_translate_client = GetChromeTranslateClient();
@@ -791,8 +791,9 @@
       ->SetIgnoreMissingKeyForTesting(true);
 
   // Before browsing: set auto translate from French to Chinese.
-  GetChromeTranslateClient()->GetTranslatePrefs()->WhitelistLanguagePair(
-      "fr", "zh-CN");
+  GetChromeTranslateClient()
+      ->GetTranslatePrefs()
+      ->AddLanguagePairToAlwaysTranslateList("fr", "zh-CN");
 
   ClickFrenchHrefTranslateLinkOnGooglePage();
 
@@ -846,7 +847,7 @@
   GetChromeTranslateClient()
       ->GetTranslateManager()
       ->SetIgnoreMissingKeyForTesting(true);
-  GetChromeTranslateClient()->GetTranslatePrefs()->BlacklistSite(
+  GetChromeTranslateClient()->GetTranslatePrefs()->AddSiteToNeverPromptList(
       "www.google.com");
 
   ClickFrenchHrefTranslateLinkOnGooglePage();
@@ -874,7 +875,7 @@
       ->SetIgnoreMissingKeyForTesting(true);
   GetChromeTranslateClient()->GetTranslatePrefs()->AddToLanguageList("fr",
                                                                      true);
-  GetChromeTranslateClient()->GetTranslatePrefs()->BlacklistSite(
+  GetChromeTranslateClient()->GetTranslatePrefs()->AddSiteToNeverPromptList(
       "www.google.com");
 
   ClickFrenchHrefTranslateLinkOnGooglePage();
@@ -1013,7 +1014,7 @@
   GetChromeTranslateClient()
       ->GetTranslateManager()
       ->SetIgnoreMissingKeyForTesting(true);
-  GetChromeTranslateClient()->GetTranslatePrefs()->BlacklistSite(
+  GetChromeTranslateClient()->GetTranslatePrefs()->AddSiteToNeverPromptList(
       "www.google.com");
 
   ClickFrenchHrefTranslateLinkOnGooglePage();
@@ -1056,7 +1057,7 @@
   GetChromeTranslateClient()
       ->GetTranslateManager()
       ->SetIgnoreMissingKeyForTesting(true);
-  GetChromeTranslateClient()->GetTranslatePrefs()->BlacklistSite(
+  GetChromeTranslateClient()->GetTranslatePrefs()->AddSiteToNeverPromptList(
       "www.google.com");
 
   ClickFrenchHrefTranslateLinkOnGooglePage();
@@ -1533,8 +1534,9 @@
 }
 
 // Tests that the language detection / HTML attribute override works correctly.
-// For languages in the whitelist, the detected language should override the
-// HTML attribute. For all other languages, the HTML attribute should be used.
+// For languages in the always-translate list, the detected language should
+// override the HTML attribute. For all other languages, the HTML attribute
+// should be used.
 IN_PROC_BROWSER_TEST_F(TranslateManagerWithSubFrameSupportBrowserTest,
                        PageLanguageDetectionConflict) {
   // Open a new tab with a page in French with incorrect HTML language
@@ -1859,8 +1861,8 @@
   SetTranslateScript(kTestValidScript);
 
   // Before browsing: set auto translate from French to Chinese.
-  chrome_translate_client->GetTranslatePrefs()->WhitelistLanguagePair("fr",
-                                                                      "zh-CN");
+  chrome_translate_client->GetTranslatePrefs()
+      ->AddLanguagePairToAlwaysTranslateList("fr", "zh-CN");
 
   // Load a German page and detect it's language
   AddTabAtIndex(0,
diff --git a/chrome/browser/translate/translate_manager_render_view_host_unittest.cc b/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
index a10890c..50d8b1a 100644
--- a/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
+++ b/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
@@ -1228,7 +1228,7 @@
       translate::TranslatePrefs::kPrefTranslateBlockedLanguages);
   translate_prefs->AddToLanguageList("fr", /*force_blocked=*/true);
   EXPECT_TRUE(translate_prefs->IsBlockedLanguage("fr"));
-  EXPECT_FALSE(translate_prefs->IsSiteBlacklisted(url.host()));
+  EXPECT_FALSE(translate_prefs->IsSiteOnNeverPromptList(url.host()));
   EXPECT_FALSE(translate_prefs->CanTranslateLanguage(accept_languages, "fr"));
 
   EXPECT_TRUE(CloseTranslateInfoBar());
@@ -1239,12 +1239,12 @@
   // There should not be a translate infobar.
   EXPECT_TRUE(GetTranslateInfoBar() == NULL);
 
-  // Remove the language from the blacklist.
+  // Remove the language from the blocklist.
   SetPrefObserverExpectation(
       translate::TranslatePrefs::kPrefTranslateBlockedLanguages);
   translate_prefs->UnblockLanguage("fr");
   EXPECT_FALSE(translate_prefs->IsBlockedLanguage("fr"));
-  EXPECT_FALSE(translate_prefs->IsSiteBlacklisted(url.host()));
+  EXPECT_FALSE(translate_prefs->IsSiteOnNeverPromptList(url.host()));
   EXPECT_TRUE(translate_prefs->CanTranslateLanguage(accept_languages, "fr"));
 
   // Navigate to a page in French.
@@ -1273,19 +1273,18 @@
   PrefService* prefs = profile->GetPrefs();
   PrefChangeRegistrar registrar;
   registrar.Init(prefs);
-  registrar.Add(
-      translate::TranslatePrefs::kPrefTranslateSiteBlacklistDeprecated,
-      pref_callback_);
+  registrar.Add(translate::TranslatePrefs::kPrefNeverPromptSitesDeprecated,
+                pref_callback_);
   std::unique_ptr<translate::TranslatePrefs> translate_prefs(
       ChromeTranslateClient::CreateTranslatePrefs(prefs));
-  EXPECT_FALSE(translate_prefs->IsSiteBlacklisted(host));
+  EXPECT_FALSE(translate_prefs->IsSiteOnNeverPromptList(host));
   translate::TranslateAcceptLanguages* accept_languages =
       ChromeTranslateClient::GetTranslateAcceptLanguages(profile);
   EXPECT_TRUE(translate_prefs->CanTranslateLanguage(accept_languages, "fr"));
   SetPrefObserverExpectation(
-      translate::TranslatePrefs::kPrefTranslateSiteBlacklistDeprecated);
-  translate_prefs->BlacklistSite(host);
-  EXPECT_TRUE(translate_prefs->IsSiteBlacklisted(host));
+      translate::TranslatePrefs::kPrefNeverPromptSitesDeprecated);
+  translate_prefs->AddSiteToNeverPromptList(host);
+  EXPECT_TRUE(translate_prefs->IsSiteOnNeverPromptList(host));
   EXPECT_TRUE(translate_prefs->CanTranslateLanguage(accept_languages, "fr"));
 
   EXPECT_TRUE(CloseTranslateInfoBar());
@@ -1296,11 +1295,11 @@
   // There should not be a translate infobar.
   EXPECT_TRUE(GetTranslateInfoBar() == NULL);
 
-  // Remove the site from the blacklist.
+  // Remove the site from the never-prompt list.
   SetPrefObserverExpectation(
-      translate::TranslatePrefs::kPrefTranslateSiteBlacklistDeprecated);
-  translate_prefs->RemoveSiteFromBlacklist(host);
-  EXPECT_FALSE(translate_prefs->IsSiteBlacklisted(host));
+      translate::TranslatePrefs::kPrefNeverPromptSitesDeprecated);
+  translate_prefs->RemoveSiteFromNeverPromptList(host);
+  EXPECT_FALSE(translate_prefs->IsSiteOnNeverPromptList(host));
   EXPECT_TRUE(translate_prefs->CanTranslateLanguage(accept_languages, "fr"));
 
   // Navigate to a page in French.
@@ -1322,13 +1321,13 @@
   PrefService* prefs = profile->GetPrefs();
   PrefChangeRegistrar registrar;
   registrar.Init(prefs);
-  registrar.Add(translate::TranslatePrefs::kPrefTranslateWhitelists,
+  registrar.Add(translate::TranslatePrefs::kPrefAlwaysTranslateLists,
                 pref_callback_);
   std::unique_ptr<translate::TranslatePrefs> translate_prefs(
       ChromeTranslateClient::CreateTranslatePrefs(prefs));
   SetPrefObserverExpectation(
-      translate::TranslatePrefs::kPrefTranslateWhitelists);
-  translate_prefs->WhitelistLanguagePair("fr", "en");
+      translate::TranslatePrefs::kPrefAlwaysTranslateLists);
+  translate_prefs->AddLanguagePairToAlwaysTranslateList("fr", "en");
 
   // Load a page in French.
   SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
@@ -1365,8 +1364,8 @@
   // Now revert the always translate pref and make sure we go back to expected
   // behavior, which is show a "before translate" infobar.
   SetPrefObserverExpectation(
-      translate::TranslatePrefs::kPrefTranslateWhitelists);
-  translate_prefs->RemoveLanguagePairFromWhitelist("fr", "en");
+      translate::TranslatePrefs::kPrefAlwaysTranslateLists);
+  translate_prefs->RemoveLanguagePairFromAlwaysTranslateList("fr", "en");
   SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
   EXPECT_FALSE(GetTranslateMessage(&original_lang, &target_lang));
   infobar = GetTranslateInfoBar();
@@ -1381,16 +1380,16 @@
   if (TranslateService::IsTranslateBubbleEnabled())
     return;
 
-  // Blacklist www.google.fr and French for translation.
+  // Never prompt www.google.fr and French for translation.
   GURL url("http://www.google.fr");
   Profile* profile =
       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
   std::unique_ptr<translate::TranslatePrefs> translate_prefs(
       ChromeTranslateClient::CreateTranslatePrefs(profile->GetPrefs()));
   translate_prefs->AddToLanguageList("fr", /*force_blocked=*/true);
-  translate_prefs->BlacklistSite(url.host());
+  translate_prefs->AddSiteToNeverPromptList(url.host());
   EXPECT_TRUE(translate_prefs->IsBlockedLanguage("fr"));
-  EXPECT_TRUE(translate_prefs->IsSiteBlacklisted(url.host()));
+  EXPECT_TRUE(translate_prefs->IsSiteOnNeverPromptList(url.host()));
 
   // Simulate navigating to a page in French. The translate menu should show but
   // should only be enabled when the page language has been received.
@@ -1421,9 +1420,9 @@
   EXPECT_EQ("fr", original_lang);
   EXPECT_EQ("en", target_lang);
 
-  // This should also have reverted the blacklisting of this site and language.
+  // This should also have reverted the blocklisting of this site and language.
   EXPECT_FALSE(translate_prefs->IsBlockedLanguage("fr"));
-  EXPECT_FALSE(translate_prefs->IsSiteBlacklisted(url.host()));
+  EXPECT_FALSE(translate_prefs->IsSiteOnNeverPromptList(url.host()));
 
   // Let's simulate the page being translated.
   SimulateOnPageTranslated("fr", "en");
@@ -1523,7 +1522,7 @@
   }
   // Simulate the user pressing "Always translate French".
   infobar->AlwaysTranslatePageLanguage();
-  EXPECT_TRUE(translate_prefs->IsLanguagePairWhitelisted("fr", "en"));
+  EXPECT_TRUE(translate_prefs->IsLanguagePairOnAlwaysTranslateList("fr", "en"));
   // Simulate the translate script being retrieved (it only needs to be done
   // once in the test as it is cached).
   SimulateTranslateScriptURLFetch(true);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index d51ce6f..c5f5a12 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -185,6 +185,8 @@
     "uninstall_browser_prompt.h",
     "view_ids.h",
     "webauthn/authenticator_request_dialog.h",
+    "webid/identity_dialog_controller.cc",
+    "webid/identity_dialog_controller.h",
     "webui/about_ui.cc",
     "webui/about_ui.h",
     "webui/autofill_and_password_manager_internals/autofill_internals_ui.cc",
diff --git a/chrome/browser/ui/profile_picker.cc b/chrome/browser/ui/profile_picker.cc
index 7854d4d..5f42b85a 100644
--- a/chrome/browser/ui/profile_picker.cc
+++ b/chrome/browser/ui/profile_picker.cc
@@ -24,10 +24,32 @@
 #include "chrome/browser/notifications/win/notification_launch_id.h"
 #endif
 
+namespace {
+
+ProfilePicker::AvailabilityOnStartup GetAvailabilityOnStartup() {
+  int availability_on_startup = g_browser_process->local_state()->GetInteger(
+      prefs::kBrowserProfilePickerAvailabilityOnStartup);
+  switch (availability_on_startup) {
+    case 0:
+      return ProfilePicker::AvailabilityOnStartup::kEnabled;
+    case 1:
+      return ProfilePicker::AvailabilityOnStartup::kDisabled;
+    case 2:
+      return ProfilePicker::AvailabilityOnStartup::kForced;
+    default:
+      NOTREACHED();
+  }
+  return ProfilePicker::AvailabilityOnStartup::kEnabled;
+}
+
+}  // namespace
+
 // static
 bool ProfilePicker::ShouldShowAtLaunch(
     const base::CommandLine& command_line,
     const std::vector<GURL>& urls_to_launch) {
+  AvailabilityOnStartup availability_on_startup = GetAvailabilityOnStartup();
+
   // Don't show the picker if a certain profile (or an incognito window in the
   // default profile) is explicitly requested.
   if (profiles::IsGuestModeRequested(command_line,
@@ -64,15 +86,26 @@
     return false;
   }
 
+  if (signin_util::IsForceSigninEnabled())
+    return false;
+
+  if (!base::FeatureList::IsEnabled(features::kNewProfilePicker))
+    return false;
+
+  // TODO (crbug/1155158): Move this over the urls check once the
+  // profile picker can forward urls specified in command line.
+  if (availability_on_startup == AvailabilityOnStartup::kForced)
+    return true;
+
   size_t number_of_profiles = g_browser_process->profile_manager()
                                   ->GetProfileAttributesStorage()
                                   .GetNumberOfProfiles();
-  if (signin_util::IsForceSigninEnabled() || number_of_profiles == 1)
+
+  if (number_of_profiles == 1)
     return false;
 
   bool pref_enabled = g_browser_process->local_state()->GetBoolean(
       prefs::kBrowserShowProfilePickerOnStartup);
   base::UmaHistogramBoolean("ProfilePicker.AskOnStartup", pref_enabled);
-  return pref_enabled &&
-         base::FeatureList::IsEnabled(features::kNewProfilePicker);
+  return pref_enabled;
 }
diff --git a/chrome/browser/ui/profile_picker.h b/chrome/browser/ui/profile_picker.h
index 47cc942..854ceecd 100644
--- a/chrome/browser/ui/profile_picker.h
+++ b/chrome/browser/ui/profile_picker.h
@@ -37,6 +37,16 @@
     kMaxValue = kNewSessionOnExistingProcess,
   };
 
+  // Values for the ProfilePickerOnStartupAvailability policy. Should not be
+  // re-numbered. See components/policy/resources/policy_templates.json for
+  // documentation.
+  enum class AvailabilityOnStartup {
+    kEnabled = 0,
+    kDisabled = 1,
+    kForced = 2,
+    kMax = kForced
+  };
+
   // Shows the Profile picker for the given `entry_point` or re-activates an
   // existing one. In the latter case, the displayed page is not updated.
   static void Show(EntryPoint entry_point);
diff --git a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
index d69bec5b..e2a6343 100644
--- a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
+++ b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
@@ -7,6 +7,7 @@
 #include "chrome/browser/themes/theme_service_aura_linux.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/views/theme_profile_key.h"
+#include "ui/base/cursor/cursor_factory.h"
 #include "ui/base/ime/linux/fake_input_method_context_factory.h"
 #include "ui/display/screen.h"
 #include "ui/views/linux_ui/linux_ui.h"
@@ -17,7 +18,6 @@
 #endif
 
 #if defined(USE_OZONE)
-#include "ui/base/cursor/cursor_factory.h"
 #include "ui/base/ime/input_method.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/ozone/public/ozone_platform.h"
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view.cc b/chrome/browser/ui/views/page_action/pwa_install_view.cc
index 0279bfc4..242b47e 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/installable/installable_metrics.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/user_education/feature_promo_controller.h"
+#include "chrome/browser/ui/user_education/feature_promo_text_replacements.h"
 #include "chrome/browser/ui/views/user_education/feature_promo_bubble_params.h"
 #include "chrome/browser/ui/views/user_education/feature_promo_controller_views.h"
 #include "chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.h"
@@ -93,8 +94,11 @@
     if (controller) {
       // Reset the iph flag when it's shown again.
       install_icon_clicked_after_iph_shown_ = false;
-      controller->MaybeShowPromo(
+      controller->MaybeShowPromoWithTextReplacements(
           feature_engagement::kIPHDesktopPwaInstallFeature,
+          FeaturePromoTextReplacements::WithString(
+              banners::AppBannerManager::GetInstallableWebAppName(
+                  web_contents)),
           base::Bind(&PwaInstallView::OnIphClosed,
                      weak_ptr_factory_.GetWeakPtr()));
     }
diff --git a/chrome/browser/ui/views/translate/translate_language_browsertest.cc b/chrome/browser/ui/views/translate/translate_language_browsertest.cc
index 136670c..69c39e0 100644
--- a/chrome/browser/ui/views/translate/translate_language_browsertest.cc
+++ b/chrome/browser/ui/views/translate/translate_language_browsertest.cc
@@ -300,7 +300,7 @@
   InitInIncognitoMode(false);
 
   // Before browsing: set auto translate from French to Chinese.
-  GetTranslatePrefs()->WhitelistLanguagePair("fr", "zh-CN");
+  GetTranslatePrefs()->AddLanguagePairToAlwaysTranslateList("fr", "zh-CN");
   EXPECT_EQ("", GetTranslatePrefs()->GetRecentTargetLanguage());
 
   // Load an Italian page and translate to Spanish. After this, Spanish should
diff --git a/chrome/browser/ui/webid/DIR_METADATA b/chrome/browser/ui/webid/DIR_METADATA
new file mode 100644
index 0000000..b36c62f
--- /dev/null
+++ b/chrome/browser/ui/webid/DIR_METADATA
@@ -0,0 +1,3 @@
+monorail {
+  component: "Blink>Identity>WebID"
+}
diff --git a/chrome/browser/ui/webid/OWNERS b/chrome/browser/ui/webid/OWNERS
new file mode 100644
index 0000000..cfed0af
--- /dev/null
+++ b/chrome/browser/ui/webid/OWNERS
@@ -0,0 +1 @@
+file://content/browser/webid/OWNERS
diff --git a/chrome/browser/ui/webid/identity_dialog_controller.cc b/chrome/browser/ui/webid/identity_dialog_controller.cc
new file mode 100644
index 0000000..f5083222
--- /dev/null
+++ b/chrome/browser/ui/webid/identity_dialog_controller.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 "chrome/browser/ui/webid/identity_dialog_controller.h"
+
+#include "url/gurl.h"
+
+IdentityDialogController::IdentityDialogController() = default;
+
+IdentityDialogController::~IdentityDialogController() = default;
+
+void IdentityDialogController::ShowInitialPermissionDialog(
+    InitialApprovalCallback callback) {
+  // TODO(kenrb): Add UI permission dialog.
+  std::move(callback).Run(
+      content::IdentityRequestDialogController::UserApproval::kApproved);
+}
+
+void IdentityDialogController::ShowIdProviderWindow(
+    const GURL& idp_signin_url,
+    IdProviderWindowClosedCallback callback) {
+  // TODO(kenrb): Add UI modal window to display the IDP UI and load the IDP
+  // page.
+  std::move(callback).Run();
+}
+
+void IdentityDialogController::ShowTokenExchangePermissionDialog(
+    TokenExchangeApprovalCallback callback) {
+  // TODO(kenrb): Add Identity permission dialog.
+  std::move(callback).Run(
+      content::IdentityRequestDialogController::UserApproval::kApproved);
+}
diff --git a/chrome/browser/ui/webid/identity_dialog_controller.h b/chrome/browser/ui/webid/identity_dialog_controller.h
new file mode 100644
index 0000000..072dbfa
--- /dev/null
+++ b/chrome/browser/ui/webid/identity_dialog_controller.h
@@ -0,0 +1,40 @@
+// 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_WEBID_IDENTITY_DIALOG_CONTROLLER_H_
+#define CHROME_BROWSER_UI_WEBID_IDENTITY_DIALOG_CONTROLLER_H_
+
+#include "base/callback.h"
+#include "content/public/browser/identity_request_dialog_controller.h"
+
+class GURL;
+
+using InitialApprovalCallback =
+    content::IdentityRequestDialogController::InitialApprovalCallback;
+using IdProviderWindowClosedCallback =
+    content::IdentityRequestDialogController::IdProviderWindowClosedCallback;
+using TokenExchangeApprovalCallback =
+    content::IdentityRequestDialogController::TokenExchangeApprovalCallback;
+
+// The IdentityDialogController controls the views that are used across
+// browser-mediated federated sign-in flows.
+class IdentityDialogController
+    : public content::IdentityRequestDialogController {
+ public:
+  IdentityDialogController();
+
+  IdentityDialogController(const IdentityDialogController&) = delete;
+  IdentityDialogController& operator=(const IdentityDialogController&) = delete;
+
+  ~IdentityDialogController() override;
+
+  // content::IdentityRequestDelegate
+  void ShowInitialPermissionDialog(InitialApprovalCallback) override;
+  void ShowIdProviderWindow(const GURL& idp_signin_url,
+                            IdProviderWindowClosedCallback) override;
+  void ShowTokenExchangePermissionDialog(
+      TokenExchangeApprovalCallback) override;
+};
+
+#endif  // CHROME_BROWSER_UI_WEBID_IDENTITY_DIALOG_CONTROLLER_H_
diff --git a/chrome/browser/ui/webui/settings/captions_handler.cc b/chrome/browser/ui/webui/settings/captions_handler.cc
index f6a8838..f2e31020 100644
--- a/chrome/browser/ui/webui/settings/captions_handler.cc
+++ b/chrome/browser/ui/webui/settings/captions_handler.cc
@@ -23,7 +23,9 @@
 
 CaptionsHandler::CaptionsHandler(PrefService* prefs) : prefs_(prefs) {}
 
-CaptionsHandler::~CaptionsHandler() = default;
+CaptionsHandler::~CaptionsHandler() {
+  speech::SODAInstaller::GetInstance()->RemoveObserver(this);
+}
 
 void CaptionsHandler::RegisterMessages() {
   web_ui()->RegisterMessageCallback(
diff --git a/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.cc b/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.cc
index 286a74f78..277bf50c 100644
--- a/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "ash/public/cpp/ambient/ambient_backend_controller.h"
+#include "ash/public/cpp/ambient/ambient_metrics.h"
 #include "ash/public/cpp/ambient/common/ambient_settings.h"
 #include "ash/public/cpp/image_downloader.h"
 #include "base/barrier_closure.h"
@@ -221,6 +222,11 @@
         settings_->topic_source = ash::AmbientModeTopicSource::kArtGallery;
       else
         settings_->topic_source = ash::AmbientModeTopicSource::kGooglePhotos;
+
+      ash::ambient::RecordAmbientModeTotalNumberOfAlbums(
+          personal_albums_.albums.size());
+      ash::ambient::RecordAmbientModeSelectedNumberOfAlbums(
+          settings_->selected_album_ids.size());
       break;
     case ash::AmbientModeTopicSource::kArtGallery:
       // For Art gallery, we set the corresponding setting to be enabled or not
diff --git a/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler_unittest.cc
index 8589543..d84e435 100644
--- a/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler_unittest.cc
@@ -9,6 +9,7 @@
 #include "ash/public/cpp/ambient/common/ambient_settings.h"
 #include "ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h"
 #include "ash/public/cpp/test/test_image_downloader.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "content/public/test/test_web_ui.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -52,6 +53,10 @@
 
   content::TestWebUI* web_ui() { return web_ui_.get(); }
 
+  const base::HistogramTester& histogram_tester() const {
+    return histogram_tester_;
+  }
+
   void SetTopicSource(ash::AmbientModeTopicSource topic_source) {
     if (!handler_->settings_)
       handler_->settings_ = ash::AmbientSettings();
@@ -70,6 +75,10 @@
     handler_->HandleRequestAlbums(&args);
   }
 
+  void HandleSetSelectedAlbums(const base::ListValue* args) {
+    handler_->HandleSetSelectedAlbums(args);
+  }
+
   void FetchSettings() {
     handler_->RequestSettingsAndAlbums(/*topic_source=*/base::nullopt);
   }
@@ -213,6 +222,7 @@
       fake_backend_controller_;
   std::unique_ptr<ash::TestImageDownloader> image_downloader_;
   std::unique_ptr<TestAmbientModeHandler> handler_;
+  base::HistogramTester histogram_tester_;
 };
 
 TEST_F(AmbientModeHandlerTest, TestSendTemperatureUnitAndTopicSource) {
@@ -700,5 +710,30 @@
   VerifySettingsSent(topic_source_art_gallery, "celsius");
 }
 
+TEST_F(AmbientModeHandlerTest, TestAlbumNumbersAreRecorded) {
+  RequestSettings();
+  ReplyFetchSettingsAndAlbums(/*success=*/true);
+
+  base::ListValue args;
+  base::DictionaryValue dictionary;
+  ash::AmbientModeTopicSource topic_source =
+      ash::AmbientModeTopicSource::kGooglePhotos;
+  dictionary.SetKey("topicSource", base::Value(static_cast<int>(topic_source)));
+
+  base::Value albums(base::Value::Type::LIST);
+  base::Value album(base::Value::Type::DICTIONARY);
+  album.SetKey("albumId", base::Value("0"));
+  albums.Append(std::move(album));
+  dictionary.SetKey("albums", std::move(albums));
+
+  args.Append(std::move(dictionary));
+  HandleSetSelectedAlbums(&args);
+
+  histogram_tester().ExpectTotalCount("Ash.AmbientMode.TotalNumberOfAlbums",
+                                      /*count=*/1);
+  histogram_tester().ExpectTotalCount("Ash.AmbientMode.SelectedNumberOfAlbums",
+                                      /*count=*/1);
+}
+
 }  // namespace settings
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
index f271889..d25f0a7 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
@@ -43,7 +43,7 @@
     "phoneHubTaskContinuationState";
 const char kPageContentDataWifiSyncStateKey[] = "wifiSyncState";
 const char kPageContentDataSmartLockStateKey[] = "smartLockState";
-const char kIsNotificationAccessGranted[] = "isNotificationAccessGranted";
+const char kNotificationAccessStatus[] = "notificationAccessStatus";
 const char kIsAndroidSmsPairingComplete[] = "isAndroidSmsPairingComplete";
 
 constexpr char kAndroidSmsInfoOriginKey[] = "origin";
@@ -473,11 +473,16 @@
           ? android_sms_pairing_state_tracker_->IsAndroidSmsPairingComplete()
           : false);
 
-  page_content_dictionary->SetBoolean(
-      kIsNotificationAccessGranted,
-      notification_access_manager_
-          ? notification_access_manager_->HasAccessBeenGranted()
-          : false);
+  // TODO(khorimoto): Send prohibited value if notification access is
+  // prohibited.
+  static const int kAccessNotGranted = 1;
+  static const int kAccessGranted = 2;
+  int access_value = kAccessNotGranted;
+  if (notification_access_manager_ &&
+      notification_access_manager_->HasAccessBeenGranted()) {
+    access_value = kAccessGranted;
+  }
+  page_content_dictionary->SetInteger(kNotificationAccessStatus, access_value);
 
   return page_content_dictionary;
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
index 300f6d7..f2b8a5e 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
@@ -301,6 +301,10 @@
        IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_CONNECTION_LOST_WITH_PHONE_SUMMARY},
       {"multideviceNotificationAccessSetupEstablishFailureSummary",
        IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_COULD_NOT_ESTABLISH_CONNECTION_SUMMARY},
+      {"multideviceNotificationAccessSetupAccessProhibitedTitle",
+       IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_ACCESS_PROHIBITED_TITLE},
+      {"multideviceNotificationAccessProhibitedTooltip",
+       IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_PROHIBITED_TOOLTIP},
       {"multideviceInstantTetheringItemTitle",
        IDS_SETTINGS_MULTIDEVICE_INSTANT_TETHERING},
       {"multideviceInstantTetheringItemSummary",
@@ -382,6 +386,15 @@
       l10n_util::GetStringFUTF16(
           IDS_SETTINGS_MULTIDEVICE_PHONE_HUB_NOTIFICATIONS_SUMMARY,
           ui::GetChromeOSDeviceName()));
+  // TODO(https://crbug.com/1144053): Replace with updated URL.
+  html_source->AddString(
+      "multideviceNotificationAccessSetupAccessProhibitedSummary",
+      l10n_util::GetStringFUTF16(
+          IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_ACCESS_PROHIBITED_SUMMARY,
+          base::UTF8ToUTF16(
+              multidevice_setup::
+                  GetBoardSpecificBetterTogetherSuiteLearnMoreUrl()
+                      .spec())));
   html_source->AddString(
       "multideviceWifiSyncItemSummary",
       l10n_util::GetStringFUTF16(
diff --git a/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.cc b/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.cc
index 3e3f93eea..0ccc78d 100644
--- a/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.cc
@@ -83,12 +83,14 @@
   // new_value is initialized as null. Null value is used in cases that don't
   // require extra metadata.
   base::Value new_value;
-  if (value->is_bool_value()) {
-    new_value = base::Value(value->get_bool_value());
-  } else if (value->is_int_value()) {
-    new_value = base::Value(value->get_int_value());
-  } else if (value->is_string_value()) {
-    new_value = base::Value(value->get_string_value());
+  if (value) {
+    if (value->is_bool_value()) {
+      new_value = base::Value(value->get_bool_value());
+    } else if (value->is_int_value()) {
+      new_value = base::Value(value->get_int_value());
+    } else if (value->is_string_value()) {
+      new_value = base::Value(value->get_string_value());
+    }
   }
   section->LogMetric(setting, new_value);
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.h b/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.h
index d557e9c..b29c7f2 100644
--- a/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.h
+++ b/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.h
@@ -46,6 +46,8 @@
                            TestRecordSettingChangedBoolPref);
   FRIEND_TEST_ALL_PREFIXES(SettingsUserActionTrackerTest,
                            TestRecordSettingChangedIntPref);
+  FRIEND_TEST_ALL_PREFIXES(SettingsUserActionTrackerTest,
+                           TestRecordSettingChangedNullValue);
 
   // mojom::UserActionRecorder:
   void RecordPageFocus() override;
diff --git a/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker_unittest.cc
index fd1856b..8493d1e 100644
--- a/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker_unittest.cc
@@ -39,6 +39,8 @@
                                        mojom::Setting::kTouchpadSpeed);
     fake_hierarchy_.AddSettingMetadata(mojom::Section::kPeople,
                                        mojom::Setting::kAddAccount);
+    fake_hierarchy_.AddSettingMetadata(mojom::Section::kPrinting,
+                                       mojom::Setting::kScanningApp);
   }
 
   base::HistogramTester histogram_tester_;
@@ -138,5 +140,26 @@
               mojom::Setting::kTouchpadSpeed);
 }
 
+TEST_F(SettingsUserActionTrackerTest, TestRecordSettingChangedNullValue) {
+  // Record that the Scan app is opened.
+  tracker_.RecordSettingChangeWithDetails(mojom::Setting::kScanningApp,
+                                          nullptr);
+
+  // The umbrella metric for which setting was changed should be updated. Note
+  // that kScanningApp has enum value of 1403.
+  histogram_tester_.ExpectTotalCount("ChromeOS.Settings.SettingChanged",
+                                     /*count=*/1);
+  histogram_tester_.ExpectBucketCount("ChromeOS.Settings.SettingChanged",
+                                      /*sample=*/1403,
+                                      /*count=*/1);
+
+  // The LogMetric fn in the Printing section should have been called.
+  const FakeOsSettingsSection* printing_section =
+      static_cast<const FakeOsSettingsSection*>(
+          fake_sections_.GetSection(mojom::Section::kPrinting));
+  EXPECT_TRUE(printing_section->logged_metrics().back() ==
+              mojom::Setting::kScanningApp);
+}
+
 }  // namespace settings.
 }  // namespace chromeos.
diff --git a/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc
index c182484c..218c0fe 100644
--- a/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc
@@ -244,6 +244,8 @@
 void AddNearbyShareData(content::WebUIDataSource* html_source) {
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
       {"nearbyShareTitle", IDS_SETTINGS_NEARBY_SHARE_TITLE},
+      {"nearbyShareSetUpButtonTitle",
+       IDS_SETTINGS_NEARBY_SHARE_SET_UP_BUTTON_TITLE},
       {"nearbyShareDeviceNameRowTitle",
        IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_ROW_TITLE},
       {"nearbyShareDeviceNameDialogTitle",
diff --git a/chrome/browser/ui/webui/signin/profile_picker_ui.cc b/chrome/browser/ui/webui/signin/profile_picker_ui.cc
index 0c196b2..385c15b 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_ui.cc
+++ b/chrome/browser/ui/webui/signin/profile_picker_ui.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_shortcut_manager.h"
 #include "chrome/browser/signin/signin_util.h"
+#include "chrome/browser/ui/profile_picker.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.h"
 #include "chrome/browser/ui/webui/signin/profile_picker_handler.h"
@@ -119,6 +120,13 @@
       {"uninstallThirdPartyThemeButton", IDS_NTP_CUSTOMIZE_3PT_THEME_UNINSTALL},
   };
   AddLocalizedStringsBulk(html_source, kLocalizedStrings);
+  ProfilePicker::AvailabilityOnStartup availability_on_startup =
+      static_cast<ProfilePicker::AvailabilityOnStartup>(
+          g_browser_process->local_state()->GetInteger(
+              prefs::kBrowserProfilePickerAvailabilityOnStartup));
+  html_source->AddBoolean("disableAskOnStartup",
+                          availability_on_startup !=
+                              ProfilePicker::AvailabilityOnStartup::kEnabled);
   html_source->AddBoolean("askOnStartup",
                           g_browser_process->local_state()->GetBoolean(
                               prefs::kBrowserShowProfilePickerOnStartup));
diff --git a/chrome/browser/webshare/chromeos/sharesheet_client.cc b/chrome/browser/webshare/chromeos/sharesheet_client.cc
index fc77356..ae646b9 100644
--- a/chrome/browser/webshare/chromeos/sharesheet_client.cc
+++ b/chrome/browser/webshare/chromeos/sharesheet_client.cc
@@ -13,6 +13,7 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/apps/app_service/intent_util.h"
+#include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sharesheet/sharesheet_service.h"
 #include "chrome/browser/sharesheet/sharesheet_service_factory.h"
@@ -20,7 +21,6 @@
 #include "chrome/browser/webshare/chromeos/store_files_task.h"
 #include "chrome/browser/webshare/share_service_impl.h"
 #include "chrome/common/chrome_features.h"
-#include "chromeos/dbus/cros_disks_client.h"
 #include "components/services/app_service/public/cpp/intent_util.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
@@ -33,7 +33,7 @@
 namespace {
 
 constexpr base::FilePath::CharType kWebShareDirname[] =
-    FILE_PATH_LITERAL("WebShare");
+    FILE_PATH_LITERAL(".WebShare");
 
 // We don't use |supplied_name| as it may contain special characters, and it may
 // not be unique. The suffix has been checked by
@@ -97,10 +97,14 @@
     return;
   }
 
+  Profile* const profile =
+      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
+  DCHECK(profile);
+
   current_share_ = CurrentShare();
   current_share_->files = std::move(files);
   current_share_->directory =
-      chromeos::CrosDisksClient::GetArchiveMountPoint().Append(
+      file_manager::util::GetMyFilesFolderForProfile(profile).Append(
           kWebShareDirname);
   if (share_url.is_valid()) {
     if (text.empty())
diff --git a/chrome/browser/webshare/chromeos/sharesheet_client_browsertest.cc b/chrome/browser/webshare/chromeos/sharesheet_client_browsertest.cc
index b56224b1..2a073637 100644
--- a/chrome/browser/webshare/chromeos/sharesheet_client_browsertest.cc
+++ b/chrome/browser/webshare/chromeos/sharesheet_client_browsertest.cc
@@ -14,6 +14,7 @@
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/scoped_blocking_call.h"
+#include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_features.h"
@@ -112,6 +113,12 @@
 
   EXPECT_EQ("share succeeded", content::EvalJs(contents, script));
   EXPECT_EQ(file_paths.size(), 2U);
+
+  const base::FilePath my_files =
+      file_manager::util::GetMyFilesFolderForProfile(browser()->profile());
+  EXPECT_EQ(file_paths[0], my_files.AppendASCII(".WebShare/share1.mp3"));
+  EXPECT_EQ(file_paths[1], my_files.AppendASCII(".WebShare/share2.mp4"));
+
   CheckSize(file_paths[0], /*expected_size=*/345);
   CheckSize(file_paths[1], /*expected_size=*/67890);
 }
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 6c7b1fb3..0898293 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1607104719-553836ec892e604b847a609e48d12198e967747c.profdata
+chrome-linux-master-1607252866-c2fa6eaa51da4882fc07c7786de461457a327487.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 79b25e9..b1ee5a0 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1607104719-e1d1a8dd7fb3b6aba7965596c29afef7497a651e.profdata
+chrome-mac-master-1607252866-f3ab87149644d613d134273463ed0ee5ad40f575.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index bf88bb32..192b61e9 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1607061555-42057a46d6898e28766e44cc3eff7a0d211d34f4.profdata
+chrome-win32-master-1607252866-d41b621d05e6450c8f7df54cb40144efff641518.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index dcc347e..40525035 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1607070900-a3f370d99263588b261e824540349b020ef94941.profdata
+chrome-win64-master-1607241930-f5b83cf2f1c9491df564851856be44747b1ca52b.profdata
diff --git a/chrome/common/chrome_paths.cc b/chrome/common/chrome_paths.cc
index 2baba49..7805c09 100644
--- a/chrome/common/chrome_paths.cc
+++ b/chrome/common/chrome_paths.cc
@@ -408,13 +408,6 @@
       cur = cur.Append(FILE_PATH_LITERAL("custom_wallpapers"));
       break;
 #endif
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-    case chrome::DIR_SUPERVISED_USER_INSTALLED_WHITELISTS:
-      if (!base::PathService::Get(chrome::DIR_USER_DATA, &cur))
-        return false;
-      cur = cur.Append(FILE_PATH_LITERAL("SupervisedUserInstalledWhitelists"));
-      break;
-#endif
     // The following are only valid in the development environment, and
     // will fail if executed from an installed executable (because the
     // generated path won't exist).
diff --git a/chrome/common/chrome_paths.h b/chrome/common/chrome_paths.h
index e422dca..851da41 100644
--- a/chrome/common/chrome_paths.h
+++ b/chrome/common/chrome_paths.h
@@ -118,9 +118,6 @@
   DIR_CHROMEOS_CUSTOM_WALLPAPERS,     // Directory where custom wallpapers
                                       // reside.
 #endif
-  DIR_SUPERVISED_USER_INSTALLED_WHITELISTS,  // Directory where sanitized
-                                             // supervised user whitelists are
-                                             // installed.
 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC)
   DIR_NATIVE_MESSAGING,       // System directory where native messaging host
                               // manifest files are stored.
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 693541f..4e0bafd 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -332,11 +332,6 @@
 // Causes Chrome to initiate an installation flow for the given app.
 const char kInstallChromeApp[]              = "install-chrome-app";
 
-// A list of allowlists to install for a supervised user, for testing.
-// The list is of the following form: <id>[:<name>],[<id>[:<name>],...]
-const char kInstallSupervisedUserAllowlists[] =
-    "install-supervised-user-whitelists";
-
 // Marks a renderer as an Instant process.
 const char kInstantProcess[]                = "instant-process";
 
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 8922e5d..73f70ca 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -111,7 +111,6 @@
 extern const char kIncognito[];
 extern const char kInstallAutogeneratedTheme[];
 extern const char kInstallChromeApp[];
-extern const char kInstallSupervisedUserAllowlists[];
 extern const char kInstantProcess[];
 extern const char kKeepAliveForTest[];
 extern const char kKioskMode[];
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 7fbdfb2..83c64ad 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -203,11 +203,6 @@
 // the format.
 const char kSupervisedUserSharedSettings[] = "profile.managed.shared_settings";
 
-// A dictionary storing allowlists for a supervised user. The key is the CRX ID
-// of the allowlist, the value a dictionary containing allowlist properties
-// (currently the name).
-const char kSupervisedUserAllowlists[] = "profile.managed.whitelists";
-
 #if BUILDFLAG(ENABLE_RLZ)
 // Integer. RLZ ping delay in seconds.
 const char kRlzPingDelaySeconds[] = "rlz_ping_delay";
@@ -2685,6 +2680,10 @@
 // Whether profile can be used before sign in.
 const char kForceBrowserSignin[] = "profile.force_browser_signin";
 
+// Whether profile picker is enabled, disabled or forced on startup.
+const char kBrowserProfilePickerAvailabilityOnStartup[] =
+    "profile.picker_availability_on_startup";
+
 // Whether to show the profile picker on startup or not.
 const char kBrowserShowProfilePickerOnStartup[] =
     "profile.show_picker_on_startup";
@@ -2724,11 +2723,6 @@
 const char kRecoveryComponentNeedsElevation[] =
     "recovery_component.needs_elevation";
 
-// A dictionary that maps from supervised user whitelist IDs to their properties
-// (name and a list of clients that registered the whitelist).
-const char kRegisteredSupervisedUserWhitelists[] =
-    "supervised_users.whitelists";
-
 #if !defined(OS_ANDROID)
 // Boolean that indicates whether Chrome enterprise extension request is enabled
 // or not.
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 4511234..e8f714c 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -62,7 +62,6 @@
 extern const char kSupervisedUserSecondCustodianProfileImageURL[];
 extern const char kSupervisedUserSecondCustodianProfileURL[];
 extern const char kSupervisedUserSharedSettings[];
-extern const char kSupervisedUserAllowlists[];
 extern const char kURLsToRestoreOnStartup[];
 extern const char kUserFeedbackAllowed[];
 
@@ -899,6 +898,7 @@
 extern const char kBrowserGuestModeEnforced[];
 extern const char kBrowserAddPersonEnabled[];
 extern const char kForceBrowserSignin[];
+extern const char kBrowserProfilePickerAvailabilityOnStartup[];
 extern const char kBrowserShowProfilePickerOnStartup[];
 extern const char kSigninAllowedOnNextStartup[];
 
@@ -911,7 +911,6 @@
 
 extern const char kRecoveryComponentNeedsElevation[];
 
-extern const char kRegisteredSupervisedUserWhitelists[];
 
 #if !defined(OS_ANDROID)
 extern const char kCloudExtensionRequestEnabled[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index fadfb708..4b7cbe6 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -706,6 +706,7 @@
     deps = [
       "//base:base_java",
       "//base:base_java_test_support",
+      "//chrome/android:base_module_java",
       "//chrome/android:chrome_all_java",
       "//chrome/browser/tabmodel:java",
       "//components/crash/android:handler_java",
@@ -6139,13 +6140,11 @@
   }
   if (enable_supervised_users) {
     sources += [
-      "../browser/component_updater/supervised_user_whitelist_installer_unittest.cc",
       "../browser/content_settings/content_settings_supervised_provider_unittest.cc",
       "../browser/supervised_user/child_accounts/child_account_service_unittest.cc",
       "../browser/supervised_user/child_accounts/family_info_fetcher_unittest.cc",
       "../browser/supervised_user/child_accounts/permission_request_creator_apiary_unittest.cc",
       "../browser/supervised_user/kids_management_url_checker_client_unittest.cc",
-      "../browser/supervised_user/supervised_user_allowlist_service_unittest.cc",
       "../browser/supervised_user/supervised_user_model_type_controller_unittest.cc",
       "../browser/supervised_user/supervised_user_pref_store_unittest.cc",
       "../browser/supervised_user/supervised_user_service_unittest.cc",
diff --git a/chrome/test/base/testing_browser_process.cc b/chrome/test/base/testing_browser_process.cc
index 40a22ed..3ad5f08 100644
--- a/chrome/test/base/testing_browser_process.cc
+++ b/chrome/test/base/testing_browser_process.cc
@@ -392,13 +392,6 @@
   return nullptr;
 }
 
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-component_updater::SupervisedUserWhitelistInstaller*
-TestingBrowserProcess::supervised_user_whitelist_installer() {
-  return nullptr;
-}
-#endif
-
 MediaFileSystemRegistry* TestingBrowserProcess::media_file_system_registry() {
 #if defined(OS_ANDROID)
   NOTIMPLEMENTED();
diff --git a/chrome/test/base/testing_browser_process.h b/chrome/test/base/testing_browser_process.h
index 1d7aee67..b17ec0a3 100644
--- a/chrome/test/base/testing_browser_process.h
+++ b/chrome/test/base/testing_browser_process.h
@@ -133,10 +133,6 @@
 #endif
 
   component_updater::ComponentUpdateService* component_updater() override;
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-  component_updater::SupervisedUserWhitelistInstaller*
-  supervised_user_whitelist_installer() override;
-#endif
   MediaFileSystemRegistry* media_file_system_registry() override;
 
   WebRtcLogUploader* webrtc_log_uploader() override;
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index f75b44f..3aeee72e4 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -8306,5 +8306,17 @@
         "prefs": { "browser.clear_data.clear_on_exit": { "value": ["browsing_history"]}}
       }
     ]
+  },
+  "ProfilePickerOnStartupAvailability": {
+    "os": ["win", "linux", "mac"],
+    "can_be_recommended": false,
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ProfilePickerOnStartupAvailability": 2 },
+        "prefs": {
+          "profile.picker_availability_on_startup": { "location": "local_state", "value": 2 }
+        }
+      }
+    ]
   }
 }
diff --git a/chrome/test/data/webui/cr_components/chromeos/BUILD.gn b/chrome/test/data/webui/cr_components/chromeos/BUILD.gn
index 62ec3f3..dc5d52f 100644
--- a/chrome/test/data/webui/cr_components/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/cr_components/chromeos/BUILD.gn
@@ -4,11 +4,11 @@
 
 import("//ui/webui/resources/tools/js_modulizer.gni")
 
-
 group("modulize") {
   deps = [
     "cellular_setup:modulize",
     "network:modulize",
+    "network_health:modulize",
     "//chrome/test/data/webui/chromeos:modulize",
   ]
 }
diff --git a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js
index 209c0e45..c95c8b1 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js
@@ -60,6 +60,10 @@
 ].forEach(test => registerTest('NetworkComponents', 'network', ...test));
 
 [
+  ['RoutineGroup', 'network_health/routine_group_test.js', []],
+].forEach(test => registerTest('NetworkHealth', 'network', ...test));
+
+[
   ['ActivationCodePage', 'cellular_setup/activation_code_page_test.js',[
     './cellular_setup/fake_media_devices.js',
   ]],
diff --git a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_v3_browsertest.js b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_v3_browsertest.js
index ddf8683..bd38d8e7 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_v3_browsertest.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_v3_browsertest.js
@@ -30,7 +30,10 @@
  ['NetworkProxy', 'network/network_proxy_test.m.js'],
  ['NetworkSelect', 'network/network_select_test.m.js'],
  ['NetworkSiminfo', 'network/network_siminfo_test.m.js'],
-].forEach(test => registerTest('NetworkComponents', ...test));
+].forEach(test => registerTest('NetworkComponents', 'os-settings', ...test));
+
+[['RoutineGroup', 'network_health/routine_group_test.m.js'],
+].forEach(test => registerTest('NetworkHealth', 'connectivity-diagnostics', ...test));
 
 [['ActivationCodePage', 'cellular_setup/activation_code_page_test.m.js'],
  ['BasePage', 'cellular_setup/base_page_test.m.js'],
@@ -42,17 +45,18 @@
  ['PsimFlowUi', 'cellular_setup/psim_flow_ui_test.m.js'],
  ['SetupSelectionFlow', 'cellular_setup/setup_selection_flow_test.m.js'],
  ['SetupLoadingPage', 'cellular_setup/setup_loading_page_test.m.js'],
-].forEach(test => registerTest('CellularSetup', ...test));
+].forEach(test => registerTest('CellularSetup', 'os-settings', ...test));
 // clang-format on
 
-function registerTest(componentName, testName, module, caseName) {
+function registerTest(componentName, webuiHost, testName, module, caseName) {
   const className = `${componentName}${testName}TestV3`;
   this[className] = class extends PolymerTest {
     /** @override */
     get browsePreload() {
       // TODO(jhawkins): Set up test_loader.html for internet-config-dialog
       // and use it here instead of os-settings.
-      return `chrome://os-settings/test_loader.html?module=cr_components/chromeos/${module}`;
+      return `chrome://${
+          webuiHost}/test_loader.html?module=cr_components/chromeos/${module}`;
     }
 
     /** @override */
@@ -67,6 +71,7 @@
     get featureList() {
       return {
         enabled: [
+          'chromeos::features::kConnectivityDiagnosticsWebUi',
           'chromeos::features::kOsSettingsPolymer3',
           'chromeos::features::kUpdatedCellularActivationUi',
         ],
diff --git a/chrome/test/data/webui/cr_components/chromeos/network_health/BUILD.gn b/chrome/test/data/webui/cr_components/chromeos/network_health/BUILD.gn
new file mode 100644
index 0000000..4d7899a
--- /dev/null
+++ b/chrome/test/data/webui/cr_components/chromeos/network_health/BUILD.gn
@@ -0,0 +1,18 @@
+# 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")
+import("//ui/webui/resources/tools/js_modulizer.gni")
+
+js_library("routine_group_test") {
+  deps = [
+    "../../..:chai_assert",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
+}
+
+js_modulizer("modulize") {
+  input_files = [ "routine_group_test.js" ]
+}
diff --git a/chrome/test/data/webui/cr_components/chromeos/network_health/routine_group_test.js b/chrome/test/data/webui/cr_components/chromeos/network_health/routine_group_test.js
new file mode 100644
index 0000000..5fa9444
--- /dev/null
+++ b/chrome/test/data/webui/cr_components/chromeos/network_health/routine_group_test.js
@@ -0,0 +1,212 @@
+// 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.
+
+// clang-format off
+// #import 'chrome://connectivity-diagnostics/strings.m.js';
+// #import 'chrome://resources/cr_components/chromeos/network_health/network_diagnostics_mojo.m.js'
+// #import {Routine, Icons} from 'chrome://resources/cr_components/chromeos/network_health/network_diagnostics_types.m.js'
+// #import 'chrome://resources/cr_components/chromeos/network_health/routine_group.m.js'
+// #import {assertTrue, assertFalse, assertEquals} from '../../../chai_assert.js';
+
+// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+// clang-format on
+
+/**
+ * Creates baseline routines.
+ * @return {!Array<!Routine>}
+ * @private
+ */
+function createRoutines() {
+  return [
+    {
+      name: 'NetworkDiagnosticsLanConnectivity',
+      running: false,
+      resultMsg: 'Passed',
+      group: 0,
+      type: 0,
+      result: {
+        verdict: chromeos.networkDiagnostics.mojom.RoutineVerdict.kNoProblem
+      },
+      ariaDescription: '',
+    },
+    {
+      name: 'NetworkDiagnosticsSignalStrength',
+      running: false,
+      resultMsg: 'Passed',
+      group: 0,
+      type: 1,
+      result: {
+        verdict: chromeos.networkDiagnostics.mojom.RoutineVerdict.kNoProblem
+      },
+      ariaDescription: '',
+    }
+  ];
+}
+
+/**
+ * Removes any prefixed URL from a icon image path
+ * @param {string}
+ * @return {string}
+ * @private
+ */
+function getIconFromSrc(src) {
+  const values = src.split('/');
+  return values[values.length - 1];
+}
+
+/**
+ * Test suite for the Routine Group element.
+ */
+suite('RoutineGroupTest', function routineGroupTest() {
+  /** @type {?RoutineGroupElement} */
+  let routineGroup = null;
+
+  setup(() => {
+    document.body.innerHTML = '';
+    routineGroup = /** @type {!RoutineGroupElement} */ (
+        document.createElement('routine-group'));
+    routineGroup.name = 'Group';
+    routineGroup.expanded = false;
+    document.body.appendChild(routineGroup);
+    Polymer.dom.flush();
+  });
+
+  teardown(function() {
+    routineGroup.remove();
+    routineGroup = null;
+  });
+
+  /**
+   * Takes the provided routines and passes them to the RoutineGroupElement,
+   * then flushes the Polymes DOM.
+   * @param {!Array<!Routine>} routines
+   */
+  function setRoutines(routines) {
+    routineGroup.routines = routines;
+    Polymer.dom.flush();
+  }
+
+  /**
+   * Clicks the routine group container to toggle the expanded state.
+   * @param {boolean} expanded
+   */
+  function clickRoutineGroup() {
+    const container = routineGroup.$$('.group-container');
+    assertTrue(!!container);
+    container.click();
+    Polymer.dom.flush();
+  }
+
+  /**
+   * Check that the spinner is visible and the group icon is not.
+   */
+  function checkRunning() {
+    const spinner = routineGroup.$$('paper-spinner-lite');
+    assertTrue(!!spinner);
+    assertFalse(spinner.hidden);
+    const icon = routineGroup.$$('.routine-icon');
+    assertTrue(!!icon);
+    assertTrue(icon.hidden);
+  }
+
+  /**
+   * Check that the spinner is hidden and the group icon is visible and set to
+   * `iconResult`.
+   * @param {string} iconResult
+   */
+  function checkResult(iconResult) {
+    const spinner = routineGroup.$$('paper-spinner-lite');
+    assertFalse(!!spinner);
+    const icon = routineGroup.$$('.routine-icon');
+    assertTrue(!!icon);
+    assertFalse(icon.hidden);
+    assertEquals(getIconFromSrc(icon.src), iconResult);
+  }
+
+  /**
+   * Test using one running routine.
+   */
+  test('RunningOne', () => {
+    let routines = createRoutines();
+    routines[0].running = true;
+    routines[0].result = null;
+    routines[0].resultMsg = '';
+    setRoutines(routines);
+
+    checkRunning();
+    clickRoutineGroup();
+  });
+
+  /**
+   * Test when all routines are running.
+   */
+  test('RunningAll', () => {
+    let routines = createRoutines();
+    for (let routine of routines) {
+      routine.running = true;
+      routine.result = null;
+      routine.resultMsg = '';
+    }
+    setRoutines(routines);
+    checkRunning();
+    clickRoutineGroup();
+  });
+
+  /**
+   * Test when all routines are complete.
+   */
+  test('RunningNone', () => {
+    routineGroup.routines = createRoutines();
+    Polymer.dom.flush();
+
+    checkResult(Icons.TEST_PASSED);
+    clickRoutineGroup();
+  });
+
+  /**
+   * Test when all routines are complete and one has failed.
+   */
+  test('FailedOne', () => {
+    let routines = createRoutines();
+    routines[0].resultMsg = 'Failed';
+    routines[0].result = {
+      'verdict': chromeos.networkDiagnostics.mojom.RoutineVerdict.kProblem
+    };
+    setRoutines(routines);
+    checkResult(Icons.TEST_FAILED);
+    clickRoutineGroup();
+  });
+
+  /**
+   * Test when routines are complete and one did not run.
+   */
+  test('NotRunOne', () => {
+    let routines = createRoutines();
+    routines[0].resultMsg = 'Not Run';
+    routines[0].result = {
+      'verdict': chromeos.networkDiagnostics.mojom.RoutineVerdict.kNotRun
+    };
+    setRoutines(routines);
+    checkResult(Icons.TEST_NOT_RUN);
+    clickRoutineGroup();
+  });
+
+  /**
+   * Test when routines are complete. One routine failed and one did not run.
+   */
+  test('NotRunAndFailed', () => {
+    let routines = createRoutines();
+    routines[0].resultMsg = 'Not Run';
+    routines[0].result = {
+      'verdict': chromeos.networkDiagnostics.mojom.RoutineVerdict.kNotRun
+    };
+    routines[1].resultMsg = 'Failed';
+    routines[1].result = {
+      'verdict': chromeos.networkDiagnostics.mojom.RoutineVerdict.kProblem
+    };
+    setRoutines(routines);
+    checkResult(Icons.TEST_FAILED);
+    clickRoutineGroup();
+  });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/multidevice_feature_toggle_tests.js b/chrome/test/data/webui/settings/chromeos/multidevice_feature_toggle_tests.js
index 8def0b2..2257895 100644
--- a/chrome/test/data/webui/settings/chromeos/multidevice_feature_toggle_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/multidevice_feature_toggle_tests.js
@@ -5,7 +5,7 @@
 // clang-format off
 // #import 'chrome://os-settings/chromeos/os_settings.js';
 
-// #import {MultiDeviceFeature, MultiDeviceFeatureState} from 'chrome://os-settings/chromeos/os_settings.js';
+// #import {MultiDeviceFeature, MultiDeviceFeatureState, PhoneHubNotificationAccessStatus} from 'chrome://os-settings/chromeos/os_settings.js';
 // #import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 // #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 // clang-format on
@@ -21,17 +21,31 @@
   let featureToggle = null;
   /** @type {?CrToggleElement} */
   let crToggle = null;
+  /** @type {!settings.MultiDeviceFeature} */
+  let featureToTest;
+  /** @type {string} */
+  let pageContentDataKey;
 
   /**
-   * Sets the state of the feature shown in the toggle (i.e. Messages). Note
-   * that in order to trigger featureToggle's bindings to update, we set its
-   * pageContentData to a new object as the actual UI does.
-   * @param {?settings.MultiDeviceFeatureState} newMessagesState. New value for
-   * featureToggle.pageContentData.messagesState.
+   * Sets the state of the feature shown in the toggle. Note that in order to
+   * trigger featureToggle's bindings to update, we set its pageContentData to a
+   * new object as the actual UI does.
+   * @param {?settings.MultiDeviceFeatureState} newFeatureState
    */
-  function setMessagesState(newMessagesState) {
+  function setFeatureState(newFeatureState) {
     featureToggle.pageContentData = Object.assign(
-        {}, featureToggle.pageContentData, {messagesState: newMessagesState});
+        {}, featureToggle.pageContentData,
+        {[pageContentDataKey]: newFeatureState});
+    Polymer.dom.flush();
+  }
+
+  /**
+   * @param {!settings.PhoneHubNotificationAccessStatus} accessStatus
+   */
+  function setNotificationAccessStatus(accessStatus) {
+    featureToggle.pageContentData = Object.assign(
+        {}, featureToggle.pageContentData,
+        {notificationAccessStatus: accessStatus});
     Polymer.dom.flush();
   }
 
@@ -49,19 +63,25 @@
     Polymer.dom.flush();
   }
 
-  setup(() => {
-    PolymerTest.clearBody();
+  /**
+   * @param {!settings.MultiDeviceFeature} feature
+   * @param {string} key
+   */
+  function init(feature, key) {
+    featureToTest = feature;
+    pageContentDataKey = key;
 
     featureToggle =
         document.createElement('settings-multidevice-feature-toggle');
-    featureToggle.feature = settings.MultiDeviceFeature.MESSAGES;
+    featureToggle.feature = feature;
+
     // Initially toggle will be unchecked but not disabled. Note that the word
     // "disabled" is ambiguous for feature toggles because it can refer to the
     // feature or the cr-toggle property/attribute. DISABLED_BY_USER refers to
     // the former so it unchecks but does not functionally disable the toggle.
     featureToggle.pageContentData = {
       betterTogetherState: settings.MultiDeviceFeatureState.ENABLED_BY_USER,
-      messagesState: settings.MultiDeviceFeatureState.DISABLED_BY_USER,
+      [pageContentDataKey]: settings.MultiDeviceFeatureState.DISABLED_BY_USER,
     };
     document.body.appendChild(featureToggle);
     Polymer.dom.flush();
@@ -71,44 +91,56 @@
     assertFalse(featureToggle.checked_);
     assertFalse(crToggle.checked);
     assertFalse(crToggle.disabled);
+  }
+
+  setup(() => {
+    PolymerTest.clearBody();
   });
 
   test('checked property can be set by feature state', () => {
-    setMessagesState(settings.MultiDeviceFeatureState.ENABLED_BY_USER);
+    init(settings.MultiDeviceFeature.MESSAGES, 'messagesState');
+
+    setFeatureState(settings.MultiDeviceFeatureState.ENABLED_BY_USER);
     assertTrue(featureToggle.checked_);
     assertTrue(crToggle.checked);
 
-    setMessagesState(settings.MultiDeviceFeatureState.DISABLED_BY_USER);
+    setFeatureState(settings.MultiDeviceFeatureState.DISABLED_BY_USER);
     assertFalse(featureToggle.checked_);
     assertFalse(crToggle.checked);
   });
 
   test('disabled property can be set by feature state', () => {
-    setMessagesState(settings.MultiDeviceFeatureState.PROHIBITED_BY_POLICY);
+    init(settings.MultiDeviceFeature.MESSAGES, 'messagesState');
+
+    setFeatureState(settings.MultiDeviceFeatureState.PROHIBITED_BY_POLICY);
     assertTrue(crToggle.disabled);
 
-    setMessagesState(settings.MultiDeviceFeatureState.DISABLED_BY_USER);
+    setFeatureState(settings.MultiDeviceFeatureState.DISABLED_BY_USER);
     assertFalse(crToggle.disabled);
   });
 
   test('disabled and checked properties update simultaneously', () => {
-    setMessagesState(settings.MultiDeviceFeatureState.ENABLED_BY_USER);
+    init(settings.MultiDeviceFeature.MESSAGES, 'messagesState');
+
+    setFeatureState(settings.MultiDeviceFeatureState.ENABLED_BY_USER);
     assertTrue(featureToggle.checked_);
     assertTrue(crToggle.checked);
     assertFalse(crToggle.disabled);
 
-    setMessagesState(settings.MultiDeviceFeatureState.PROHIBITED_BY_POLICY);
+    setFeatureState(settings.MultiDeviceFeatureState.PROHIBITED_BY_POLICY);
     assertFalse(featureToggle.checked_);
     assertFalse(crToggle.checked);
     assertTrue(crToggle.disabled);
 
-    setMessagesState(settings.MultiDeviceFeatureState.DISABLED_BY_USER);
+    setFeatureState(settings.MultiDeviceFeatureState.DISABLED_BY_USER);
     assertFalse(featureToggle.checked_);
     assertFalse(crToggle.checked);
     assertFalse(crToggle.disabled);
   });
 
   test('disabled property can be set by suite pref', () => {
+    init(settings.MultiDeviceFeature.MESSAGES, 'messagesState');
+
     setSuiteState(settings.MultiDeviceFeatureState.DISABLED_BY_USER);
     Polymer.dom.flush();
     assertTrue(crToggle.disabled);
@@ -119,7 +151,9 @@
   });
 
   test('checked property is unaffected by suite pref', () => {
-    setMessagesState(settings.MultiDeviceFeatureState.ENABLED_BY_USER);
+    init(settings.MultiDeviceFeature.MESSAGES, 'messagesState');
+
+    setFeatureState(settings.MultiDeviceFeatureState.ENABLED_BY_USER);
     assertTrue(featureToggle.checked_);
     assertTrue(crToggle.checked);
     assertFalse(crToggle.disabled);
@@ -132,9 +166,28 @@
   });
 
   test('clicking toggle does not change checked property', () => {
+    init(settings.MultiDeviceFeature.MESSAGES, 'messagesState');
+
     const preClickCrToggleChecked = crToggle.checked;
     crToggle.click();
     Polymer.dom.flush();
     assertEquals(crToggle.checked, preClickCrToggleChecked);
   });
+
+  test('notification access is prohibited', () => {
+    init(
+        settings.MultiDeviceFeature.PHONE_HUB_NOTIFICATIONS,
+        'phoneHubNotificationsState');
+
+    setFeatureState(settings.MultiDeviceFeatureState.ENABLED_BY_USER);
+    assertTrue(featureToggle.checked_);
+    assertTrue(crToggle.checked);
+    assertFalse(crToggle.disabled);
+
+    setNotificationAccessStatus(
+        settings.PhoneHubNotificationAccessStatus.PROHIBITED);
+    assertFalse(featureToggle.checked_);
+    assertFalse(crToggle.checked);
+    assertTrue(crToggle.disabled);
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/multidevice_notification_access_setup_dialog_tests.js b/chrome/test/data/webui/settings/chromeos/multidevice_notification_access_setup_dialog_tests.js
index 0fc5e50..fa72081 100644
--- a/chrome/test/data/webui/settings/chromeos/multidevice_notification_access_setup_dialog_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/multidevice_notification_access_setup_dialog_tests.js
@@ -146,4 +146,27 @@
 
     assertFalse(notificationAccessSetupDialog.$$('#dialog').open);
   });
+
+  test('Test notification access prohibited', async () => {
+    assertTrue(!!buttonContainer.querySelector('#cancelButton'));
+    assertTrue(!!buttonContainer.querySelector('#getStartedButton'));
+    assertFalse(!!buttonContainer.querySelector('#doneButton'));
+    assertFalse(!!buttonContainer.querySelector('#tryAgainButton'));
+    assertFalse(!!buttonContainer.querySelector('#closeButton'));
+    buttonContainer.querySelector('#getStartedButton').click();
+    assertEquals(browserProxy.getCallCount('attemptNotificationSetup'), 1);
+
+    simulateStatusChanged(
+        NotificationAccessSetupOperationStatus.NOTIFICATION_ACCESS_PROHIBITED);
+
+    assertFalse(!!buttonContainer.querySelector('#cancelButton'));
+    assertFalse(!!buttonContainer.querySelector('#getStartedButton'));
+    assertFalse(!!buttonContainer.querySelector('#doneButton'));
+    assertFalse(!!buttonContainer.querySelector('#tryAgainButton'));
+    assertTrue(!!buttonContainer.querySelector('#closeButton'));
+
+    buttonContainer.querySelector('#closeButton').click();
+
+    assertFalse(notificationAccessSetupDialog.$$('#dialog').open);
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/multidevice_page_tests.js b/chrome/test/data/webui/settings/chromeos/multidevice_page_tests.js
index 596e61e..0895a69 100644
--- a/chrome/test/data/webui/settings/chromeos/multidevice_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/multidevice_page_tests.js
@@ -6,12 +6,13 @@
 // #import 'chrome://os-settings/chromeos/os_settings.js';
 
 // #import {TestLifetimeBrowserProxy} from './test_os_lifetime_browser_proxy.m.js';
-// #import {MultiDeviceSettingsMode, MultiDeviceFeature, MultiDeviceFeatureState, MultiDevicePageContentData, MultiDeviceBrowserProxyImpl, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
+// #import {MultiDeviceSettingsMode, MultiDeviceFeature, MultiDeviceFeatureState, MultiDevicePageContentData, MultiDeviceBrowserProxyImpl, PhoneHubNotificationAccessStatus, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
 // #import {TestOsResetBrowserProxy} from './test_os_reset_browser_proxy.m.js';
 // #import {assertEquals, assertFalse, assertNotEquals, assertTrue} from '../../chai_assert.js';
 // #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 // #import {TestMultideviceBrowserProxy, createFakePageContentData, HOST_DEVICE} from './test_multidevice_browser_proxy.m.js';
-// #import {waitAfterNextRender} from 'chrome://test/test_util.m.js';
+// #import {isChildVisible, waitAfterNextRender} from 'chrome://test/test_util.m.js';
+// import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 // #import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 // clang-format on
 
@@ -72,9 +73,12 @@
    * @param {boolean} accessGranted
    */
   function setPhoneHubNotificationAccessGranted(accessGranted) {
+    const accessState = accessGranted ?
+        settings.PhoneHubNotificationAccessStatus.ACCESS_GRANTED :
+        settings.PhoneHubNotificationAccessStatus.AVAILABLE_BUT_NOT_GRANTED;
     setPageContentData(Object.assign(
         {}, multidevicePage.pageContentData,
-        {isNotificationAccessGranted: accessGranted}));
+        {notificationAccessStatus: accessState}));
   }
 
   /**
@@ -110,8 +114,10 @@
       const accessDialog = multidevicePage.$$(
           'settings-multidevice-notification-access-setup-dialog');
       assertEquals(
-          !accessDialog,
-          multidevicePage.pageContentData.isNotificationAccessGranted);
+          !!accessDialog,
+          multidevicePage.pageContentData.notificationAccessStatus ===
+              settings.PhoneHubNotificationAccessStatus
+                  .AVAILABLE_BUT_NOT_GRANTED);
       return;
     }
 
@@ -135,10 +141,27 @@
     browserProxy = new multidevice.TestMultideviceBrowserProxy();
     settings.MultiDeviceBrowserProxyImpl.instance_ = browserProxy;
 
+    loadTimeData.overrideValues({
+      nearbySharingFeatureFlag: true,
+    });
+
     multidevicePage = document.createElement('settings-multidevice-page');
     assertTrue(!!multidevicePage);
 
+    multidevicePage.prefs = {
+      'nearby_sharing': {
+        'onboarding_complete': {
+          value: false,
+        },
+        'enabled': {
+          value: false,
+        },
+      },
+    };
+
     document.body.appendChild(multidevicePage);
+    Polymer.dom.flush();
+
     return browserProxy.whenCalled('getPageContentData');
   });
 
@@ -189,6 +212,8 @@
     PolymerTest.clearBody();
     browserProxy = new multidevice.TestMultideviceBrowserProxy();
     settings.MultiDeviceBrowserProxyImpl.instance_ = browserProxy;
+    browserProxy.data.notificationAccessStatus =
+        settings.PhoneHubNotificationAccessStatus.AVAILABLE_BUT_NOT_GRANTED;
 
     multidevicePage = document.createElement('settings-multidevice-page');
     assertTrue(!!multidevicePage);
@@ -367,4 +392,19 @@
           return enableFeatureWithAuthFn(Feature.SMART_LOCK);
         });
   });
+
+  test('Nearby setup button visibility', async () => {
+    assertTrue(
+        test_util.isChildVisible(multidevicePage, '#nearbySetUp', false));
+    assertFalse(test_util.isChildVisible(
+        multidevicePage, '#nearbySharingToggleButton', false));
+
+    multidevicePage.setPrefValue('nearby_sharing.onboarding_complete', true);
+    Polymer.dom.flush();
+
+    assertFalse(
+        test_util.isChildVisible(multidevicePage, '#nearbySetUp', false));
+    assertTrue(test_util.isChildVisible(
+        multidevicePage, '#nearbySharingToggleButton', false));
+  });
 });
diff --git a/chrome/test/data/webui/signin/profile_picker_main_view_test.js b/chrome/test/data/webui/signin/profile_picker_main_view_test.js
index 4cdffaf9..6096d1f 100644
--- a/chrome/test/data/webui/signin/profile_picker_main_view_test.js
+++ b/chrome/test/data/webui/signin/profile_picker_main_view_test.js
@@ -62,6 +62,7 @@
     loadTimeData.overrideValues({
       isGuestModeEnabled: true,
       isProfileCreationAllowed: true,
+      disableAskOnStartup: false
     });
   }
 
@@ -230,4 +231,24 @@
         mainViewElement.shadowRoot.querySelectorAll('profile-card'));
     assertTrue(mainViewElement.$$('cr-checkbox').hidden);
   });
+
+  test('AskOnStartupMulipleProfiles', async function() {
+    // Disable AskOnStartup
+    loadTimeData.overrideValues({disableAskOnStartup: true});
+    resetTest();
+
+    await browserProxy.whenCalled('initializeMainView');
+    // Hidden while profiles list is not yet defined.
+    assertTrue(mainViewElement.$$('#wrapper').hidden);
+    assertTrue(mainViewElement.$$('cr-checkbox').hidden);
+    const profiles = generateProfilesList(2);
+    webUIListenerCallback('profiles-list-changed', [...profiles]);
+    flushTasks();
+    await verifyProfileCard(
+        profiles, mainViewElement.shadowRoot.querySelectorAll('profile-card'));
+
+    // Checkbox hidden even if there are multiple profiles because of
+    // disableAskOnStartup.
+    assertTrue(mainViewElement.$$('cr-checkbox').hidden);
+  });
 });
diff --git a/chrome/updater/mac/BUILD.gn b/chrome/updater/mac/BUILD.gn
index b0b0c26..379a2f8 100644
--- a/chrome/updater/mac/BUILD.gn
+++ b/chrome/updater/mac/BUILD.gn
@@ -35,7 +35,7 @@
 source_set("updater_tests") {
   testonly = true
 
-  sources = [ "net/network_unittest.mm" ]
+  sources = [ "net/network_unittest.cc" ]
 
   deps = [
     ":network_fetcher_sources",
diff --git a/chrome/updater/mac/net/network_unittest.mm b/chrome/updater/mac/net/network_unittest.cc
similarity index 73%
rename from chrome/updater/mac/net/network_unittest.mm
rename to chrome/updater/mac/net/network_unittest.cc
index c125cee..ec0da64 100644
--- a/chrome/updater/mac/net/network_unittest.mm
+++ b/chrome/updater/mac/net/network_unittest.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "chrome/updater/mac/net/network.h"
-#include "chrome/updater/mac/net/network_fetcher.h"
 
 #include <stdint.h>
 
@@ -11,9 +10,11 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/notreached.h"
 #include "base/run_loop.h"
 #include "base/test/gmock_callback_support.h"
 #include "base/test/task_environment.h"
+#include "chrome/updater/mac/net/network_fetcher.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
@@ -21,18 +22,19 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
-using base::test::RunClosure;
-using ResponseStartedCallback =
-    update_client::NetworkFetcher::ResponseStartedCallback;
-using ProgressCallback = update_client::NetworkFetcher::ProgressCallback;
-using PostRequestCompleteCallback =
-    update_client::NetworkFetcher::PostRequestCompleteCallback;
-using DownloadToFileCompleteCallback =
-    update_client::NetworkFetcher::DownloadToFileCompleteCallback;
-
 namespace updater {
+namespace {
 
-static base::FilePath testFilePath;
+using ::base::test::RunClosure;
+using ResponseStartedCallback =
+    ::update_client::NetworkFetcher::ResponseStartedCallback;
+using ProgressCallback = ::update_client::NetworkFetcher::ProgressCallback;
+using PostRequestCompleteCallback =
+    ::update_client::NetworkFetcher::PostRequestCompleteCallback;
+using DownloadToFileCompleteCallback =
+    ::update_client::NetworkFetcher::DownloadToFileCompleteCallback;
+
+}  // namespace
 
 class ChromeUpdaterNetworkMacTest : public ::testing::Test {
  public:
@@ -62,11 +64,13 @@
     PostRequestCompleted();
   }
 
-  void DownloadCallback(int net_error, int64_t content_size) {
+  void DownloadCallback(const base::FilePath& test_file_path,
+                        int net_error,
+                        int64_t content_size) {
     EXPECT_EQ(net_error, 0);
     EXPECT_GT(content_size, 0);
-    EXPECT_FALSE(testFilePath.empty());
-    EXPECT_TRUE(base::PathExists(testFilePath));
+    EXPECT_FALSE(test_file_path.empty());
+    EXPECT_TRUE(base::PathExists(test_file_path));
     DownloadToFileCompleted();
   }
 
@@ -74,19 +78,21 @@
       const net::test_server::HttpRequest& request) {
     auto http_response =
         std::make_unique<net::test_server::BasicHttpResponse>();
-    http_response->set_code(net::HTTP_OK);
 
-    if (request.content.size() > 0) {
-      // Echo the posted data back if there's any.
+    if (request.method == net::test_server::HttpMethod::METHOD_POST) {
+      // Echo the posted data back.
       http_response->set_content(request.content);
-    } else {
+      http_response->AddCustomHeader("X-Retry-After", "67");
+      http_response->AddCustomHeader("ETag", "Wfhw789h");
+      http_response->AddCustomHeader("X-Cup-Server-Proof", "server-proof");
+    } else if (request.method == net::test_server::HttpMethod::METHOD_GET) {
       http_response->set_content("hello");
+      http_response->set_content_type("application/octet-stream");
+    } else {
+      NOTREACHED();
     }
-    http_response->set_content_type("application/octet-stream");
 
-    http_response->AddCustomHeader("X-Retry-After", "67");
-    http_response->AddCustomHeader("ETag", "Wfhw789h");
-    http_response->AddCustomHeader("X-Cup-Server-Proof", "server-proof");
+    http_response->set_code(net::HTTP_OK);
     return http_response;
   }
 
@@ -97,39 +103,30 @@
 };
 
 #pragma mark - Test Methods
-TEST_F(ChromeUpdaterNetworkMacTest, NetworkFetcherMacHTTPFactory) {
-  base::RunLoop run_loop;
-  base::RepeatingClosure quit_closure = run_loop.QuitClosure();
-  auto fetcher = base::MakeRefCounted<NetworkFetcherFactory>()->Create();
-  quit_closure.Run();
-  run_loop.Run();
-  EXPECT_NE(nullptr, fetcher.get());
-}
 
 TEST_F(ChromeUpdaterNetworkMacTest, NetworkFetcherMacPostRequest) {
   base::RunLoop run_loop;
   base::RepeatingClosure quit_closure = run_loop.QuitClosure();
   EXPECT_CALL(*this, PostRequestCompleted()).WillOnce(RunClosure(quit_closure));
 
-  auto fetcher = base::MakeRefCounted<NetworkFetcherFactory>()->Create();
-
   net::EmbeddedTestServer test_server;
   test_server.RegisterRequestHandler(base::BindRepeating(
       &ChromeUpdaterNetworkMacTest::HandleRequest, base::Unretained(this)));
-  ASSERT_TRUE(test_server.Start());
-  const GURL url = test_server.GetURL("/echo");
+  const net::test_server::EmbeddedTestServerHandle server_handle =
+      test_server.StartAndReturnHandle();
+  ASSERT_TRUE(server_handle);
 
-  constexpr char kPostData[] = {0x01, 0x00, 0x55, 0x33, 0xda, 0x10, 0x44};
-  const std::string post_data(kPostData, sizeof(kPostData));
+  const std::string kPostData = "\x01\x00\x55\x33\xda\x10\x44";
+
+  auto fetcher = base::MakeRefCounted<NetworkFetcherFactory>()->Create();
   fetcher->PostRequest(
-      url, post_data, {}, {},
+      test_server.GetURL("/echo"), kPostData, {}, {},
       base::BindOnce(&ChromeUpdaterNetworkMacTest::StartedCallback,
                      base::Unretained(this)),
       base::BindRepeating(&ChromeUpdaterNetworkMacTest::ProgressCallback,
                           base::Unretained(this)),
       base::BindOnce(&ChromeUpdaterNetworkMacTest::PostRequestCompleteCallback,
-                     base::Unretained(this), post_data));
-
+                     base::Unretained(this), kPostData));
   run_loop.Run();
 }
 
@@ -138,28 +135,29 @@
   base::RepeatingClosure quit_closure = run_loop.QuitClosure();
   EXPECT_CALL(*this, DownloadToFileCompleted())
       .WillOnce(RunClosure(quit_closure));
-  auto fetcher = base::MakeRefCounted<NetworkFetcherFactory>()->Create();
 
   net::EmbeddedTestServer test_server;
   test_server.RegisterRequestHandler(base::BindRepeating(
       &ChromeUpdaterNetworkMacTest::HandleRequest, base::Unretained(this)));
-  ASSERT_TRUE(test_server.Start());
-  const GURL url = test_server.GetURL("/echo");
+  const net::test_server::EmbeddedTestServerHandle server_handle =
+      test_server.StartAndReturnHandle();
+  ASSERT_TRUE(server_handle);
 
   base::ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-  testFilePath =
+  const base::FilePath test_file_path =
       temp_dir.GetPath().Append(FILE_PATH_LITERAL("downloaded_file"));
 
+  auto fetcher = base::MakeRefCounted<NetworkFetcherFactory>()->Create();
   fetcher->DownloadToFile(
-      url, testFilePath,
+      test_server.GetURL("/echo"), test_file_path,
       base::BindOnce(&ChromeUpdaterNetworkMacTest::StartedCallback,
                      base::Unretained(this)),
       base::BindRepeating(&ChromeUpdaterNetworkMacTest::ProgressCallback,
                           base::Unretained(this)),
       base::BindOnce(&ChromeUpdaterNetworkMacTest::DownloadCallback,
-                     base::Unretained(this)));
-
+                     base::Unretained(this), test_file_path));
   run_loop.Run();
 }
+
 }  // namespace updater
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 74f33a03..9ece0e78 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-13611.0.0
\ No newline at end of file
+13646.0.0
\ No newline at end of file
diff --git a/chromeos/components/chromebox_for_meetings/features/features.h b/chromeos/components/chromebox_for_meetings/features/features.h
index f87f76c..5b77242 100644
--- a/chromeos/components/chromebox_for_meetings/features/features.h
+++ b/chromeos/components/chromebox_for_meetings/features/features.h
@@ -21,9 +21,11 @@
 extern const base::FeatureParam<bool> kCfmTelemetryParam;
 
 // Whether cross platform mojo connections is enabled.
+COMPONENT_EXPORT(CFM_FEATURES)
 bool IsCfmMojoEnabled();
 
 // Whether Telemetry through Encrypted Reporting Pipeline is enabled.
+COMPONENT_EXPORT(CFM_FEATURES)
 bool IsCfmTelemetryEnabled();
 
 }  // namespace features
diff --git a/chromeos/components/connectivity_diagnostics/connectivity_diagnostics_ui.cc b/chromeos/components/connectivity_diagnostics/connectivity_diagnostics_ui.cc
index 3864ec5..8622d8dd 100644
--- a/chromeos/components/connectivity_diagnostics/connectivity_diagnostics_ui.cc
+++ b/chromeos/components/connectivity_diagnostics/connectivity_diagnostics_ui.cc
@@ -14,6 +14,7 @@
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui_data_source.h"
+#include "ui/resources/grit/webui_generated_resources.h"
 
 namespace chromeos {
 
@@ -37,6 +38,8 @@
   }
 
   source->SetDefaultResource(default_resource);
+  source->AddResourcePath("test_loader.html", IDR_WEBUI_HTML_TEST_LOADER_HTML);
+  source->AddResourcePath("test_loader.js", IDR_WEBUI_JS_TEST_LOADER_JS);
 }
 
 }  // namespace
diff --git a/chromeos/components/phonehub/message_receiver_impl.cc b/chromeos/components/phonehub/message_receiver_impl.cc
index 9275a66..8772818 100644
--- a/chromeos/components/phonehub/message_receiver_impl.cc
+++ b/chromeos/components/phonehub/message_receiver_impl.cc
@@ -10,6 +10,7 @@
 
 #include "base/logging.h"
 #include "chromeos/components/phonehub/proto/phonehub_api.pb.h"
+#include "chromeos/components/phonehub/util/histogram_util.h"
 
 namespace chromeos {
 namespace phonehub {
@@ -61,6 +62,8 @@
 
   PA_LOG(INFO) << "MessageReceiver received a "
                << GetMessageTypeName(message_type) << " message.";
+  util::LogMessageResult(message_type,
+                         util::PhoneHubMessageResult::kResponseReceived);
 
   // Decode the proto message if the message is something we want to notify to
   // clients.
diff --git a/chromeos/components/phonehub/message_sender_impl.cc b/chromeos/components/phonehub/message_sender_impl.cc
index c699d61..87fcc20 100644
--- a/chromeos/components/phonehub/message_sender_impl.cc
+++ b/chromeos/components/phonehub/message_sender_impl.cc
@@ -10,6 +10,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chromeos/components/phonehub/connection_manager.h"
+#include "chromeos/components/phonehub/util/histogram_util.h"
 
 namespace chromeos {
 namespace phonehub {
@@ -109,7 +110,9 @@
   connection_manager_->SendMessage(SerializeMessage(message_type, request));
   UMA_HISTOGRAM_ENUMERATION("PhoneHub.Usage.SentMessageTypeCount", message_type,
                             proto::MessageType_MAX);
+  util::LogMessageResult(message_type,
+                         util::PhoneHubMessageResult::kRequestAttempted);
 }
 
 }  // namespace phonehub
-}  // namespace chromeos
\ No newline at end of file
+}  // namespace chromeos
diff --git a/chromeos/components/phonehub/util/histogram_util.cc b/chromeos/components/phonehub/util/histogram_util.cc
index 5c20d4ab..1783d5e5 100644
--- a/chromeos/components/phonehub/util/histogram_util.cc
+++ b/chromeos/components/phonehub/util/histogram_util.cc
@@ -4,12 +4,59 @@
 
 #include "chromeos/components/phonehub/util/histogram_util.h"
 
+#include <string>
+
 #include "base/metrics/histogram_functions.h"
+#include "chromeos/components/phonehub/proto/phonehub_api.pb.h"
 
 namespace chromeos {
 namespace phonehub {
 namespace util {
 
+namespace {
+
+std::string GetMessageResultHistogramName(proto::MessageType message_type) {
+  switch (message_type) {
+    case proto::MessageType::DISMISS_NOTIFICATION_REQUEST:
+      FALLTHROUGH;
+    case proto::MessageType::DISMISS_NOTIFICATION_RESPONSE:
+      return "PhoneHub.TaskCompletion.NotificationDismissal.Result";
+
+    case proto::MessageType::NOTIFICATION_INLINE_REPLY_REQUEST:
+      FALLTHROUGH;
+    case proto::MessageType::NOTIFICATION_INLINE_REPLY_RESPONSE:
+      return "PhoneHub.TaskCompletion.NotificationInlineReply.Result";
+
+    case proto::MessageType::UPDATE_NOTIFICATION_MODE_REQUEST:
+      FALLTHROUGH;
+    case proto::MessageType::UPDATE_NOTIFICATION_MODE_RESPONSE:
+      return "PhoneHub.TaskCompletion.SilencePhone.Result";
+
+    case proto::MessageType::RING_DEVICE_REQUEST:
+      FALLTHROUGH;
+    case proto::MessageType::RING_DEVICE_RESPONSE:
+      return "PhoneHub.TaskCompletion.LocatePhone.Result";
+
+    case proto::MessageType::SHOW_NOTIFICATION_ACCESS_SETUP_REQUEST:
+      FALLTHROUGH;
+    case proto::MessageType::SHOW_NOTIFICATION_ACCESS_SETUP_RESPONSE:
+      return "PhoneHub.TaskCompletion.ShowNotificationAccessSetup.Result";
+
+    case proto::MessageType::UPDATE_BATTERY_MODE_REQUEST:
+      FALLTHROUGH;
+    case proto::MessageType::UPDATE_BATTERY_MODE_RESPONSE:
+      return "PhoneHub.TaskCompletion.UpdateBatteryMode.Result";
+
+    default:
+      // Note that PROVIDE_CROS_STATE, PHONE_STATUS_SNAPSHOT and
+      // PHONE_STATUS_UPDATE message types are not logged as part of this
+      // metrics.
+      return std::string();
+  }
+}
+
+}  // namespace
+
 void LogFeatureOptInEntryPoint(OptInEntryPoint entry_point) {
   base::UmaHistogramEnumeration("PhoneHub.OptInEntryPoint", entry_point);
 }
@@ -19,6 +66,14 @@
       "PhoneHub.TaskCompletion.TetherConnection.Result", result);
 }
 
+void LogMessageResult(proto::MessageType message_type,
+                      PhoneHubMessageResult result) {
+  const std::string histogram_name =
+      GetMessageResultHistogramName(message_type);
+  if (!histogram_name.empty())
+    base::UmaHistogramEnumeration(histogram_name, result);
+}
+
 }  // namespace util
 }  // namespace phonehub
 }  // namespace chromeos
diff --git a/chromeos/components/phonehub/util/histogram_util.h b/chromeos/components/phonehub/util/histogram_util.h
index db9826a..35ad88f 100644
--- a/chromeos/components/phonehub/util/histogram_util.h
+++ b/chromeos/components/phonehub/util/histogram_util.h
@@ -5,6 +5,8 @@
 #ifndef CHROMEOS_COMPONENTS_PHONEHUB_UTIL_HISTOGRAM_UTIL_H_
 #define CHROMEOS_COMPONENTS_PHONEHUB_UTIL_HISTOGRAM_UTIL_H_
 
+#include "chromeos/components/phonehub/proto/phonehub_api.pb.h"
+
 namespace chromeos {
 namespace phonehub {
 namespace util {
@@ -27,12 +29,24 @@
   kMaxValue = kSuccess,
 };
 
+// Keep in sync with corresponding enum in tools/metrics/histograms/enums.xml.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class PhoneHubMessageResult {
+  kRequestAttempted = 0,
+  kResponseReceived = 1,
+  kMaxValue = kResponseReceived,
+};
+
 // Logs a given opt-in |entry_point| for the PhoneHub feature.
 void LogFeatureOptInEntryPoint(OptInEntryPoint entry_point);
 
 // Logs a given |result| of a tethering connection attempt.
 void LogTetherConnectionResult(TetherConnectionResult result);
 
+// Logs a given |result| for a request message.
+void LogMessageResult(proto::MessageType message, PhoneHubMessageResult result);
+
 }  // namespace util
 }  // namespace phonehub
 }  // namespace chromeos
diff --git a/chromeos/components/quick_answers/search_result_loader.cc b/chromeos/components/quick_answers/search_result_loader.cc
index dfaa8f30..28f2195 100644
--- a/chromeos/components/quick_answers/search_result_loader.cc
+++ b/chromeos/components/quick_answers/search_result_loader.cc
@@ -30,6 +30,9 @@
 //   "client_id": {
 //     "client_type": "EXPERIMENTAL"
 //   }
+//   "options": {
+//     "page_size": "1"
+//   }
 // }
 //
 // Which is:
@@ -38,12 +41,17 @@
 //      "raw_query": STRING
 //   "client_id": DICT
 //       "client_type": STRING
+//   "options": DICT
+//       "page_size": STRING
 
 constexpr base::StringPiece kQueryKey = "query";
 constexpr base::StringPiece kRawQueryKey = "rawQuery";
 constexpr base::StringPiece kClientTypeKey = "clientType";
 constexpr base::StringPiece kClientIdKey = "clientId";
 constexpr base::StringPiece kClientType = "QUICK_ANSWERS_CROS";
+constexpr base::StringPiece kPageSizeKey = "pageSize";
+constexpr base::StringPiece kOptionsKey = "options";
+constexpr base::StringPiece kPageSize = "1";
 
 std::string BuildSearchRequestPayload(const std::string& selected_text) {
   Value payload(Value::Type::DICTIONARY);
@@ -57,6 +65,10 @@
   client_id.SetKey(kClientTypeKey, Value(kClientType));
   payload.SetKey(kClientIdKey, std::move(client_id));
 
+  Value options(Value::Type::DICTIONARY);
+  options.SetKey(kPageSizeKey, Value(kPageSize));
+  payload.SetKey(kOptionsKey, std::move(options));
+
   std::string request_payload_str;
   base::JSONWriter::Write(payload, &request_payload_str);
 
diff --git a/chromeos/components/scanning/resources/color_mode_select.html b/chromeos/components/scanning/resources/color_mode_select.html
index 904629db8..213cf7a 100644
--- a/chromeos/components/scanning/resources/color_mode_select.html
+++ b/chromeos/components/scanning/resources/color_mode_select.html
@@ -4,8 +4,8 @@
   <div slot="settings">
     <!-- TODO(jschettler): Verify this meets a11y expecations (e.g. ChromeVox
         should announce when a new option is focused). -->
-    <select class="md-select" value="{{selectedColorMode::change}}"
-        disabled="[[disabled]]">
+    <select id="colorModeSelect" class="md-select"
+        value="{{selectedColorMode::change}}" disabled="[[disabled]]">
       <!-- TODO(jschettler): Determine how the color modes should be sorted. -->
       <template is="dom-repeat" items="[[colorModes]]" as="colorMode">
         <option value="[[colorMode]]"
diff --git a/chromeos/components/scanning/resources/file_type_select.html b/chromeos/components/scanning/resources/file_type_select.html
index 410519c..bca68fdb 100644
--- a/chromeos/components/scanning/resources/file_type_select.html
+++ b/chromeos/components/scanning/resources/file_type_select.html
@@ -4,8 +4,8 @@
   <div slot="settings">
     <!-- TODO(jschettler): Verify this meets a11y expecations (e.g. ChromeVox
         should announce when a new option is focused). -->
-    <select class="md-select" value="{{selectedFileType::change}}"
-        disabled="[[disabled]]">
+    <select id="fileTypeSelect" class="md-select"
+        value="{{selectedFileType::change}}" disabled="[[disabled]]">
       <!-- The option values must match the chromeos.scanning.mojom.FileType
           values they correspond to. -->
       <!-- TODO(jschettler): Change default back to PDF when it's supported. -->
diff --git a/chromeos/components/scanning/resources/page_size_select.html b/chromeos/components/scanning/resources/page_size_select.html
index cba49ee..2ba65d8 100644
--- a/chromeos/components/scanning/resources/page_size_select.html
+++ b/chromeos/components/scanning/resources/page_size_select.html
@@ -4,8 +4,8 @@
   <div slot="settings">
     <!-- TODO(jschettler): Verify this meets a11y expecations (e.g. ChromeVox
         should announce when a new option is focused). -->
-    <select class="md-select" value="{{selectedPageSize::change}}"
-        disabled="[[disabled]]">
+    <select id="pageSizeSelect" class="md-select"
+        value="{{selectedPageSize::change}}" disabled="[[disabled]]">
       <!-- TODO(jschettler): Sort the page sizes. -->
       <template is="dom-repeat" items="[[pageSizes]]" as="pageSize">
         <option value="[[pageSize]]"
diff --git a/chromeos/components/scanning/resources/resolution_select.html b/chromeos/components/scanning/resources/resolution_select.html
index 1e4fe1e..95857a31 100644
--- a/chromeos/components/scanning/resources/resolution_select.html
+++ b/chromeos/components/scanning/resources/resolution_select.html
@@ -4,8 +4,8 @@
   <div slot="settings">
     <!-- TODO(jschettler): Verify this meets a11y expecations (e.g. ChromeVox
         should announce when a new option is focused). -->
-    <select class="md-select" value="{{selectedResolution::change}}"
-        disabled="[[disabled]]">
+    <select id="resolutionSelect" class="md-select"
+        value="{{selectedResolution::change}}" disabled="[[disabled]]">
       <!-- TODO(jschettler): Sort the resolutions. -->
       <template is="dom-repeat" items="[[resolutions]]" as="resolution">
         <option value="[[resolution]]"
diff --git a/chromeos/components/scanning/resources/scanner_select.html b/chromeos/components/scanning/resources/scanner_select.html
index 541603f..e2ccc5f 100644
--- a/chromeos/components/scanning/resources/scanner_select.html
+++ b/chromeos/components/scanning/resources/scanner_select.html
@@ -19,8 +19,9 @@
     </div>
     <!-- TODO(jschettler): Verify this meets a11y expecations (e.g. ChromeVox
         should announce when a new option is focused). -->
-    <select class="md-select" value="{{selectedScannerId::change}}"
-        hidden$="[[!loaded]]" disabled="[[disabled]]">
+    <select id="scannerSelect" class="md-select"
+        value="{{selectedScannerId::change}}" hidden$="[[!loaded]]"
+        disabled="[[disabled]]">
       <!-- TODO(jschettler): Figure out why hiding/disabling the option doesn't
           remove it from the dropdown. -->
       <template is="dom-if" if="[[!scanners.length]]" restamp>
diff --git a/chromeos/components/scanning/resources/scanning_app.html b/chromeos/components/scanning/resources/scanning_app.html
index 68dd3772..07516e20 100644
--- a/chromeos/components/scanning/resources/scanning_app.html
+++ b/chromeos/components/scanning/resources/scanning_app.html
@@ -46,15 +46,13 @@
     box-sizing: border-box;
     display: block;
     margin: 0 auto;
-    min-height: 480px;
-    min-width: 600px;
     position: relative;
     width: var(--container-width);
   }
 
   .left-panel {
-    padding-inline-end: var(--left-panel-padding-end);
-    padding-inline-start: var(--left-panel-padding-start);
+    margin-inline-end: var(--left-panel-margin-inline-end);
+    margin-inline-start: var(--left-panel-margin-inline-start);
     width: var(--left-panel-width);
   }
 
@@ -64,9 +62,10 @@
   }
 
   .right-panel {
-    margin-bottom: var(--right-panel-margin-bottom);
-    padding-inline-end: var(--right-panel-padding-end);
-    padding-inline-start: var(--right-panel-padding-start);
+    margin-inline-end: var(--right-panel-margin-inline-end);
+    margin-inline-start: var(--right-panel-margin-inline-start);
+    padding-inline-end: var(--right-panel-padding-inline-end);
+    padding-inline-start: var(--right-panel-padding-inline-start);
     width: var(--right-panel-width);
   }
 
diff --git a/chromeos/components/scanning/resources/scanning_shared_css.html b/chromeos/components/scanning/resources/scanning_shared_css.html
index 31ff00ee..64dede8 100644
--- a/chromeos/components/scanning/resources/scanning_shared_css.html
+++ b/chromeos/components/scanning/resources/scanning_shared_css.html
@@ -15,45 +15,62 @@
       text-overflow: ellipsis;
     }
 
-    /* TODO(michaelcheco): Update once responsiveness spec is finalized. */
-    @media (min-width:600px) and (max-width: 767px) {
+    @media (min-width: 600px) {
       :host {
-        --container-width: 767px;
-        --left-panel-padding-end: 32px;
-        --left-panel-padding-start: 32px;
+        --container-width: 600px;
+        --left-panel-margin-inline-end: 10px;
+        --left-panel-margin-inline-start: 10px;
+        --left-panel-width: 200px;
+        --panel-container-margin-top: 20px;
+        --right-panel-margin-inline-end: 10px;
+        --right-panel-margin-inline-start: 0;
+        --right-panel-padding-inline-end: 8px;
+        --right-panel-padding-inline-start: 8px;
+        --right-panel-width: 370px;
+      }
+    }
+
+    @media (min-width: 768px) {
+      :host {
+        --container-width: 768px;
+        --left-panel-margin-inline-end: 32px;
+        --left-panel-margin-inline-start: 32px;
         --left-panel-width: 288px;
         --panel-container-margin-top: 20px;
-        --right-panel-margin-bottom: 20px;
-        --right-panel-padding-end: 32px;
-        --right-panel-padding-start: 0px;
+        --right-panel-margin-inline-end: 32px;
+        --right-panel-margin-inline-start: 0;
+        --right-panel-padding-inline-end: 16px;
+        --right-panel-padding-inline-start: 16px;
         --right-panel-width: 384px;
       }
     }
 
-    @media (min-width:768px) and (max-width: 1279px) {
+    @media (min-width: 960px) {
       :host {
         --container-width: 960px;
-        --left-panel-padding-end: 48px;
-        --left-panel-padding-start: 48px;
+        --left-panel-margin-inline-end: 48px;
+        --left-panel-margin-inline-start: 48px;
         --left-panel-width: 384px;
         --panel-container-margin-top: 20px;
-        --right-panel-margin-bottom: 32px;
-        --right-panel-padding-end: 48px;
-        --right-panel-padding-start: 48px;
+        --right-panel-margin-inline-end: 48px;
+        --right-panel-margin-inline-start: 48px;
+        --right-panel-padding-inline-end: 16px;
+        --right-panel-padding-inline-start: 16px;
         --right-panel-width: 384px;
       }
     }
 
-    @media (min-width:1280px) {
+    @media (min-width: 1280px) {
       :host {
         --container-width: 1280px;
-        --left-panel-padding-end: 60px;
-        --left-panel-padding-start: 164px;
+        --left-panel-margin-inline-end: 60px;
+        --left-panel-margin-inline-start: 164px;
         --left-panel-width: 416px;
         --panel-container-margin-top: 64px;
-        --right-panel-margin-bottom: 64px;
-        --right-panel-padding-end: 164px;
-        --right-panel-padding-start: 60px;
+        --right-panel-margin-inline-end: 164px;
+        --right-panel-margin-inline-start: 60px;
+        --right-panel-padding-inline-end: 32px;
+        --right-panel-padding-inline-start: 32px;
         --right-panel-width: 416px;
       }
     }
diff --git a/chromeos/components/scanning/resources/source_select.html b/chromeos/components/scanning/resources/source_select.html
index 708fa85..17e8a2b 100644
--- a/chromeos/components/scanning/resources/source_select.html
+++ b/chromeos/components/scanning/resources/source_select.html
@@ -4,8 +4,8 @@
   <div slot="settings">
     <!-- TODO(jschettler): Verify this meets a11y expecations (e.g. ChromeVox
         should announce when a new option is focused). -->
-    <select class="md-select" value="{{selectedSource::change}}"
-        disabled="[[disabled]]">
+    <select id="sourceSelect" class="md-select"
+        value="{{selectedSource::change}}" disabled="[[disabled]]">
       <template is="dom-if" if="[[!sources.length]]" restamp>
         <option value="">
           [[i18n('defaultSourceOptionText')]]
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc
index cc23c4d..fc46df7 100644
--- a/chromeos/constants/chromeos_switches.cc
+++ b/chromeos/constants/chromeos_switches.cc
@@ -418,6 +418,10 @@
 const char kIgnoreUserProfileMappingForTests[] =
     "ignore-user-profile-mapping-for-tests";
 
+// Decreases delay in uploading installation event logs for integration test.
+const char kInstallLogFastUploadForTests[] =
+    "install-log-fast-upload-for-tests";
+
 // If set, the Chrome settings will not expose the option to enable crostini
 // unless the enable-experimental-kernel-vm-support flag is set in
 // chrome://flags
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h
index 0daad9f..33fe4dd 100644
--- a/chromeos/constants/chromeos_switches.h
+++ b/chromeos/constants/chromeos_switches.h
@@ -168,6 +168,8 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kIgnoreArcVmDevConf[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kIgnoreUserProfileMappingForTests[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kInstallLogFastUploadForTests[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kKernelnextRestrictVMs[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kLacrosChromeAdditionalArgs[];
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
index 641cb93..e1b519a6b 100644
--- a/chromeos/profiles/orderfile.newest.txt
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -1 +1 @@
-chromeos-chrome-orderfile-field-89-4324.9-1606733211-benchmark-89.0.4337.0-r1.orderfile.xz
+chromeos-chrome-orderfile-field-89-4324.9-1606733211-benchmark-89.0.4339.0-r1.orderfile.xz
diff --git a/chromeos/services/cros_healthd/public/cpp/service_connection_unittest.cc b/chromeos/services/cros_healthd/public/cpp/service_connection_unittest.cc
index 61700aa2..7f0b4fb 100644
--- a/chromeos/services/cros_healthd/public/cpp/service_connection_unittest.cc
+++ b/chromeos/services/cros_healthd/public/cpp/service_connection_unittest.cc
@@ -218,6 +218,11 @@
               HttpsLatency,
               (NetworkDiagnosticsRoutines::HttpsLatencyCallback),
               (override));
+  MOCK_METHOD(void,
+              VideoConferencing,
+              (const base::Optional<std::string>&,
+               NetworkDiagnosticsRoutines::VideoConferencingCallback),
+              (override));
 
   mojo::PendingRemote<NetworkDiagnosticsRoutines> pending_remote() {
     if (receiver_.is_bound()) {
diff --git a/chromeos/services/nearby/DEPS b/chromeos/services/nearby/DEPS
index 2494de8e..c3fb294 100644
--- a/chromeos/services/nearby/DEPS
+++ b/chromeos/services/nearby/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+components/keyed_service/core/keyed_service.h",
+  "+device/bluetooth/public/cpp/bluetooth_uuid.h",
 ]
diff --git a/chromeos/services/nearby/public/cpp/BUILD.gn b/chromeos/services/nearby/public/cpp/BUILD.gn
index 3e6c9ccb..ecfd3a1 100644
--- a/chromeos/services/nearby/public/cpp/BUILD.gn
+++ b/chromeos/services/nearby/public/cpp/BUILD.gn
@@ -3,7 +3,11 @@
 # found in the LICENSE file.
 
 static_library("cpp") {
-  sources = [ "nearby_process_manager.h" ]
+  sources = [
+    "nearby_client_uuids.cc",
+    "nearby_client_uuids.h",
+    "nearby_process_manager.h",
+  ]
 
   deps = [
     "//base",
diff --git a/chromeos/services/nearby/public/cpp/nearby_client_uuids.cc b/chromeos/services/nearby/public/cpp/nearby_client_uuids.cc
new file mode 100644
index 0000000..c0ba34ea
--- /dev/null
+++ b/chromeos/services/nearby/public/cpp/nearby_client_uuids.cc
@@ -0,0 +1,43 @@
+// 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 "chromeos/services/nearby/public/cpp/nearby_client_uuids.h"
+
+#include <set>
+#include <string>
+
+#include "base/no_destructor.h"
+#include "base/stl_util.h"
+
+namespace chromeos {
+namespace nearby {
+
+namespace {
+const char kNearbySharingUuid[] = "a82efa21-ae5c-3dde-9bbc-f16da7b16c5a";
+const char kSecureChannelUuid[] = "a384bd4f-41ea-3b02-8901-8c2ed9a79970";
+}  // namespace
+
+const std::vector<device::BluetoothUUID>& GetNearbyClientUuids() {
+  static const base::NoDestructor<std::vector<device::BluetoothUUID>>
+      kAllowedUuids([] {
+        // This literal initialization unfortunately does not work with
+        // base::NoDestructor.
+        std::vector<device::BluetoothUUID> allowed_uuids{
+            device::BluetoothUUID(kNearbySharingUuid),
+            device::BluetoothUUID(kSecureChannelUuid)};
+        return allowed_uuids;
+      }());
+
+  return *kAllowedUuids;
+}
+
+bool IsNearbyClientUuid(const device::BluetoothUUID& uuid) {
+  static const base::NoDestructor<std::set<device::BluetoothUUID>>
+      kAllowedUuidSet(std::begin(GetNearbyClientUuids()),
+                      std::end(GetNearbyClientUuids()));
+  return base::Contains(*kAllowedUuidSet, uuid);
+}
+
+}  // namespace nearby
+}  // namespace chromeos
diff --git a/chromeos/services/nearby/public/cpp/nearby_client_uuids.h b/chromeos/services/nearby/public/cpp/nearby_client_uuids.h
new file mode 100644
index 0000000..edc612d
--- /dev/null
+++ b/chromeos/services/nearby/public/cpp/nearby_client_uuids.h
@@ -0,0 +1,28 @@
+// 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 CHROMEOS_SERVICES_NEARBY_PUBLIC_CPP_NEARBY_CLIENT_UUIDS_H_
+#define CHROMEOS_SERVICES_NEARBY_PUBLIC_CPP_NEARBY_CLIENT_UUIDS_H_
+
+#include <vector>
+
+#include "device/bluetooth/public/cpp/bluetooth_uuid.h"
+
+namespace chromeos {
+namespace nearby {
+
+// Returns a list of Bluetooth Service UUIDs corresponding to current clients
+// of Nearby Connections (e.g., Nearby Share). Callers can use this function or
+// IsNearbyClientUuid() to verify that outgoing or incoming Bluetooth
+// connections are initiated by said recognized Nearby Connections clients.
+const std::vector<device::BluetoothUUID>& GetNearbyClientUuids();
+
+// Helper function to check if |uuid| is present in list returned by
+// GetNearbyClientUuids().
+bool IsNearbyClientUuid(const device::BluetoothUUID& uuid);
+
+}  // namespace nearby
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_NEARBY_PUBLIC_CPP_NEARBY_CLIENT_UUIDS_H_
diff --git a/chromeos/services/network_health/public/mojom/network_diagnostics.mojom b/chromeos/services/network_health/public/mojom/network_diagnostics.mojom
index 8bb1675..0601a58 100644
--- a/chromeos/services/network_health/public/mojom/network_diagnostics.mojom
+++ b/chromeos/services/network_health/public/mojom/network_diagnostics.mojom
@@ -128,6 +128,17 @@
   kVeryHighLatency,
 };
 
+// Problems related to the VideoConferencing routine.
+[Extensible]
+enum VideoConferencingProblem {
+  // Failed requests to a STUN server via UDP.
+  kUdpFailure,
+  // Failed requests to a STUN server via TCP.
+  kTcpFailure,
+  // Failed to establish a TLS connection to media hostnames.
+  kMediaFailure,
+};
+
 // This interface is to be used by any clients that need to run specific
 // network-related diagnostics. Expected clients of this interface are
 // NetworkHealth, cros_healthd, and a connectivity diagnostics Web UI (to name
@@ -180,4 +191,12 @@
   // the system.
   HttpsLatency() => (RoutineVerdict verdict,
       array<HttpsLatencyProblem> problems);
+
+  // Tests the device's video conferencing capabalities by testing whether the
+  // device can:
+  // (1) Contact either a default or specified STUN server via UDP.
+  // (2) Contact either a default or specified STUN server via TCP.
+  // (3) Reach common media endpoints.
+  VideoConferencing(string? stun_server_hostname) => (RoutineVerdict verdict,
+      array<VideoConferencingProblem> problems, string? support_details);
 };
diff --git a/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtils.java b/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtils.java
index 0ba2bb7..cb93affe 100644
--- a/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtils.java
+++ b/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtils.java
@@ -11,6 +11,7 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Environment;
 import android.provider.MediaStore;
 import android.text.TextUtils;
@@ -19,7 +20,6 @@
 
 import org.chromium.base.ApplicationState;
 import org.chromium.base.ApplicationStatus;
-import org.chromium.base.BuildInfo;
 import org.chromium.base.Callback;
 import org.chromium.base.ContentUriUtils;
 import org.chromium.base.ContextUtils;
@@ -258,7 +258,7 @@
 
                 Uri uri = null;
                 if (!isTemporary) {
-                    if (BuildInfo.isAtLeastQ()) {
+                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                         uri = addToMediaStore(destFile);
                     } else {
                         long downloadId = addCompletedDownload(destFile);
@@ -386,7 +386,7 @@
 
     @TargetApi(29)
     public static Uri addToMediaStore(File file) {
-        assert BuildInfo.isAtLeastQ();
+        assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
 
         final ContentValues contentValues = new ContentValues();
         contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, file.getName());
diff --git a/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtilsTest.java b/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtilsTest.java
index 7d8ed97..0e0f68a6 100644
--- a/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtilsTest.java
+++ b/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtilsTest.java
@@ -14,6 +14,7 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Environment;
 import android.os.Looper;
 import android.provider.MediaStore;
@@ -25,7 +26,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.BuildInfo;
 import org.chromium.base.Callback;
 import org.chromium.base.ContentUriUtils;
 import org.chromium.base.ContextUtils;
@@ -155,7 +155,7 @@
 
     private void deleteAllTestImages() throws TimeoutException {
         AsyncTask.SERIAL_EXECUTOR.execute(() -> {
-            if (BuildInfo.isAtLeastQ()) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                 deleteMediaStoreFiles();
             }
             deleteExternalStorageFiles();
diff --git a/components/browser_ui/styles/android/java/res/values/styles.xml b/components/browser_ui/styles/android/java/res/values/styles.xml
index ac23f20..b73e81a 100644
--- a/components/browser_ui/styles/android/java/res/values/styles.xml
+++ b/components/browser_ui/styles/android/java/res/values/styles.xml
@@ -9,6 +9,7 @@
         <!-- Text colors -->
         <item name="android:textColorLink">@color/default_text_color_link</item>
         <item name="android:textColorHighlight">@color/text_highlight_color</item>
+        <item name="android:textColorHint">@color/default_text_color_secondary</item>
 
         <!-- Color of checkboxes, switches, buttons, etc. -->
         <item name="colorAccent">@color/default_control_color_active</item>
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonRenderTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonRenderTest.java
index 828ef310..ee143d1 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonRenderTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonRenderTest.java
@@ -41,8 +41,14 @@
     private static List<ParameterSet> sClassParams =
             new NightModeTestUtils.NightModeParams().getParameters();
 
+    private static final int REVISION = 1;
+    private static final String REVISION_DESCRIPTION = "Updated EditText hint color for a11y";
+
     @Rule
-    public RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus().build();
+    public RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus()
+                                                    .setRevision(REVISION)
+                                                    .setDescription(REVISION_DESCRIPTION)
+                                                    .build();
 
     private RadioButtonWithDescriptionLayout mLayout;
 
diff --git a/components/component_updater/component_updater_paths.cc b/components/component_updater/component_updater_paths.cc
index 79e89d5..31574f8 100644
--- a/components/component_updater/component_updater_paths.cc
+++ b/components/component_updater/component_updater_paths.cc
@@ -18,9 +18,6 @@
 
 }  // namespace
 
-const base::FilePath::CharType kSupervisedUserWhitelistDirName[] =
-    FILE_PATH_LITERAL("SupervisedUserWhitelists");
-
 bool PathProvider(int key, base::FilePath* result) {
   DCHECK_GT(g_components_user_root_key, 0);
   DCHECK_GT(g_components_preinstalled_root_key, 0);
@@ -54,9 +51,6 @@
     case DIR_SWIFT_SHADER:
       cur = cur.Append(FILE_PATH_LITERAL("SwiftShader"));
       break;
-    case DIR_SUPERVISED_USER_ALLOWLISTS:
-      cur = cur.Append(kSupervisedUserWhitelistDirName);
-      break;
     default:
       return false;
   }
diff --git a/components/component_updater/component_updater_paths.h b/components/component_updater/component_updater_paths.h
index b9be88b8..d9383d2 100644
--- a/components/component_updater/component_updater_paths.h
+++ b/components/component_updater/component_updater_paths.h
@@ -9,8 +9,6 @@
 
 namespace component_updater {
 
-extern const base::FilePath::CharType kSupervisedUserWhitelistDirName[];
-
 enum {
   PATH_START = 10000,
   DIR_COMPONENT_PREINSTALLED = PATH_START,  // Directory that contains component
@@ -31,7 +29,6 @@
   DIR_COMPONENT_CLD2,              // The Compact Language Detector.
   DIR_RECOVERY_BASE,               // The Recovery.
   DIR_SWIFT_SHADER,                // The SwiftShader.
-  DIR_SUPERVISED_USER_ALLOWLISTS,  // The Supervised user allowlists.
   PATH_END
 };
 
diff --git a/components/crash/core/app/crashpad_android.cc b/components/crash/core/app/crashpad_android.cc
index 66b0506d..27e34434 100644
--- a/components/crash/core/app/crashpad_android.cc
+++ b/components/crash/core/app/crashpad_android.cc
@@ -295,7 +295,8 @@
                           std::string* handler_library) {
   // The linker doesn't support loading executables passed on its command
   // line until Q.
-  if (!base::android::BuildInfo::GetInstance()->is_at_least_q()) {
+  if (base::android::BuildInfo::GetInstance()->sdk_int() <
+      base::android::SDK_VERSION_Q) {
     return false;
   }
 
diff --git a/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java b/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java
index 680a7237..c5f96a97 100644
--- a/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java
+++ b/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java
@@ -22,7 +22,6 @@
 
 import androidx.annotation.NonNull;
 
-import org.chromium.base.BuildInfo;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.StrictModeContext;
@@ -132,7 +131,7 @@
      */
     @CalledByNative
     public static boolean shouldPublishDownload(final String filePath) {
-        if (isAtLeastQ()) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
             if (filePath == null) return false;
             // Only need to publish downloads that are on primary storage.
             return !sDownloadDelegate.isDownloadOnSDCard(filePath);
@@ -297,7 +296,7 @@
      * @return whether download collection is supported.
      */
     public static boolean supportsDownloadCollection() {
-        return isAtLeastQ();
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
     }
 
     /**
@@ -333,10 +332,6 @@
         return DownloadCollectionBridgeJni.get().getExpirationDurationInDays();
     }
 
-    private static boolean isAtLeastQ() {
-        return BuildInfo.isAtLeastQ() || Build.VERSION.SDK_INT >= 29;
-    }
-
     /**
      * Helper method to create a pending session for download to be written into.
      * @param fileName Name of the file.
diff --git a/components/download/internal/common/in_progress_download_manager.cc b/components/download/internal/common/in_progress_download_manager.cc
index 1b80240..08e1835 100644
--- a/components/download/internal/common/in_progress_download_manager.cc
+++ b/components/download/internal/common/in_progress_download_manager.cc
@@ -576,7 +576,8 @@
     std::unique_ptr<std::vector<DownloadDBEntry>> entries) {
 #if defined(OS_ANDROID)
   // Retrieve display names for all downloads from media store if needed.
-  if (base::android::BuildInfo::GetInstance()->is_at_least_q()) {
+  if (base::android::BuildInfo::GetInstance()->sdk_int() >=
+      base::android::SDK_VERSION_Q) {
     DownloadCollectionBridge::GetDisplayNamesCallback callback =
         base::BindOnce(&InProgressDownloadManager::OnDownloadNamesRetrieved,
                        weak_factory_.GetWeakPtr(), std::move(entries));
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index 4ccf798..dbd8e53 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -340,7 +340,10 @@
 
 void Server::OnDisplayRemoved(const display::Display& old_display) {
   DCHECK_EQ(outputs_.count(old_display.id()), 1u);
+  std::unique_ptr<WaylandDisplayOutput> output =
+      std::move(outputs_[old_display.id()]);
   outputs_.erase(old_display.id());
+  output.release()->OnDisplayRemoved();
 }
 
 wl_resource* Server::GetOutputResource(wl_client* client, int64_t display_id) {
diff --git a/components/exo/wayland/wayland_display_observer.cc b/components/exo/wayland/wayland_display_observer.cc
index aa9b2db5..e15cb23 100644
--- a/components/exo/wayland/wayland_display_observer.cc
+++ b/components/exo/wayland/wayland_display_observer.cc
@@ -35,9 +35,14 @@
   observers_.AddObserver(observer);
 
   display::Display display;
-  bool rv = display::Screen::GetScreen()->GetDisplayWithDisplayId(output_->id(),
-                                                                  &display);
-  DCHECK(rv);
+  bool exists = display::Screen::GetScreen()->GetDisplayWithDisplayId(
+      output_->id(), &display);
+  if (!exists) {
+    // WaylandDisplayHandler is created asynchronously, and the
+    // display can be deleted before created. This usually won't happen
+    // in real environment, but can happen in test environment.
+    return;
+  }
 
   // Send the first round of changes to the observer.
   constexpr uint32_t all_changes = 0xFFFFFFFF;
@@ -53,6 +58,8 @@
 void WaylandDisplayHandler::OnDisplayMetricsChanged(
     const display::Display& display,
     uint32_t changed_metrics) {
+  DCHECK(output_resource_);
+
   if (output_->id() != display.id())
     return;
 
@@ -68,9 +75,11 @@
     wl_client_flush(wl_resource_get_client(output_resource_));
   }
 }
-
 bool WaylandDisplayHandler::SendDisplayMetrics(const display::Display& display,
                                                uint32_t changed_metrics) {
+  if (!output_resource_)
+    return false;
+
   // There is no need to check DISPLAY_METRIC_PRIMARY because when primary
   // changes, bounds always changes. (new primary should have had non
   // 0,0 origin).
diff --git a/components/exo/wayland/wayland_display_output.cc b/components/exo/wayland/wayland_display_output.cc
index 569c8d6..79a9fd6a 100644
--- a/components/exo/wayland/wayland_display_output.cc
+++ b/components/exo/wayland/wayland_display_output.cc
@@ -11,11 +11,27 @@
 
 #include "base/logging.h"
 #include "base/stl_util.h"
+#include "base/task/thread_pool.h"
 #include "components/exo/surface.h"
 #include "components/exo/wayland/server_util.h"
 
 namespace exo {
 namespace wayland {
+namespace {
+base::TimeDelta kDeleteTaskDelay = base::TimeDelta::FromSeconds(3);
+
+void DoDelete(WaylandDisplayOutput* output, int retry_count) {
+  if (retry_count > 0 && output->output_counts() > 0) {
+    // Try a few times to give a client chance to release it.
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, base::BindOnce(&DoDelete, output, retry_count - 1),
+        kDeleteTaskDelay);
+  } else {
+    delete output;
+  }
+}
+
+}  // namespace
 
 WaylandDisplayOutput::WaylandDisplayOutput(int64_t id) : id_(id) {}
 
@@ -29,6 +45,15 @@
     wl_global_destroy(global_);
 }
 
+void WaylandDisplayOutput::OnDisplayRemoved() {
+  if (global_)
+    wl_global_remove(global_);
+
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, base::BindOnce(&DoDelete, this, /*retry_count=*/3),
+      kDeleteTaskDelay);
+}
+
 int64_t WaylandDisplayOutput::id() const {
   return id_;
 }
diff --git a/components/exo/wayland/wayland_display_output.h b/components/exo/wayland/wayland_display_output.h
index eeae307..58c25f93 100644
--- a/components/exo/wayland/wayland_display_output.h
+++ b/components/exo/wayland/wayland_display_output.h
@@ -17,8 +17,11 @@
 namespace exo {
 namespace wayland {
 
-// Class that represent a wayland output. Tied to a specific display ID
-// and associated with a global.
+// Class that represent a wayland output. Tied to a specific display ID and
+// associated with a global, and wl_outputs created by clients.  This object
+// will self destruct upon the display removal aftrer delays up to 9 seconds (3
+// seconds x 3 times) to give time for clients to release the output they
+// created, excepf for the shutdown scenario where they're removed immediately.
 class WaylandDisplayOutput {
  public:
   explicit WaylandDisplayOutput(int64_t display_id);
@@ -34,6 +37,11 @@
 
   wl_resource* GetOutputResourceForClient(wl_client* client);
 
+  // Self destruct in 5 seconeds.
+  void OnDisplayRemoved();
+
+  int output_counts() const { return output_ids_.size(); }
+
  private:
   const int64_t id_;
   wl_global* global_ = nullptr;
diff --git a/components/ntp_tiles/most_visited_sites.h b/components/ntp_tiles/most_visited_sites.h
index a2f5403..4e1f4f3 100644
--- a/components/ntp_tiles/most_visited_sites.h
+++ b/components/ntp_tiles/most_visited_sites.h
@@ -75,6 +75,7 @@
   // If true, |url| should not be shown on the NTP.
   virtual bool IsBlocked(const GURL& url) = 0;
 
+  // TODO(crbug.com/1149782): Remove the allowlists from New Tab Page.
   // Explicitly-specified sites to show on NTP.
   virtual std::vector<Allowlist> GetAllowlists() = 0;
 
diff --git a/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy.cc b/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy.cc
index 22bedb9..4f4dd36 100644
--- a/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy.cc
+++ b/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy.cc
@@ -77,7 +77,8 @@
 
   // Don't throttle unless http or https.
   bool throttled = true;
-  const GURL& url = contents->GetLastCommittedURL();
+  // Use `GetVisibleURL()` since we haven't committed the navigation yet.
+  const GURL& url = contents->GetVisibleURL();
   if (!CanThrottleUrlScheme(url))
     throttled = false;
 
diff --git a/components/policy/core/common/policy_map.cc b/components/policy/core/common/policy_map.cc
index cdff814e..00826ec7 100644
--- a/components/policy/core/common/policy_map.cc
+++ b/components/policy/core/common/policy_map.cc
@@ -23,10 +23,9 @@
 
 const base::string16 GetLocalizedString(
     PolicyMap::Entry::L10nLookupFunction lookup,
-    const base::string16& initial_string,
     const std::map<int, base::Optional<std::vector<base::string16>>>&
         localized_string_ids) {
-  base::string16 result = initial_string;
+  base::string16 result = base::string16();
   base::string16 line_feed = base::UTF8ToUTF16("\n");
   for (const auto& string_pairs : localized_string_ids) {
     if (string_pairs.second)
@@ -70,7 +69,6 @@
                  ? std::make_unique<ExternalDataFetcher>(*external_data_fetcher)
                  : nullptr);
   copy.ignored_ = ignored_;
-  copy.error_strings_ = error_strings_;
   copy.error_message_ids_ = error_message_ids_;
   copy.warning_message_ids_ = warning_message_ids_;
   copy.is_default_value_ = is_default_value_;
@@ -100,7 +98,6 @@
       conflicts_are_equal && level == other.level && scope == other.scope &&
       source == other.source &&  // Necessary for PolicyUIHandler observers.
                                  // They have to update when sources change.
-      error_strings_ == other.error_strings_ &&
       error_message_ids_ == other.error_message_ids_ &&
       warning_message_ids_ == other.warning_message_ids_ &&
       is_default_value_ == other.is_default_value_ &&
@@ -111,10 +108,6 @@
   return equals;
 }
 
-void PolicyMap::Entry::AddError(base::StringPiece error) {
-  base::StrAppend(&error_strings_, {error, "\n"});
-}
-
 void PolicyMap::Entry::AddError(int message_id) {
   error_message_ids_.emplace(message_id, base::nullopt);
 }
@@ -152,13 +145,12 @@
 
 base::string16 PolicyMap::Entry::GetLocalizedErrors(
     L10nLookupFunction lookup) const {
-  return GetLocalizedString(lookup, base::UTF8ToUTF16(error_strings_),
-                            error_message_ids_);
+  return GetLocalizedString(lookup, error_message_ids_);
 }
 
 base::string16 PolicyMap::Entry::GetLocalizedWarnings(
     L10nLookupFunction lookup) const {
-  return GetLocalizedString(lookup, base::string16(), warning_message_ids_);
+  return GetLocalizedString(lookup, warning_message_ids_);
 }
 
 bool PolicyMap::Entry::ignored() const {
@@ -254,10 +246,6 @@
   map_[policy] = std::move(entry);
 }
 
-void PolicyMap::AddError(const std::string& policy, const std::string& error) {
-  map_[policy].AddError(error);
-}
-
 void PolicyMap::AddError(const std::string& policy, int message_id) {
   map_[policy].AddError(message_id);
 }
diff --git a/components/policy/core/common/policy_map.h b/components/policy/core/common/policy_map.h
index 09b8ab4..dfce115 100644
--- a/components/policy/core/common/policy_map.h
+++ b/components/policy/core/common/policy_map.h
@@ -70,8 +70,6 @@
     // Returns true if |this| equals |other|.
     bool Equals(const Entry& other) const;
 
-    // Add a non-localized error given its UTF-8 string contents.
-    void AddError(base::StringPiece error);
     // Add a localized error given its l10n message ID.
     void AddError(int message_id);
     // Add a localized error given its l10n message ID and placeholder args.
@@ -128,7 +126,6 @@
     base::Optional<base::Value> value_;
     bool ignored_ = false;
     bool is_default_value_ = false;
-    std::string error_strings_;
     std::map<int, base::Optional<std::vector<base::string16>>>
         error_message_ids_;
     std::map<int, base::Optional<std::vector<base::string16>>>
@@ -164,11 +161,6 @@
 
   void Set(const std::string& policy, Entry entry);
 
-  // Adds non-localized |error| to the map for the key |policy| that should be
-  // shown to the user alongside the value in the policy UI. This should only be
-  // called for policies that are already stored in this map.
-  void AddError(const std::string& policy, const std::string& error);
-
   // Adds a localized error with |message_id| to the map for the key |policy|
   // that should be shown to the user alongisde the value in the policy UI. This
   // should only be called for policies that are already stored in the map.
diff --git a/components/policy/core/common/policy_map_unittest.cc b/components/policy/core/common/policy_map_unittest.cc
index 962acff..838bddf 100644
--- a/components/policy/core/common/policy_map_unittest.cc
+++ b/components/policy/core/common/policy_map_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/policy/core/common/external_data_manager.h"
@@ -81,16 +82,18 @@
   base::Value expected_b("bbb");
   EXPECT_TRUE(expected_b.Equals(map.GetValue(kTestPolicyName1)));
   SetPolicy(&map, kTestPolicyName1, CreateExternalDataFetcher("dummy"));
-  map.AddError(kTestPolicyName1, kTestError);
+  map.AddError(kTestPolicyName1, IDS_POLICY_STORE_STATUS_VALIDATION_ERROR,
+               {base::UTF8ToUTF16(kTestError)});
   EXPECT_FALSE(map.GetValue(kTestPolicyName1));
   const PolicyMap::Entry* entry = map.Get(kTestPolicyName1);
   ASSERT_TRUE(entry != nullptr);
   EXPECT_EQ(POLICY_LEVEL_MANDATORY, entry->level);
   EXPECT_EQ(POLICY_SCOPE_USER, entry->scope);
   EXPECT_EQ(POLICY_SOURCE_CLOUD, entry->source);
+  std::string error_string = base::StrCat({"Validation error: ", kTestError});
   PolicyMap::Entry::L10nLookupFunction lookup = base::BindRepeating(
       static_cast<base::string16 (*)(int)>(&base::NumberToString16));
-  EXPECT_EQ(base::UTF8ToUTF16(kTestError), entry->GetLocalizedErrors(lookup));
+  EXPECT_EQ(base::UTF8ToUTF16(error_string), entry->GetLocalizedErrors(lookup));
   EXPECT_TRUE(
       ExternalDataFetcher::Equals(entry->external_data_fetcher.get(),
                                   CreateExternalDataFetcher("dummy").get()));
@@ -118,9 +121,6 @@
   map.AddError(kTestPolicyName1, 5678);
   EXPECT_EQ(base::UTF8ToUTF16("1234\n5678"),
             entry1->GetLocalizedErrors(lookup));
-  map.AddError(kTestPolicyName1, "abcd");
-  EXPECT_EQ(base::UTF8ToUTF16("abcd\n1234\n5678"),
-            entry1->GetLocalizedErrors(lookup));
 
   // Add second entry to make sure errors are added individually.
   SetPolicy(&map, kTestPolicyName2, base::Value(0));
@@ -128,13 +128,13 @@
   // Test AddError with placeholder replacement (one arg)
   map.AddError(kTestPolicyName2, IDS_POLICY_MIGRATED_OLD_POLICY,
                {base::UTF8ToUTF16("SomeNewPolicy")});
-  EXPECT_EQ(base::UTF8ToUTF16("abcd\n1234\n5678"),
+  EXPECT_EQ(base::UTF8ToUTF16("1234\n5678"),
             entry1->GetLocalizedErrors(lookup));
   EXPECT_EQ(base::UTF8ToUTF16("This policy is deprecated. You should use the "
                               "SomeNewPolicy policy instead."),
             entry2->GetLocalizedErrors(lookup));
   map.AddError(kTestPolicyName2, 1357);
-  EXPECT_EQ(base::UTF8ToUTF16("abcd\n1234\n5678"),
+  EXPECT_EQ(base::UTF8ToUTF16("1234\n5678"),
             entry1->GetLocalizedErrors(lookup));
   EXPECT_EQ(base::UTF8ToUTF16("1357\nThis policy is deprecated. You should use "
                               "the SomeNewPolicy policy instead."),
@@ -143,10 +143,9 @@
   map.AddError(
       kTestPolicyName1, IDS_POLICY_DLP_CLIPBOARD_BLOCKED_ON_COPY_VM,
       {base::UTF8ToUTF16("SomeSource"), base::UTF8ToUTF16("SomeDestination")});
-  EXPECT_EQ(
-      base::UTF8ToUTF16("abcd\n1234\n5678\nYour administrator has blocked "
-                        "sharing from SomeSource to SomeDestination"),
-      entry1->GetLocalizedErrors(lookup));
+  EXPECT_EQ(base::UTF8ToUTF16("1234\n5678\nYour administrator has blocked "
+                              "sharing from SomeSource to SomeDestination"),
+            entry1->GetLocalizedErrors(lookup));
   EXPECT_EQ(base::UTF8ToUTF16("1357\nThis policy is deprecated. You should use "
                               "the SomeNewPolicy policy instead."),
             entry2->GetLocalizedErrors(lookup));
diff --git a/components/policy/core/common/policy_migrator.cc b/components/policy/core/common/policy_migrator.cc
index d4724fa..eb3dcf6 100644
--- a/components/policy/core/common/policy_migrator.cc
+++ b/components/policy/core/common/policy_migrator.cc
@@ -33,17 +33,16 @@
               << "' has been copied to '" << migration.new_name << "'.";
       auto new_entry = entry->DeepCopy();
       migration.transform.Run(new_entry.value());
-      new_entry.AddError(
-          l10n_util::GetStringFUTF8(IDS_POLICY_MIGRATED_NEW_POLICY,
-                                    base::UTF8ToUTF16(migration.old_name)));
+      new_entry.AddError(IDS_POLICY_MIGRATED_NEW_POLICY,
+                         {base::UTF8ToUTF16(migration.old_name)});
       dest->Set(migration.new_name, std::move(new_entry));
     } else {
       VLOG(3) << "Legacy policy '" << migration.old_name
               << "' is ignored because '" << migration.new_name
               << "' is also set. ";
     }
-    entry->AddError(l10n_util::GetStringFUTF8(
-        IDS_POLICY_MIGRATED_OLD_POLICY, base::UTF8ToUTF16(migration.new_name)));
+    entry->AddError(IDS_POLICY_MIGRATED_OLD_POLICY,
+                    {base::UTF8ToUTF16(migration.new_name)});
   } else {
     VLOG(3) << "Legacy policy '" << migration.old_name << "' is not set.";
   }
diff --git a/components/policy/core/common/policy_proto_decoders.cc b/components/policy/core/common/policy_proto_decoders.cc
index 1657efb2..b648d75 100644
--- a/components/policy/core/common/policy_proto_decoders.cc
+++ b/components/policy/core/common/policy_proto_decoders.cc
@@ -10,11 +10,13 @@
 #include "base/json/json_reader.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "components/policy/core/common/cloud/cloud_external_data_manager.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/policy_constants.h"
 #include "components/policy/proto/cloud_policy.pb.h"
+#include "components/strings/grit/components_strings.h"
 
 namespace policy {
 
@@ -140,7 +142,8 @@
     map->Set(access->policy_key, level, scope, source,
              DecodeIntegerProto(proto, &error), nullptr);
     if (!error.empty())
-      map->AddError(access->policy_key, error);
+      map->AddError(access->policy_key, IDS_POLICY_PROTO_PARSING_ERROR,
+                    {base::UTF8ToUTF16(error)});
   }
 
   for (const StringPolicyAccess* access = &kStringPolicyAccess[0];
@@ -166,7 +169,8 @@
     map->Set(access->policy_key, level, scope, source, std::move(value),
              std::move(external_data_fetcher));
     if (!error.empty())
-      map->AddError(access->policy_key, error);
+      map->AddError(access->policy_key, IDS_POLICY_PROTO_PARSING_ERROR,
+                    {base::UTF8ToUTF16(error)});
   }
 
   for (const StringListPolicyAccess* access = &kStringListPolicyAccess[0];
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 91aba63a..db2b0656 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -24116,7 +24116,54 @@
       If this policy is set at platform level, Sync should be disabled at platform level.
       If this policy is set at user level, Sync should be disabled for that user in order for this policy to take effect.
       '''
-    }
+    },
+    {
+      'name': 'ProfilePickerOnStartupAvailability',
+      'owners': ['file://components/policy/resources/OWNERS'],
+      'type': 'int-enum',
+      'schema': {
+        'type': 'integer',
+        'enum': [ 0, 1, 2 ],
+      },
+      'items': [
+        {
+          'name': 'Enabled',
+          'value': 0,
+          'caption': '''Profile picker available at startup''',
+        },
+        {
+          'name': 'Disabled',
+          'value': 1,
+          'caption': '''Profile picker disabled at startup''',
+        },
+        {
+          'name': 'Forced',
+          'value': 2,
+          'caption': '''Profile picker forced at startup''',
+        },
+      ],
+      'supported_on': [
+        'chrome.*:89-',
+      ],
+      'features': {
+        'dynamic_refresh': False,
+        'per_profile': False,
+      },
+      'default': 0,
+      'example_value': 0,
+      'id': 810,
+      'caption': '''Profile picker availabily on startup''',
+      'tags': [],
+      'desc': '''Specifies whether the profile picker is enabled, disabled or forced at the browser startup.
+
+      By default the profile picker is not shown if the browser starts in guest or incognito mode, a profile directory and/or urls are specified by command line, an app is explicitly requested to open, the browser was launched by a native notification, there is only one profile available or the policy ForceBrowserSignin is set to true.
+
+      If 'Enabled' (0) is selected or the policy is left unset, the profile picker will be shown at startup by default, but users will be able to enable/disable it.
+
+      If 'Disabled' (1) is selected, the profile picker will never be shown, and users will not be able to change the setting.
+
+      If 'Forced' (2) is selected, the profile picker cannot be suppressed by the user. The profile picker will be shown even if there is only one profile available.''',
+    },
   ],
 
   'messages': {
@@ -25039,6 +25086,6 @@
   'placeholders': [],
   'deleted_policy_ids': [114, 115, 204, 205, 206, 412, 476, 544, 546, 562, 569, 578, 583, 585, 586, 587, 588, 589, 590, 591, 600, 668, 669],
   'deleted_atomic_policy_group_ids': [19],
-  'highest_id_currently_used': 809,
+  'highest_id_currently_used': 810,
   'highest_atomic_group_id_currently_used': 40
 }
diff --git a/components/policy_strings.grdp b/components/policy_strings.grdp
index 6e2fa5e..b3f68b5 100644
--- a/components/policy_strings.grdp
+++ b/components/policy_strings.grdp
@@ -274,6 +274,9 @@
   <message name="IDS_POLICY_UNKNOWN" desc="Text displayed in the status column when a policy name is not recognized.">
     Unknown policy.
   </message>
+  <message name="IDS_POLICY_PROTO_PARSING_ERROR" desc="Text displayed when error is encountered during policy parsing/decoding.">
+    Policy parsing error: <ph name="ERROR">$1<ex>Invalid JSON string: Line: 1, column: 14</ex></ph>
+  </message>
 
   <!-- chrome://policy -->
   <message name="IDS_POLICY_TITLE" desc="Page title and the title of the section that lists policies.">
diff --git a/components/policy_strings_grdp/IDS_POLICY_PROTO_PARSING_ERROR.png.sha1 b/components/policy_strings_grdp/IDS_POLICY_PROTO_PARSING_ERROR.png.sha1
new file mode 100644
index 0000000..5f77eef
--- /dev/null
+++ b/components/policy_strings_grdp/IDS_POLICY_PROTO_PARSING_ERROR.png.sha1
@@ -0,0 +1 @@
+1955b17099ef4b9e48c1ce020c548614d64087c0
\ No newline at end of file
diff --git a/components/security_interstitials/core/common/resources/interstitial_common.css b/components/security_interstitials/core/common/resources/interstitial_common.css
index f9484658f..6873dd3 100644
--- a/components/security_interstitials/core/common/resources/interstitial_common.css
+++ b/components/security_interstitials/core/common/resources/interstitial_common.css
@@ -387,7 +387,6 @@
 
   #details.hidden,
   #main-content.hidden {
-    display: block;
     height: 0;
     opacity: 0;
     overflow: hidden;
diff --git a/components/security_interstitials_strings.grdp b/components/security_interstitials_strings.grdp
index 60915b88..a9a8054 100644
--- a/components/security_interstitials_strings.grdp
+++ b/components/security_interstitials_strings.grdp
@@ -524,7 +524,7 @@
     The information you’re about to submit is not secure
   </message>
   <message name="IDS_INSECURE_FORM_PRIMARY_PARAGRAPH" desc="Main paragraph of the insecure form submission warning. This warning is shown when a form shown on a secure website is submitted over an insecure connection.">
-    Because the site is using a connection that’s not completely secure, your information will be visible to others.
+    Because this form is being submitted using a connection that’s not secure, your information will be visible to others.
   </message>
   <message name="IDS_INSECURE_FORM_BACK_BUTTON" desc="Text for the button in the insecure form warning that takes the user back to the previous page">
     Go back
diff --git a/components/security_interstitials_strings_grdp/IDS_INSECURE_FORM_PRIMARY_PARAGRAPH.png.sha1 b/components/security_interstitials_strings_grdp/IDS_INSECURE_FORM_PRIMARY_PARAGRAPH.png.sha1
index 786c2acb..4cfda387 100644
--- a/components/security_interstitials_strings_grdp/IDS_INSECURE_FORM_PRIMARY_PARAGRAPH.png.sha1
+++ b/components/security_interstitials_strings_grdp/IDS_INSECURE_FORM_PRIMARY_PARAGRAPH.png.sha1
@@ -1 +1 @@
-57e519dbb05ed2d3541523dd174ea7d469dda94e
\ No newline at end of file
+d0989aa82d7d33093860aff7e2b0c4b6bbaea615
\ No newline at end of file
diff --git a/components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom b/components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom
index 6cc9764e..2c2d1f3 100644
--- a/components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom
+++ b/components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom
@@ -111,7 +111,7 @@
   BitmapForSeparatedFrame(mojo_base.mojom.UnguessableToken frame_guid,
                           gfx.mojom.Rect clip_rect,
                           float scale_factor) =>
-    (BitmapStatus status, skia.mojom.BitmapWithArbitraryBpp? bitmap);
+    (BitmapStatus status, skia.mojom.BitmapN32? bitmap);
 
   // Starts the compositing process for |BitmapForMainFrame| calls using frame
   // data |request|. |status| will be negative on failure. If the root frame is
@@ -127,7 +127,7 @@
   // |status|.
   // |BeginMainFrameComposite| must be called before this method.
   BitmapForMainFrame(gfx.mojom.Rect clip_rect, float scale_factor) =>
-    (BitmapStatus status, skia.mojom.BitmapWithArbitraryBpp? bitmap);
+    (BitmapStatus status, skia.mojom.BitmapN32? bitmap);
 
   // Sets the root frame of the compositor. Used for tracing and diagnostics.
   SetRootFrameUrl(url.mojom.Url url);
diff --git a/components/sync/base/model_type.h b/components/sync/base/model_type.h
index 72764d39f..4cb4ac3 100644
--- a/components/sync/base/model_type.h
+++ b/components/sync/base/model_type.h
@@ -110,9 +110,9 @@
   SUPERVISED_USER_SETTINGS,
   // App List items, used by the ChromeOS app launcher.
   APP_LIST,
+  // TODO(crbug.com/1155257): Remove the deprecated type, because it isn't used.
   // Supervised user allowlists. Each item contains a CRX ID (like an extension
   // ID) and a name.
-  // TODO(crbug.com/1155257): Remove the deprecated type.
   DEPRECATED_SUPERVISED_USER_ALLOWLISTS,
   // ARC package items, i.e. Android apps on ChromeOS.
   ARC_PACKAGE,
diff --git a/components/sync/protocol/managed_user_whitelist_specifics.proto b/components/sync/protocol/managed_user_whitelist_specifics.proto
index c9938bd..7266ff4 100644
--- a/components/sync/protocol/managed_user_whitelist_specifics.proto
+++ b/components/sync/protocol/managed_user_whitelist_specifics.proto
@@ -16,6 +16,7 @@
 
 package sync_pb;
 
+// TODO(crbug.com/1149788): Deprecate.
 // Properties of supervised user whitelist sync objects.
 // The fields here are a subset of the fields in an ExtensionSpecifics.
 message ManagedUserWhitelistSpecifics {
diff --git a/components/translate/core/browser/translate_manager.cc b/components/translate/core/browser/translate_manager.cc
index 7e0fe14..504c8373 100644
--- a/components/translate/core/browser/translate_manager.cc
+++ b/components/translate/core/browser/translate_manager.cc
@@ -971,7 +971,7 @@
     const std::string& page_language_code) {
   TranslateAcceptLanguages* accept_languages =
       translate_client_->GetTranslateAcceptLanguages();
-  // Don't translate any user black-listed languages.
+  // Don't translate any user blocklisted languages.
   if (!translate_prefs->CanTranslateLanguage(accept_languages,
                                              page_language_code)) {
     decision->SetIsInLanguageBlocklist();
@@ -1006,9 +1006,9 @@
         TriggerDecision::kDisabledNeverTranslateLanguage);
   }
 
-  // Don't translate any user black-listed URLs.
+  // Don't translate any user blocklisted URLs.
   const GURL& page_url = translate_driver_->GetVisibleURL();
-  if (translate_prefs->IsSiteBlacklisted(page_url.HostNoBrackets())) {
+  if (translate_prefs->IsSiteOnNeverPromptList(page_url.HostNoBrackets())) {
     decision->SetIsInSiteBlocklist();
 
     decision->PreventAutoTranslate();
diff --git a/components/translate/core/browser/translate_manager_unittest.cc b/components/translate/core/browser/translate_manager_unittest.cc
index 133c1b67..e4ed1d8 100644
--- a/components/translate/core/browser/translate_manager_unittest.cc
+++ b/components/translate/core/browser/translate_manager_unittest.cc
@@ -1143,7 +1143,7 @@
       .WillByDefault(Return(true));
 
   translate_prefs_.BlockLanguage("de");
-  translate_prefs_.WhitelistLanguagePair("fr", "de");
+  translate_prefs_.AddLanguagePairToAlwaysTranslateList("fr", "de");
 
   ASSERT_TRUE(translate_prefs_.IsBlockedLanguage("de"));
   ASSERT_FALSE(translate_prefs_.CanTranslateLanguage(&accept_languages, "de"));
diff --git a/components/translate/core/browser/translate_prefs.cc b/components/translate/core/browser/translate_prefs.cc
index 63cf2e3..9ba87916 100644
--- a/components/translate/core/browser/translate_prefs.cc
+++ b/components/translate/core/browser/translate_prefs.cc
@@ -83,11 +83,11 @@
 
 const char kForceTriggerTranslateCount[] =
     "translate_force_trigger_on_english_count_for_backoff_1";
-const char TranslatePrefs::kPrefTranslateSiteBlacklistDeprecated[] =
+const char TranslatePrefs::kPrefNeverPromptSitesDeprecated[] =
     "translate_site_blacklist";
-const char TranslatePrefs::kPrefTranslateSiteBlacklistWithTime[] =
+const char TranslatePrefs::kPrefNeverPromptSitesWithTime[] =
     "translate_site_blacklist_with_time";
-const char TranslatePrefs::kPrefTranslateWhitelists[] = "translate_whitelists";
+const char TranslatePrefs::kPrefAlwaysTranslateLists[] = "translate_whitelists";
 const char TranslatePrefs::kPrefTranslateDeniedCount[] =
     "translate_denied_count_for_language";
 const char TranslatePrefs::kPrefTranslateIgnoredCount[] =
@@ -201,7 +201,7 @@
 #else
   DCHECK(!preferred_languages_pref);
 #endif
-  MigrateSitesBlacklist();
+  MigrateNeverPromptSites();
   ResetEmptyBlockedLanguagesToDefaults();
 }
 
@@ -230,8 +230,8 @@
 
 void TranslatePrefs::ResetToDefaults() {
   ResetBlockedLanguagesToDefault();
-  ClearBlacklistedSites();
-  ClearWhitelistedLanguagePairs();
+  ClearNeverPromptSiteList();
+  ClearAlwaysTranslateLanguagePairs();
   prefs_->ClearPref(kPrefTranslateDeniedCount);
   prefs_->ClearPref(kPrefTranslateIgnoredCount);
   prefs_->ClearPref(kPrefTranslateAcceptedCount);
@@ -474,32 +474,31 @@
   language_prefs_->ClearFluent(input_language);
 }
 
-bool TranslatePrefs::IsSiteBlacklisted(base::StringPiece site) const {
-  return prefs_->GetDictionary(kPrefTranslateSiteBlacklistWithTime)
-      ->FindKey(site);
+bool TranslatePrefs::IsSiteOnNeverPromptList(base::StringPiece site) const {
+  return prefs_->GetDictionary(kPrefNeverPromptSitesWithTime)->FindKey(site);
 }
 
-void TranslatePrefs::BlacklistSite(base::StringPiece site) {
+void TranslatePrefs::AddSiteToNeverPromptList(base::StringPiece site) {
   DCHECK(!site.empty());
-  BlacklistValue(kPrefTranslateSiteBlacklistDeprecated, site);
-  DictionaryPrefUpdate update(prefs_, kPrefTranslateSiteBlacklistWithTime);
+  AddValueToNeverPromptList(kPrefNeverPromptSitesDeprecated, site);
+  DictionaryPrefUpdate update(prefs_, kPrefNeverPromptSitesWithTime);
   base::DictionaryValue* dict = update.Get();
   dict->SetKey(site, util::TimeToValue(base::Time::Now()));
 }
 
-void TranslatePrefs::RemoveSiteFromBlacklist(base::StringPiece site) {
+void TranslatePrefs::RemoveSiteFromNeverPromptList(base::StringPiece site) {
   DCHECK(!site.empty());
-  RemoveValueFromBlacklist(kPrefTranslateSiteBlacklistDeprecated, site);
-  DictionaryPrefUpdate update(prefs_, kPrefTranslateSiteBlacklistWithTime);
+  RemoveValueFromNeverPromptList(kPrefNeverPromptSitesDeprecated, site);
+  DictionaryPrefUpdate update(prefs_, kPrefNeverPromptSitesWithTime);
   base::DictionaryValue* dict = update.Get();
   dict->RemoveKey(site);
 }
 
-std::vector<std::string> TranslatePrefs::GetBlacklistedSitesBetween(
+std::vector<std::string> TranslatePrefs::GetNeverPromptSitesBetween(
     base::Time begin,
     base::Time end) const {
   std::vector<std::string> result;
-  auto* dict = prefs_->GetDictionary(kPrefTranslateSiteBlacklistWithTime);
+  auto* dict = prefs_->GetDictionary(kPrefNeverPromptSitesWithTime);
   for (const auto& entry : dict->DictItems()) {
     base::Optional<base::Time> time = util::ValueToTime(entry.second);
     if (!time) {
@@ -512,17 +511,17 @@
   return result;
 }
 
-void TranslatePrefs::DeleteBlacklistedSitesBetween(base::Time begin,
+void TranslatePrefs::DeleteNeverPromptSitesBetween(base::Time begin,
                                                    base::Time end) {
-  for (auto& site : GetBlacklistedSitesBetween(begin, end))
-    RemoveSiteFromBlacklist(site);
+  for (auto& site : GetNeverPromptSitesBetween(begin, end))
+    RemoveSiteFromNeverPromptList(site);
 }
 
-bool TranslatePrefs::IsLanguagePairWhitelisted(
+bool TranslatePrefs::IsLanguagePairOnAlwaysTranslateList(
     base::StringPiece original_language,
     base::StringPiece target_language) {
   const base::DictionaryValue* dict =
-      prefs_->GetDictionary(kPrefTranslateWhitelists);
+      prefs_->GetDictionary(kPrefAlwaysTranslateLists);
   if (dict) {
     const std::string* auto_target_lang =
         dict->FindStringKey(original_language);
@@ -532,9 +531,10 @@
   return false;
 }
 
-void TranslatePrefs::WhitelistLanguagePair(base::StringPiece original_language,
-                                           base::StringPiece target_language) {
-  DictionaryPrefUpdate update(prefs_, kPrefTranslateWhitelists);
+void TranslatePrefs::AddLanguagePairToAlwaysTranslateList(
+    base::StringPiece original_language,
+    base::StringPiece target_language) {
+  DictionaryPrefUpdate update(prefs_, kPrefAlwaysTranslateLists);
   base::DictionaryValue* dict = update.Get();
   if (!dict) {
     NOTREACHED() << "Unregistered translate whitelist pref";
@@ -543,10 +543,10 @@
   dict->SetStringKey(original_language, target_language);
 }
 
-void TranslatePrefs::RemoveLanguagePairFromWhitelist(
+void TranslatePrefs::RemoveLanguagePairFromAlwaysTranslateList(
     base::StringPiece original_language,
     base::StringPiece target_language) {
-  DictionaryPrefUpdate update(prefs_, kPrefTranslateWhitelists);
+  DictionaryPrefUpdate update(prefs_, kPrefAlwaysTranslateLists);
   base::DictionaryValue* dict = update.Get();
   if (!dict) {
     NOTREACHED() << "Unregistered translate whitelist pref";
@@ -559,17 +559,17 @@
   language_prefs_->ResetFluentLanguagesToDefaults();
 }
 
-void TranslatePrefs::ClearBlacklistedSites() {
-  prefs_->ClearPref(kPrefTranslateSiteBlacklistDeprecated);
-  prefs_->ClearPref(kPrefTranslateSiteBlacklistWithTime);
+void TranslatePrefs::ClearNeverPromptSiteList() {
+  prefs_->ClearPref(kPrefNeverPromptSitesDeprecated);
+  prefs_->ClearPref(kPrefNeverPromptSitesWithTime);
 }
 
-bool TranslatePrefs::HasWhitelistedLanguagePairs() const {
-  return !IsDictionaryEmpty(kPrefTranslateWhitelists);
+bool TranslatePrefs::HasLanguagePairsToAlwaysTranslate() const {
+  return !IsDictionaryEmpty(kPrefAlwaysTranslateLists);
 }
 
-void TranslatePrefs::ClearWhitelistedLanguagePairs() {
-  prefs_->ClearPref(kPrefTranslateWhitelists);
+void TranslatePrefs::ClearAlwaysTranslateLanguagePairs() {
+  prefs_->ClearPref(kPrefAlwaysTranslateLists);
 }
 
 int TranslatePrefs::GetTranslationDeniedCount(
@@ -762,7 +762,7 @@
 bool TranslatePrefs::CanTranslateLanguage(
     TranslateAcceptLanguages* accept_languages,
     base::StringPiece language) {
-  // Don't translate any user black-listed languages.
+  // Don't translate any user blocklisted languages.
   if (!IsBlockedLanguage(language))
     return true;
 
@@ -789,7 +789,7 @@
 bool TranslatePrefs::ShouldAutoTranslate(base::StringPiece original_language,
                                          std::string* target_language) {
   const base::DictionaryValue* dict =
-      prefs_->GetDictionary(kPrefTranslateWhitelists);
+      prefs_->GetDictionary(kPrefAlwaysTranslateLists);
   if (dict && dict->GetString(original_language, target_language)) {
     DCHECK(!target_language->empty());
     return !target_language->empty();
@@ -829,13 +829,13 @@
 // static
 void TranslatePrefs::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterListPref(kPrefTranslateSiteBlacklistDeprecated,
+  registry->RegisterListPref(kPrefNeverPromptSitesDeprecated,
                              user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
   registry->RegisterDictionaryPref(
-      kPrefTranslateSiteBlacklistWithTime,
+      kPrefNeverPromptSitesWithTime,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
   registry->RegisterDictionaryPref(
-      kPrefTranslateWhitelists,
+      kPrefAlwaysTranslateLists,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
   registry->RegisterDictionaryPref(
       kPrefTranslateDeniedCount,
@@ -872,22 +872,22 @@
 #endif
 }
 
-void TranslatePrefs::MigrateSitesBlacklist() {
+void TranslatePrefs::MigrateNeverPromptSites() {
   // Migration should only be necessary once but there could still be old
   // Chrome instances that sync the old preference, so do it once per
   // startup.
   static bool migrated = false;
   if (migrated)
     return;
-  DictionaryPrefUpdate blacklist_update(prefs_,
-                                        kPrefTranslateSiteBlacklistWithTime);
-  base::DictionaryValue* blacklist = blacklist_update.Get();
-  if (blacklist) {
+  DictionaryPrefUpdate never_prompt_list_update(prefs_,
+                                                kPrefNeverPromptSitesWithTime);
+  base::DictionaryValue* never_prompt_list = never_prompt_list_update.Get();
+  if (never_prompt_list) {
     const base::ListValue* list =
-        prefs_->GetList(kPrefTranslateSiteBlacklistDeprecated);
+        prefs_->GetList(kPrefNeverPromptSitesDeprecated);
     for (auto& site : *list) {
-      if (!blacklist->HasKey(site.GetString())) {
-        blacklist->SetKey(site.GetString(), base::Value(0));
+      if (!never_prompt_list->HasKey(site.GetString())) {
+        never_prompt_list->SetKey(site.GetString(), base::Value(0));
       }
     }
   }
@@ -898,44 +898,44 @@
   language_prefs_->ResetEmptyFluentLanguagesToDefault();
 }
 
-bool TranslatePrefs::IsValueBlacklisted(const char* pref_id,
-                                        base::StringPiece value) const {
-  const base::ListValue* blacklist = prefs_->GetList(pref_id);
-  if (!blacklist)
+bool TranslatePrefs::IsValueOnNeverPromptList(const char* pref_id,
+                                              base::StringPiece value) const {
+  const base::ListValue* never_prompt_list = prefs_->GetList(pref_id);
+  if (!never_prompt_list)
     return false;
-  for (const base::Value& value_in_list : blacklist->GetList()) {
+  for (const base::Value& value_in_list : never_prompt_list->GetList()) {
     if (value_in_list.is_string() && value_in_list.GetString() == value)
       return true;
   }
   return false;
 }
 
-void TranslatePrefs::BlacklistValue(const char* pref_id,
-                                    base::StringPiece value) {
+void TranslatePrefs::AddValueToNeverPromptList(const char* pref_id,
+                                               base::StringPiece value) {
   ListPrefUpdate update(prefs_, pref_id);
-  base::ListValue* blacklist = update.Get();
-  if (!blacklist) {
-    NOTREACHED() << "Unregistered translate blacklist pref";
+  base::ListValue* never_prompt_list = update.Get();
+  if (!never_prompt_list) {
+    NOTREACHED() << "Unregistered never-translate pref";
     return;
   }
 
-  if (IsValueBlacklisted(pref_id, value)) {
+  if (IsValueOnNeverPromptList(pref_id, value)) {
     return;
   }
-  blacklist->AppendString(value);
+  never_prompt_list->AppendString(value);
 }
 
-void TranslatePrefs::RemoveValueFromBlacklist(const char* pref_id,
-                                              base::StringPiece value) {
+void TranslatePrefs::RemoveValueFromNeverPromptList(const char* pref_id,
+                                                    base::StringPiece value) {
   ListPrefUpdate update(prefs_, pref_id);
-  base::ListValue* blacklist = update.Get();
-  if (!blacklist) {
-    NOTREACHED() << "Unregistered translate blacklist pref";
+  base::ListValue* never_prompt_list = update.Get();
+  if (!never_prompt_list) {
+    NOTREACHED() << "Unregistered never-translate pref";
     return;
   }
 
-  auto list_view = blacklist->GetList();
-  blacklist->EraseListIter(std::find_if(
+  auto list_view = never_prompt_list->GetList();
+  never_prompt_list->EraseListIter(std::find_if(
       list_view.begin(), list_view.end(),
       [value](const base::Value& value_in_list) {
         return value_in_list.is_string() && value_in_list.GetString() == value;
@@ -943,8 +943,8 @@
 }
 
 size_t TranslatePrefs::GetListSize(const char* pref_id) const {
-  const base::ListValue* blacklist = prefs_->GetList(pref_id);
-  return blacklist == nullptr ? 0 : blacklist->GetList().size();
+  const base::ListValue* never_prompt_list = prefs_->GetList(pref_id);
+  return never_prompt_list == nullptr ? 0 : never_prompt_list->GetList().size();
 }
 
 bool TranslatePrefs::IsDictionaryEmpty(const char* pref_id) const {
diff --git a/components/translate/core/browser/translate_prefs.h b/components/translate/core/browser/translate_prefs.h
index a17058e..5061b63 100644
--- a/components/translate/core/browser/translate_prefs.h
+++ b/components/translate/core/browser/translate_prefs.h
@@ -128,11 +128,11 @@
  public:
   static const char kPrefLanguageProfile[];
   static const char kPrefForceTriggerTranslateCount[];
-  // TODO(crbug.com/524927): Remove kPrefTranslateSiteBlacklist after
+  // TODO(crbug.com/524927): Remove kPrefNeverPromptSites after
   // 3 milestones (M74).
-  static const char kPrefTranslateSiteBlacklistDeprecated[];
-  static const char kPrefTranslateSiteBlacklistWithTime[];
-  static const char kPrefTranslateWhitelists[];
+  static const char kPrefNeverPromptSitesDeprecated[];
+  static const char kPrefNeverPromptSitesWithTime[];
+  static const char kPrefAlwaysTranslateLists[];
   static const char kPrefTranslateDeniedCount[];
   static const char kPrefTranslateIgnoredCount[];
   static const char kPrefTranslateAcceptedCount[];
@@ -179,8 +179,9 @@
   void SetCountry(const std::string& country);
   std::string GetCountry() const;
 
-  // Resets the blocked languages list, the sites blacklist, the languages
-  // whitelist, the accepted/denied counts, and whether Translate is enabled.
+  // Resets the blocked languages list, the never-translate site list, the
+  // always-translate languages list, the accepted/denied counts, and whether
+  // Translate is enabled.
   void ResetToDefaults();
 
   bool IsBlockedLanguage(base::StringPiece original_language) const;
@@ -226,25 +227,26 @@
       bool translate_allowed,
       std::vector<TranslateLanguageInfo>* languages);
 
-  bool IsSiteBlacklisted(base::StringPiece site) const;
-  void BlacklistSite(base::StringPiece site);
-  void RemoveSiteFromBlacklist(base::StringPiece site);
+  bool IsSiteOnNeverPromptList(base::StringPiece site) const;
+  void AddSiteToNeverPromptList(base::StringPiece site);
+  void RemoveSiteFromNeverPromptList(base::StringPiece site);
 
-  std::vector<std::string> GetBlacklistedSitesBetween(base::Time begin,
+  std::vector<std::string> GetNeverPromptSitesBetween(base::Time begin,
                                                       base::Time end) const;
-  void DeleteBlacklistedSitesBetween(base::Time begin, base::Time end);
+  void DeleteNeverPromptSitesBetween(base::Time begin, base::Time end);
 
-  bool HasWhitelistedLanguagePairs() const;
+  bool HasLanguagePairsToAlwaysTranslate() const;
 
-  bool IsLanguagePairWhitelisted(base::StringPiece original_language,
-                                 base::StringPiece target_language);
-  void WhitelistLanguagePair(base::StringPiece original_language,
-                             base::StringPiece target_language);
-  void RemoveLanguagePairFromWhitelist(base::StringPiece original_language,
-                                       base::StringPiece target_language);
+  bool IsLanguagePairOnAlwaysTranslateList(base::StringPiece original_language,
+                                           base::StringPiece target_language);
+  void AddLanguagePairToAlwaysTranslateList(base::StringPiece original_language,
+                                            base::StringPiece target_language);
+  void RemoveLanguagePairFromAlwaysTranslateList(
+      base::StringPiece original_language,
+      base::StringPiece target_language);
 
   // These methods are used to track how many times the user has denied the
-  // translation for a specific language. (So we can present a UI to black-list
+  // translation for a specific language. (So we can present a UI to blocklist
   // that language if the user keeps denying translations).
   int GetTranslationDeniedCount(base::StringPiece language) const;
   void IncrementTranslationDeniedCount(base::StringPiece language);
@@ -257,7 +259,7 @@
   void ResetTranslationIgnoredCount(base::StringPiece language);
 
   // These methods are used to track how many times the user has accepted the
-  // translation for a specific language. (So we can present a UI to white-list
+  // translation for a specific language. (So we can present a UI to allowlist
   // that language if the user keeps accepting translations).
   int GetTranslationAcceptedCount(base::StringPiece language) const;
   void IncrementTranslationAcceptedCount(base::StringPiece language);
@@ -322,9 +324,9 @@
   // signals that the backoff should not happen for that user.
   void ReportAcceptedAfterForceTriggerOnEnglishPages();
 
-  // Migrate the sites blacklist from a list to a dictionary that maps sites
-  // to a timestamp of the creation of this entry.
-  void MigrateSitesBlacklist();
+  // Migrate the sites to never translate from a list to a dictionary that maps
+  // sites to a timestamp of the creation of this entry.
+  void MigrateNeverPromptSites();
 
   // Prevent empty blocked languages by resetting them to the default value.
   // (crbug.com/902354)
@@ -355,13 +357,15 @@
   void UpdateLanguageList(const std::vector<std::string>& languages);
 
   void ResetBlockedLanguagesToDefault();
-  void ClearBlacklistedSites();
-  void ClearWhitelistedLanguagePairs();
+  void ClearNeverPromptSiteList();
+  void ClearAlwaysTranslateLanguagePairs();
 
   // |pref_id| is the name of a list pref.
-  bool IsValueBlacklisted(const char* pref_id, base::StringPiece value) const;
-  void BlacklistValue(const char* pref_id, base::StringPiece value);
-  void RemoveValueFromBlacklist(const char* pref_id, base::StringPiece value);
+  bool IsValueOnNeverPromptList(const char* pref_id,
+                                base::StringPiece value) const;
+  void AddValueToNeverPromptList(const char* pref_id, base::StringPiece value);
+  void RemoveValueFromNeverPromptList(const char* pref_id,
+                                      base::StringPiece value);
   size_t GetListSize(const char* pref_id) const;
 
   bool IsDictionaryEmpty(const char* pref_id) const;
diff --git a/components/translate/core/browser/translate_prefs_unittest.cc b/components/translate/core/browser/translate_prefs_unittest.cc
index 421b379..63ea458 100644
--- a/components/translate/core/browser/translate_prefs_unittest.cc
+++ b/components/translate/core/browser/translate_prefs_unittest.cc
@@ -122,9 +122,9 @@
 
   void ExpectBlockedLanguageListContent(
       const std::vector<std::string>& list) const {
-    const base::ListValue* const blacklist =
+    const base::ListValue* const never_prompt_list =
         prefs_.GetList(kTranslateBlockedLanguagesPref);
-    ExpectEqualLanguageLists(*blacklist, list);
+    ExpectEqualLanguageLists(*never_prompt_list, list);
   }
 
   // Returns a vector of language codes from the elements of the given
@@ -922,28 +922,28 @@
   ExpectLanguagePrefs("en,it,es,zh,fr");
 }
 
-TEST_F(TranslatePrefsTest, SiteBlacklist) {
-  translate_prefs_->BlacklistSite("a.com");
+TEST_F(TranslatePrefsTest, SiteNeverPromptList) {
+  translate_prefs_->AddSiteToNeverPromptList("a.com");
   base::Time t = base::Time::Now();
   base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-  translate_prefs_->BlacklistSite("b.com");
-  EXPECT_TRUE(translate_prefs_->IsSiteBlacklisted("a.com"));
-  EXPECT_TRUE(translate_prefs_->IsSiteBlacklisted("b.com"));
+  translate_prefs_->AddSiteToNeverPromptList("b.com");
+  EXPECT_TRUE(translate_prefs_->IsSiteOnNeverPromptList("a.com"));
+  EXPECT_TRUE(translate_prefs_->IsSiteOnNeverPromptList("b.com"));
 
   EXPECT_EQ(std::vector<std::string>({"a.com"}),
-            translate_prefs_->GetBlacklistedSitesBetween(base::Time(), t));
+            translate_prefs_->GetNeverPromptSitesBetween(base::Time(), t));
   EXPECT_EQ(std::vector<std::string>({"a.com", "b.com"}),
-            translate_prefs_->GetBlacklistedSitesBetween(base::Time(),
+            translate_prefs_->GetNeverPromptSitesBetween(base::Time(),
                                                          base::Time::Max()));
 
-  translate_prefs_->DeleteBlacklistedSitesBetween(t, base::Time::Max());
-  EXPECT_TRUE(translate_prefs_->IsSiteBlacklisted("a.com"));
-  EXPECT_FALSE(translate_prefs_->IsSiteBlacklisted("b.com"));
+  translate_prefs_->DeleteNeverPromptSitesBetween(t, base::Time::Max());
+  EXPECT_TRUE(translate_prefs_->IsSiteOnNeverPromptList("a.com"));
+  EXPECT_FALSE(translate_prefs_->IsSiteOnNeverPromptList("b.com"));
 
-  translate_prefs_->DeleteBlacklistedSitesBetween(base::Time(),
+  translate_prefs_->DeleteNeverPromptSitesBetween(base::Time(),
                                                   base::Time::Max());
-  EXPECT_FALSE(translate_prefs_->IsSiteBlacklisted("a.com"));
-  EXPECT_FALSE(translate_prefs_->IsSiteBlacklisted("b.com"));
+  EXPECT_FALSE(translate_prefs_->IsSiteOnNeverPromptList("a.com"));
+  EXPECT_FALSE(translate_prefs_->IsSiteOnNeverPromptList("b.com"));
 }
 
 TEST_F(TranslatePrefsTest, DefaultBlockedLanguages) {
diff --git a/components/translate/core/browser/translate_trigger_decision.h b/components/translate/core/browser/translate_trigger_decision.h
index 6bca27a..de38fd1b 100644
--- a/components/translate/core/browser/translate_trigger_decision.h
+++ b/components/translate/core/browser/translate_trigger_decision.h
@@ -48,7 +48,7 @@
   // Returns true iff:
   // 1. Showing the UI is disallowed (otherwise it would be chosen over showing
   //    the UI).
-  // 2. It's possible to show the UI (language/site not blacklisted, connected
+  // 2. It's possible to show the UI (language/site not blocklisted, connected
   //    to the internet, etc)
   // 3. Ranker isn't requesting that the UI be suppressed.
   bool ShouldShowUI() const;
@@ -62,7 +62,7 @@
 
  private:
   // These fields are private because they should only be set one way. Filters
-  // "blacklist" outcomes, so for example once |can_show_ui_| is set to false,
+  // "blocklist" outcomes, so for example once |can_show_ui_| is set to false,
   // it shouldn't be reset to true.
   bool can_auto_translate_ = true;
   bool can_show_ui_ = true;
diff --git a/components/translate/core/browser/translate_ui_delegate.cc b/components/translate/core/browser/translate_ui_delegate.cc
index f9af5e0..b1dac8c 100644
--- a/components/translate/core/browser/translate_ui_delegate.cc
+++ b/components/translate/core/browser/translate_ui_delegate.cc
@@ -314,9 +314,10 @@
   UMA_HISTOGRAM_BOOLEAN(kNeverTranslateLang, value);
 }
 
+// TODO(crbug.com/1028966): Update this name to use inclusive language.
 bool TranslateUIDelegate::IsSiteBlacklisted() const {
   std::string host = GetPageHost();
-  return !host.empty() && prefs_->IsSiteBlacklisted(host);
+  return !host.empty() && prefs_->IsSiteOnNeverPromptList(host);
 }
 
 bool TranslateUIDelegate::CanBlacklistSite() const {
@@ -329,7 +330,7 @@
     return;
 
   if (value) {
-    prefs_->BlacklistSite(host);
+    prefs_->AddSiteToNeverPromptList(host);
     if (translate_manager_) {
       // Translation has been blocked for this site. Capture that in the metrics
       // Note that we don't capture a language being unblocked... which is not
@@ -338,15 +339,15 @@
           metrics::TranslateEventProto::USER_NEVER_TRANSLATE_SITE);
     }
   } else {
-    prefs_->RemoveSiteFromBlacklist(host);
+    prefs_->RemoveSiteFromNeverPromptList(host);
   }
 
   UMA_HISTOGRAM_BOOLEAN(kNeverTranslateSite, value);
 }
 
 bool TranslateUIDelegate::ShouldAlwaysTranslate() const {
-  return prefs_->IsLanguagePairWhitelisted(GetOriginalLanguageCode(),
-                                           GetTargetLanguageCode());
+  return prefs_->IsLanguagePairOnAlwaysTranslateList(GetOriginalLanguageCode(),
+                                                     GetTargetLanguageCode());
 }
 
 bool TranslateUIDelegate::ShouldAlwaysTranslateBeCheckedByDefault() const {
@@ -369,7 +370,7 @@
   const std::string& original_lang = GetOriginalLanguageCode();
   const std::string& target_lang = GetTargetLanguageCode();
   if (value) {
-    prefs_->WhitelistLanguagePair(original_lang, target_lang);
+    prefs_->AddLanguagePairToAlwaysTranslateList(original_lang, target_lang);
     // A default translation mapping has been accepted for this language.
     // Capture that in the metrics. Note that we don't capture a language being
     // unmapped... which is not the same as accepting some other translation
@@ -379,7 +380,8 @@
           metrics::TranslateEventProto::USER_ALWAYS_TRANSLATE_LANGUAGE);
     }
   } else {
-    prefs_->RemoveLanguagePairFromWhitelist(original_lang, target_lang);
+    prefs_->RemoveLanguagePairFromAlwaysTranslateList(original_lang,
+                                                      target_lang);
   }
 
   UMA_HISTOGRAM_BOOLEAN(kAlwaysTranslateLang, value);
diff --git a/components/translate/translate_internals/translate_internals_handler.cc b/components/translate/translate_internals/translate_internals_handler.cc
index df9f009..fb81a50 100644
--- a/components/translate/translate_internals/translate_internals_handler.cc
+++ b/components/translate/translate_internals/translate_internals_handler.cc
@@ -178,14 +178,14 @@
     std::string site;
     if (!args->GetString(1, &site))
       return;
-    translate_prefs->RemoveSiteFromBlacklist(site);
+    translate_prefs->RemoveSiteFromNeverPromptList(site);
   } else if (pref_name == "whitelists") {
     std::string from, to;
     if (!args->GetString(1, &from))
       return;
     if (!args->GetString(2, &to))
       return;
-    translate_prefs->RemoveLanguagePairFromWhitelist(from, to);
+    translate_prefs->RemoveLanguagePairFromAlwaysTranslateList(from, to);
   } else if (pref_name == "too_often_denied") {
     translate_prefs->ResetDenialState();
   } else {
@@ -241,9 +241,9 @@
       language::prefs::kFluentLanguages,
       prefs::kOfferTranslateEnabled,
       translate::TranslatePrefs::kPrefTranslateRecentTarget,
-      translate::TranslatePrefs::kPrefTranslateSiteBlacklistDeprecated,
-      translate::TranslatePrefs::kPrefTranslateSiteBlacklistWithTime,
-      translate::TranslatePrefs::kPrefTranslateWhitelists,
+      translate::TranslatePrefs::kPrefNeverPromptSitesDeprecated,
+      translate::TranslatePrefs::kPrefNeverPromptSitesWithTime,
+      translate::TranslatePrefs::kPrefAlwaysTranslateLists,
       translate::TranslatePrefs::kPrefTranslateDeniedCount,
       translate::TranslatePrefs::kPrefTranslateIgnoredCount,
       translate::TranslatePrefs::kPrefTranslateAcceptedCount,
diff --git a/components/viz/common/quads/render_pass_io.cc b/components/viz/common/quads/render_pass_io.cc
index fce7b1ee..ed09e2e 100644
--- a/components/viz/common/quads/render_pass_io.cc
+++ b/components/viz/common/quads/render_pass_io.cc
@@ -385,7 +385,7 @@
   // and serialization of PaintRecords.
   cc::PaintOp::SerializeOptions options(nullptr, nullptr, nullptr, nullptr,
                                         nullptr, nullptr, false, false, 0,
-                                        SkMatrix::I());
+                                        SkM44());
   cc::PaintOpWriter writer(buffer.data(), buffer.size(), options,
                            true /* enable_security_constraints */);
   writer.Write(filter.get());
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 24ec17a..ca4bcf64 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1865,6 +1865,8 @@
     "webauth/webauth_request_security_checker.h",
     "webid/federated_auth_request_impl.cc",
     "webid/federated_auth_request_impl.h",
+    "webid/idp_network_request_manager.cc",
+    "webid/idp_network_request_manager.h",
     "webrtc/webrtc_internals.cc",
     "webrtc/webrtc_internals.h",
     "webrtc/webrtc_internals_connections_observer.h",
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 7f4120b4..ac6663b 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -998,6 +998,10 @@
   RunAriaTest(FILE_PATH_LITERAL("aria-owns-crash-2.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityAriaOwnsGrid) {
+  RunAriaTest(FILE_PATH_LITERAL("aria-owns-grid.html"));
+}
+
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
                        AccessibilityAriaOwnsIgnored) {
   RunAriaTest(FILE_PATH_LITERAL("aria-owns-ignored.html"));
@@ -1008,6 +1012,11 @@
   RunAriaTest(FILE_PATH_LITERAL("aria-owns-included-in-tree.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
+                       AccessibilityAriaOwnsFromDisplayNone) {
+  RunAriaTest(FILE_PATH_LITERAL("aria-owns-from-display-none.html"));
+}
+
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityAriaOwnsList) {
   RunAriaTest(FILE_PATH_LITERAL("aria-owns-list.html"));
 }
@@ -1184,11 +1193,6 @@
 }
 
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
-                       AccessibilityAriaTableIllegalRoles) {
-  RunAriaTest(FILE_PATH_LITERAL("aria-table-illegal-roles.html"));
-}
-
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
                        AccessibilityAriaTabNestedInLists) {
   RunAriaTest(FILE_PATH_LITERAL("aria-tab-nested-in-lists.html"));
 }
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host.h b/content/browser/indexed_db/indexed_db_dispatcher_host.h
index 05b06c1..49899e6 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host.h
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host.h
@@ -64,8 +64,6 @@
 
   // A shortcut for accessing our context.
   IndexedDBContextImpl* context() const { return indexed_db_context_; }
-  storage::mojom::BlobStorageContext* mojo_blob_storage_context();
-  storage::mojom::NativeFileSystemContext* native_file_system_context();
 
   // Must be called on the IDB sequence.
   base::WeakPtr<IndexedDBDispatcherHost> AsWeakPtr() {
@@ -97,6 +95,9 @@
  private:
   friend class IndexedDBDispatcherHostTest;
 
+  storage::mojom::BlobStorageContext* mojo_blob_storage_context();
+  storage::mojom::NativeFileSystemContext* native_file_system_context();
+
   // blink::mojom::IDBFactory implementation:
   void GetDatabaseInfo(mojo::PendingAssociatedRemote<blink::mojom::IDBCallbacks>
                            pending_callbacks) override;
diff --git a/content/browser/indexed_db/indexed_db_factory_impl.h b/content/browser/indexed_db/indexed_db_factory_impl.h
index cb81798..71e26a9 100644
--- a/content/browser/indexed_db/indexed_db_factory_impl.h
+++ b/content/browser/indexed_db/indexed_db_factory_impl.h
@@ -160,8 +160,6 @@
       scoped_refptr<base::SequencedTaskRunner> idb_task_runner,
       scoped_refptr<base::SequencedTaskRunner> io_task_runner);
 
-  IndexedDBContextImpl* context() const { return context_; }
-
  private:
   friend class IndexedDBBrowserTest;
   friend class IndexedDBOriginState;
diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc
index 6c8bd00..04e360f 100644
--- a/content/browser/renderer_host/render_frame_host_manager.cc
+++ b/content/browser/renderer_host/render_frame_host_manager.cc
@@ -739,30 +739,34 @@
 }
 
 void RenderFrameHostManager::ValidateSpeculativeRenderFrameHostForBug1146573() {
+  ValidateSpeculativeRenderFrameHostForBug1146573(
+      render_frame_host_.get(), speculative_render_frame_host_.get());
+}
+
+// Static
+void RenderFrameHostManager::ValidateSpeculativeRenderFrameHostForBug1146573(
+    RenderFrameHostImpl* current,
+    RenderFrameHostImpl* pending) {
   // TODO(https://crbug.com/1146573): Remove this when the bug is closed.
   if (ShouldCreateNewHostForSameSiteSubframe())
     return;
   // This can happen during destruction after the RFH has been cleared.
-  if (!render_frame_host_)
+  if (!current)
     return;
-  if (!speculative_render_frame_host_)
+  if (!pending)
     return;
-  if (speculative_render_frame_host_->GetSiteInstance() ==
-      render_frame_host_->GetSiteInstance()) {
+  if (pending->GetSiteInstance() == current->GetSiteInstance()) {
     // This should never be true.
-    SCOPED_CRASH_KEY_BOOL(ValidateSpeculative, HostsEqual,
-                          speculative_render_frame_host_ == render_frame_host_);
-    DCHECK_NE(speculative_render_frame_host_, render_frame_host_);
+    SCOPED_CRASH_KEY_BOOL(ValidateSpeculative, HostsEqual, pending == current);
+    DCHECK_NE(pending, current);
     SCOPED_CRASH_KEY_BOOL(ValidateSpeculative, Live,
-                          render_frame_host_->IsRenderFrameLive());
-    SCOPED_CRASH_KEY_STRING256(
-        ValidateSpeculative, OldSiteInstance,
-        render_frame_host_->GetSiteInstance()->GetSiteURL().spec());
-    SCOPED_CRASH_KEY_STRING256(
-        ValidateSpeculative, NewSiteInstance,
-        speculative_render_frame_host_->GetSiteInstance()->GetSiteURL().spec());
+                          current->IsRenderFrameLive());
+    SCOPED_CRASH_KEY_STRING256(ValidateSpeculative, OldSiteInstance,
+                               current->GetSiteInstance()->GetSiteURL().spec());
+    SCOPED_CRASH_KEY_STRING256(ValidateSpeculative, NewSiteInstance,
+                               pending->GetSiteInstance()->GetSiteURL().spec());
     SCOPED_CRASH_KEY_BOOL(ValidateSpeculative, MustBeReplaced,
-                          render_frame_host_->must_be_replaced());
+                          current->must_be_replaced());
     NOTREACHED();
     base::debug::DumpWithoutCrashing();
   }
@@ -3207,6 +3211,13 @@
 
 std::unique_ptr<RenderFrameHostImpl> RenderFrameHostManager::SetRenderFrameHost(
     std::unique_ptr<RenderFrameHostImpl> render_frame_host) {
+  // It's expected that a same-site swap will occur via here when
+  // ShouldSkipEarlyCommitPendingForCrashedFrame is true, so only check when
+  // that's not the case.
+  if (!(render_frame_host_ && render_frame_host_->must_be_replaced())) {
+    RenderFrameHostManager::ValidateSpeculativeRenderFrameHostForBug1146573(
+        render_frame_host_.get(), render_frame_host.get());
+  }
   // Swap the two.
   std::unique_ptr<RenderFrameHostImpl> old_render_frame_host =
       std::move(render_frame_host_);
diff --git a/content/browser/renderer_host/render_frame_host_manager.h b/content/browser/renderer_host/render_frame_host_manager.h
index d77f2959..bc5b927 100644
--- a/content/browser/renderer_host/render_frame_host_manager.h
+++ b/content/browser/renderer_host/render_frame_host_manager.h
@@ -329,6 +329,9 @@
   // Validate the speculative RFH and DumpWithoutCrashing if it's invalid.
   // TODO(https://crbug.com/1146573): Remove this when the bug is closed.
   void ValidateSpeculativeRenderFrameHostForBug1146573();
+  static void ValidateSpeculativeRenderFrameHostForBug1146573(
+      RenderFrameHostImpl* current,
+      RenderFrameHostImpl* pending);
 
   // Called (possibly several times) during a navigation to select or create an
   // appropriate RenderFrameHost for the provided URL. The returned pointer will
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index 39d40458..e9c14e3d 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -4,7 +4,13 @@
 
 #include "content/browser/webid/federated_auth_request_impl.h"
 
+#include "base/callback.h"
+#include "base/strings/string_piece.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
+#include "content/public/common/content_client.h"
+#include "url/url_constants.h"
+
+using blink::mojom::RequestIdTokenStatus;
 
 namespace content {
 
@@ -27,6 +33,14 @@
   return true;
 }
 
+// Checks requirements for URLs received from the IDP.
+bool IdpUrlIsValid(const GURL& url) {
+  if (!url.is_valid() || !url.SchemeIs(url::kHttpsScheme))
+    return false;
+
+  return true;
+}
+
 }  // namespace
 
 FederatedAuthRequestImpl::FederatedAuthRequestImpl(
@@ -59,12 +73,135 @@
   new FederatedAuthRequestImpl(host, std::move(receiver));
 }
 
-void FederatedAuthRequestImpl::RequestIdToken(const ::GURL& provider,
+void FederatedAuthRequestImpl::RequestIdToken(const GURL& provider,
+                                              const std::string& id_request,
                                               RequestIdTokenCallback callback) {
-  // TODO(kenrb): Instantiate a WebIDRequestManager object and invoke
-  // the main request handler logic.
+  if (callback_) {
+    std::move(callback).Run(RequestIdTokenStatus::kErrorTooManyRequests, "");
+    return;
+  }
+
+  callback_ = std::move(callback);
+  provider_ = provider;
+  id_request_ = id_request;
+
+  network_manager_ =
+      IdpNetworkRequestManager::Create(provider, render_frame_host());
+  if (!network_manager_) {
+    CompleteRequest(RequestIdTokenStatus::kError, "");
+    return;
+  }
+
+  request_dialog_controller_ =
+      GetContentClient()->browser()->CreateIdentityRequestDialogController();
+
+  network_manager_->FetchIDPWellKnown(
+      base::BindOnce(&FederatedAuthRequestImpl::OnWellKnownFetched,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void FederatedAuthRequestImpl::OnWellKnownFetched(
+    IdpNetworkRequestManager::FetchStatus status,
+    const std::string& idp_endpoint) {
+  switch (status) {
+    case IdpNetworkRequestManager::FetchStatus::kWebIdNotSupported: {
+      CompleteRequest(RequestIdTokenStatus::kErrorWebIdNotSupportedByProvider,
+                      "");
+      return;
+    }
+    case IdpNetworkRequestManager::FetchStatus::kFetchError: {
+      CompleteRequest(RequestIdTokenStatus::kError, "");
+      return;
+    }
+    case IdpNetworkRequestManager::FetchStatus::kSuccess: {
+      // Intentional fall-through.
+    }
+  }
+
+  idp_endpoint_url_ = GURL(base::StringPiece(idp_endpoint));
+  // TODO(kenrb): Do we have to check that this URL is same-origin with the
+  // provider, or is that not a requirement?
   // https://crbug.com/1141125
-  std::move(callback).Run("");
+  if (!IdpUrlIsValid(idp_endpoint_url_)) {
+    CompleteRequest(RequestIdTokenStatus::kError, "");
+    return;
+  }
+
+  request_dialog_controller_->ShowInitialPermissionDialog(
+      base::BindOnce(&FederatedAuthRequestImpl::OnSigninApproved,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void FederatedAuthRequestImpl::OnSigninApproved(
+    IdentityRequestDialogController::UserApproval approval) {
+  if (approval != IdentityRequestDialogController::UserApproval::kApproved) {
+    CompleteRequest(RequestIdTokenStatus::kApprovalDeclined, "");
+    return;
+  }
+
+  network_manager_->SendSigninRequest(
+      idp_endpoint_url_, id_request_,
+      base::BindOnce(&FederatedAuthRequestImpl::OnSigninResponseReceived,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void FederatedAuthRequestImpl::OnSigninResponseReceived(
+    IdpNetworkRequestManager::SigninResponse status,
+    const std::string& response) {
+  // |response| is either the URL for the sign-in page or the ID token,
+  // depending on |status|.
+  switch (status) {
+    case IdpNetworkRequestManager::SigninResponse::kLoadIdp: {
+      GURL idp_signin_page_url = GURL(base::StringPiece(response));
+      if (!IdpUrlIsValid(idp_signin_page_url)) {
+        CompleteRequest(RequestIdTokenStatus::kError, "");
+        return;
+      }
+      request_dialog_controller_->ShowIdProviderWindow(
+          idp_signin_page_url,
+          base::BindOnce(&FederatedAuthRequestImpl::OnIdpPageClosed,
+                         weak_ptr_factory_.GetWeakPtr()));
+      return;
+    }
+    case IdpNetworkRequestManager::SigninResponse::kTokenGranted: {
+      // TODO(kenrb): Returning success here has to be dependent on whether
+      // a WebID flow has succeeded in the past, otherwise jump to
+      // the token permission dialog.
+      CompleteRequest(RequestIdTokenStatus::kSuccess, response);
+      return;
+    }
+    case IdpNetworkRequestManager::SigninResponse::kSigninError: {
+      CompleteRequest(RequestIdTokenStatus::kError, "");
+      return;
+    }
+  }
+}
+
+void FederatedAuthRequestImpl::OnIdpPageClosed() {
+  // TODO(kenrb): This needs to take a token that was provided by the IDP,
+  // or have an abort path if none provided.
+  request_dialog_controller_->ShowTokenExchangePermissionDialog(
+      base::BindOnce(&FederatedAuthRequestImpl::OnTokenProvisionApproved,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void FederatedAuthRequestImpl::OnTokenProvisionApproved(
+    IdentityRequestDialogController::UserApproval approval) {
+  if (approval != IdentityRequestDialogController::UserApproval::kApproved) {
+    CompleteRequest(RequestIdTokenStatus::kApprovalDeclined, "");
+    return;
+  }
+
+  // TODO(kenrb): Token gets returned here.
+  CompleteRequest(RequestIdTokenStatus::kSuccess, "");
+}
+
+void FederatedAuthRequestImpl::CompleteRequest(
+    blink::mojom::RequestIdTokenStatus status,
+    const std::string& id_token) {
+  request_dialog_controller_.reset();
+  network_manager_.reset();
+  std::move(callback_).Run(status, id_token);
 }
 
 }  // namespace content
diff --git a/content/browser/webid/federated_auth_request_impl.h b/content/browser/webid/federated_auth_request_impl.h
index 52c48c1d..e23360a 100644
--- a/content/browser/webid/federated_auth_request_impl.h
+++ b/content/browser/webid/federated_auth_request_impl.h
@@ -10,10 +10,13 @@
 
 #include "base/callback_forward.h"
 #include "base/macros.h"
+#include "content/browser/webid/idp_network_request_manager.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/frame_service_base.h"
+#include "content/public/browser/identity_request_dialog_controller.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "third_party/blink/public/mojom/webid/federated_auth_request.mojom.h"
+#include "url/gurl.h"
 
 namespace content {
 
@@ -33,19 +36,48 @@
   static void Create(RenderFrameHost*,
                      mojo::PendingReceiver<blink::mojom::FederatedAuthRequest>);
 
-  ~FederatedAuthRequestImpl() override;
-
   FederatedAuthRequestImpl(const FederatedAuthRequestImpl&) = delete;
   FederatedAuthRequestImpl& operator=(const FederatedAuthRequestImpl&) = delete;
 
+  ~FederatedAuthRequestImpl() override;
+
   // blink::mojom::FederatedAuthRequest:
-  void RequestIdToken(const ::GURL& provider, RequestIdTokenCallback) override;
+  void RequestIdToken(const GURL& provider,
+                      const std::string& id_request,
+                      RequestIdTokenCallback) override;
 
  private:
   FederatedAuthRequestImpl(
       RenderFrameHost*,
       mojo::PendingReceiver<blink::mojom::FederatedAuthRequest>);
 
+  void OnWellKnownFetched(IdpNetworkRequestManager::FetchStatus status,
+                          const std::string& idp_endpoint);
+
+  void OnSigninApproved(IdentityRequestDialogController::UserApproval approval);
+  void OnSigninResponseReceived(IdpNetworkRequestManager::SigninResponse status,
+                                const std::string& response);
+  void OnIdpPageClosed();
+  void OnTokenProvisionApproved(
+      IdentityRequestDialogController::UserApproval approval);
+
+  void CompleteRequest(blink::mojom::RequestIdTokenStatus,
+                       const std::string& id_token);
+
+  std::unique_ptr<IdpNetworkRequestManager> network_manager_;
+  std::unique_ptr<IdentityRequestDialogController> request_dialog_controller_;
+
+  // Parameters of auth request.
+  GURL provider_;
+  std::string id_request_;
+
+  // Fetched from the IDP well-known configuration.
+  // TODO(kenrb): This will expand to multiple fields at some point, and
+  // should be wrapped in a struct at that time.
+  GURL idp_endpoint_url_;
+
+  RequestIdTokenCallback callback_;
+
   base::WeakPtrFactory<FederatedAuthRequestImpl> weak_ptr_factory_{this};
 };
 
diff --git a/content/browser/webid/idp_network_request_manager.cc b/content/browser/webid/idp_network_request_manager.cc
new file mode 100644
index 0000000..99ea295
--- /dev/null
+++ b/content/browser/webid/idp_network_request_manager.cc
@@ -0,0 +1,304 @@
+// 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 "content/browser/webid/idp_network_request_manager.h"
+
+#include "base/base64url.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/storage_partition.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "net/base/isolation_info.h"
+#include "net/cookies/site_for_cookies.h"
+#include "net/http/http_status_code.h"
+#include "services/network/public/cpp/is_potentially_trustworthy.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "url/origin.h"
+
+namespace content {
+
+namespace {
+// TODO(kenrb): These need to be defined in the explainer or draft spec and
+// referenced here.
+constexpr char kWellKnownFilePath[] = ".well-known/webid";
+
+// Well-known configuration keys.
+constexpr char kIdpEndpointKey[] = "idp_endpoint";
+
+// Sign-in request response keys.
+constexpr char kSigninUrlKey[] = "signin_url";
+constexpr char kIdTokenKey[] = "id_token";
+
+constexpr char kAcceptMimeType[] = "application/json";
+
+// 1 MiB is an arbitrary upper bound that should account for any reasonable
+// response size that is a part of this protocol.
+constexpr int maxResponseSizeInKiB = 1024;
+
+net::NetworkTrafficAnnotationTag CreateTrafficAnnotation() {
+  return net::DefineNetworkTrafficAnnotation("webid", R"(
+        semantics {
+          sender: "WebID Backend"
+          description:
+            "The WebID API allows websites to initiate user account login "
+            "with identity providers which provide federated sign-in "
+            "capabilities using OpenID Connect. The API provides a "
+            "browser-mediated alternative to previously existing federated "
+            "sign-in implementations."
+          trigger:
+            "A website executes the navigator.id.get() JavaScript method to "
+            "initiate federated user sign-in to a designated provider."
+          data:
+            "An identity request contains a scope of claims specifying what "
+            "user information is being requested from the identity provider, "
+            "a label identifying the calling website application, and some "
+            "OpenID Connect protocol functional fields."
+          destination: WEBSITE
+        }
+        policy {
+          cookies_allowed: YES
+          cookies_store: "user"
+          setting: "Not user controlled. But the verification is a trusted "
+                   "API that doesn't use user data."
+          policy_exception_justification:
+            "Not implemented, considered not useful as no content is being "
+            "uploaded; this request merely downloads the resources on the web."
+        })");
+}
+
+scoped_refptr<network::SharedURLLoaderFactory> GetUrlLoaderFactory(
+    content::RenderFrameHost* host) {
+  return content::BrowserContext::GetDefaultStoragePartition(
+             host->GetBrowserContext())
+      ->GetURLLoaderFactoryForBrowserProcess();
+}
+
+}  // namespace
+
+// static
+std::unique_ptr<IdpNetworkRequestManager> IdpNetworkRequestManager::Create(
+    const GURL& provider,
+    RenderFrameHost* host) {
+  // WebID is restricted to secure contexts.
+  if (!network::IsUrlPotentiallyTrustworthy(provider))
+    return nullptr;
+
+  return std::make_unique<IdpNetworkRequestManager>(provider, host);
+}
+
+IdpNetworkRequestManager::IdpNetworkRequestManager(const GURL& provider,
+                                                   RenderFrameHost* host)
+    : provider_(provider), render_frame_host_(host) {}
+
+IdpNetworkRequestManager::~IdpNetworkRequestManager() = default;
+
+void IdpNetworkRequestManager::FetchIDPWellKnown(
+    FetchWellKnownCallback callback) {
+  DCHECK(!url_loader_);
+  DCHECK(!idp_well_known_callback_);
+
+  idp_well_known_callback_ = std::move(callback);
+
+  const url::Origin& idp = url::Origin::Create(provider_);
+  GURL target_url = idp.GetURL().Resolve(kWellKnownFilePath);
+
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      CreateTrafficAnnotation();
+
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = target_url;
+  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+  resource_request->headers.SetHeader(net::HttpRequestHeaders::kAccept,
+                                      kAcceptMimeType);
+  resource_request->request_initiator =
+      render_frame_host_->GetLastCommittedOrigin();
+
+  url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
+                                                 traffic_annotation);
+
+  // This creates a new URLLoaderFactory that matches the RP's factory for
+  // the early uncredentialed request, which serves to minimize cross-site
+  // tracking risk in the case that the flow does not proceed any further.
+  mojo::Remote<network::mojom::URLLoaderFactory> loader_factory;
+  render_frame_host_->CreateNetworkServiceDefaultFactory(
+      loader_factory.BindNewPipeAndPassReceiver());
+
+  url_loader_->DownloadToString(
+      loader_factory.get(),
+      base::BindOnce(&IdpNetworkRequestManager::OnWellKnownLoaded,
+                     weak_ptr_factory_.GetWeakPtr()),
+      maxResponseSizeInKiB * 1024);
+}
+
+void IdpNetworkRequestManager::SendSigninRequest(
+    const GURL& signin_url,
+    const std::string& request,
+    SigninRequestCallback callback) {
+  DCHECK(!url_loader_);
+  DCHECK(!signin_request_callback_);
+
+  signin_request_callback_ = std::move(callback);
+
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      CreateTrafficAnnotation();
+
+  // TODO(kenrb): A straight URL encoding isn't right. Add proper parsing.
+  // https://crbug.com/1141125.
+  std::string encoded_request;
+  base::Base64UrlEncode(base::StringPiece(request),
+                        base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+                        &encoded_request);
+
+  // TODO: Should this be a POST, rather than a GET using query parameters?
+  // https://crbug.com/1141125.
+  GURL target_url = GURL(signin_url.spec() + "?" + encoded_request);
+
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  auto target_origin = url::Origin::Create(target_url);
+  auto site_for_cookies = net::SiteForCookies::FromOrigin(target_origin);
+  resource_request->request_initiator =
+      render_frame_host_->GetLastCommittedOrigin();
+  resource_request->url = target_url;
+  resource_request->site_for_cookies = site_for_cookies;
+  resource_request->headers.SetHeader(net::HttpRequestHeaders::kAccept,
+                                      kAcceptMimeType);
+  resource_request->credentials_mode =
+      network::mojom::CredentialsMode::kInclude;
+  resource_request->trusted_params = network::ResourceRequest::TrustedParams();
+  resource_request->trusted_params->isolation_info = net::IsolationInfo::Create(
+      net::IsolationInfo::RequestType::kOther, target_origin, target_origin,
+      site_for_cookies);
+
+  // TODO(kenrb): Make this not send cookies. https://crbug.com/1141125.
+  url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
+                                                 traffic_annotation);
+
+  auto loader_factory = GetUrlLoaderFactory(render_frame_host_);
+
+  url_loader_->DownloadToString(
+      loader_factory.get(),
+      base::BindOnce(&IdpNetworkRequestManager::OnSigninRequestResponse,
+                     weak_ptr_factory_.GetWeakPtr()),
+      1024 * 1024);
+}
+
+void IdpNetworkRequestManager::OnWellKnownLoaded(
+    std::unique_ptr<std::string> response_body) {
+  url_loader_.reset();
+
+  int response_code = -1;
+  if (url_loader_->ResponseInfo() && url_loader_->ResponseInfo()->headers)
+    response_code = url_loader_->ResponseInfo()->headers->response_code();
+
+  if (response_code == net::HTTP_NOT_FOUND) {
+    std::move(idp_well_known_callback_)
+        .Run(FetchStatus::kWebIdNotSupported, std::string());
+    return;
+  }
+
+  if (!response_body) {
+    std::move(idp_well_known_callback_)
+        .Run(FetchStatus::kFetchError, std::string());
+    return;
+  }
+
+  data_decoder::DataDecoder::ParseJsonIsolated(
+      *response_body,
+      base::BindOnce(&IdpNetworkRequestManager::OnWellKnownParsed,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void IdpNetworkRequestManager::OnWellKnownParsed(
+    data_decoder::DataDecoder::ValueOrError result) {
+  if (!result.value) {
+    std::move(idp_well_known_callback_)
+        .Run(FetchStatus::kFetchError, std::string());
+    return;
+  }
+
+  auto& response = *result.value;
+
+  if (!response.is_dict()) {
+    std::move(idp_well_known_callback_)
+        .Run(FetchStatus::kFetchError, std::string());
+    return;
+  }
+
+  const base::Value* idp_endpoint = response.FindKey(kIdpEndpointKey);
+
+  if (!idp_endpoint || !idp_endpoint->is_string()) {
+    std::move(idp_well_known_callback_)
+        .Run(FetchStatus::kFetchError, std::string());
+    return;
+  }
+
+  std::move(idp_well_known_callback_)
+      .Run(FetchStatus::kSuccess, idp_endpoint->GetString());
+}
+
+void IdpNetworkRequestManager::OnSigninRequestResponse(
+    std::unique_ptr<std::string> response_body) {
+  int response_code = -1;
+  if (url_loader_->ResponseInfo() && url_loader_->ResponseInfo()->headers)
+    response_code = url_loader_->ResponseInfo()->headers->response_code();
+
+  url_loader_.reset();
+
+  if (!response_body) {
+    std::move(signin_request_callback_)
+        .Run(SigninResponse::kSigninError, std::string());
+    return;
+  }
+
+  data_decoder::DataDecoder::ParseJsonIsolated(
+      *response_body,
+      base::BindOnce(&IdpNetworkRequestManager::OnSigninRequestParsed,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void IdpNetworkRequestManager::OnSigninRequestParsed(
+    data_decoder::DataDecoder::ValueOrError result) {
+  if (!result.value) {
+    std::move(signin_request_callback_)
+        .Run(SigninResponse::kSigninError, std::string());
+    return;
+  }
+
+  auto& response = *result.value;
+
+  if (!response.is_dict()) {
+    std::move(signin_request_callback_)
+        .Run(SigninResponse::kSigninError, std::string());
+    return;
+  }
+
+  // TODO(kenrb): This possibly should be part of the well-known file, unless
+  // IDPs ever have a reason to serve different URLs for sign-in pages.
+  // https://crbug.com/1141125.
+  const base::Value* signin_url = response.FindKey(kSigninUrlKey);
+  const base::Value* id_token = response.FindKey(kIdTokenKey);
+
+  // Only one of the fields should be present.
+  bool signin_url_present = signin_url && signin_url->is_string();
+  bool token_present = id_token && id_token->is_string();
+  bool both_present = signin_url_present && token_present;
+  if (!(signin_url_present || token_present) || both_present) {
+    std::move(signin_request_callback_)
+        .Run(SigninResponse::kSigninError, std::string());
+    return;
+  }
+
+  if (signin_url) {
+    std::move(signin_request_callback_)
+        .Run(SigninResponse::kLoadIdp, signin_url->GetString());
+    return;
+  }
+
+  std::move(signin_request_callback_)
+      .Run(SigninResponse::kTokenGranted, id_token->GetString());
+}
+
+}  // namespace content
diff --git a/content/browser/webid/idp_network_request_manager.h b/content/browser/webid/idp_network_request_manager.h
new file mode 100644
index 0000000..dbf7958
--- /dev/null
+++ b/content/browser/webid/idp_network_request_manager.h
@@ -0,0 +1,110 @@
+// 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 CONTENT_BROWSER_WEBID_IDP_NETWORK_REQUEST_MANAGER_H_
+#define CONTENT_BROWSER_WEBID_IDP_NETWORK_REQUEST_MANAGER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "services/data_decoder/public/cpp/data_decoder.h"
+#include "url/gurl.h"
+
+namespace network {
+class SimpleURLLoader;
+}
+
+namespace content {
+
+class RenderFrameHost;
+
+// Manages network requests and maintains relevant state for interaction with
+// the Identity Provider across a WebID transaction. Owned by
+// FederatedAuthRequestImpl and has a lifetime limited to a single identity
+// transaction between an RP and an IDP.
+//
+// Diagram of the permission-based data flows between the browser and the IDP:
+//  .-------.                           .---.
+//  |Browser|                           |IDP|
+//  '-------'                           '---'
+//      |                                 |
+//      |     GET /.well-known/webid      |
+//      |-------------------------------->|
+//      |                                 |
+//      |        JSON{idp_url}            |
+//      |<--------------------------------|
+//      |                                 |
+//      | POST /idp_url with OIDC request |
+//      |-------------------------------->|
+//      |                                 |
+//      |      id_token or signin_url     |
+//      |<--------------------------------|
+//  .-------.                           .---.
+//  |Browser|                           |IDP|
+//  '-------'                           '---'
+//
+// If the IDP returns an id_token, the sequence finishes. If it returns a
+// signin_url, that URL is loaded as a rendered Document into a new window
+// for the user to interact with the IDP.
+class IdpNetworkRequestManager {
+ public:
+  enum class FetchStatus {
+    kSuccess,
+    kWebIdNotSupported,
+    kFetchError,
+  };
+
+  enum class SigninResponse {
+    kLoadIdp,
+    kTokenGranted,
+    kSigninError,
+  };
+
+  using FetchWellKnownCallback =
+      base::OnceCallback<void(FetchStatus, const std::string&)>;
+  using SigninRequestCallback =
+      base::OnceCallback<void(SigninResponse, const std::string&)>;
+
+  static std::unique_ptr<IdpNetworkRequestManager> Create(
+      const GURL& provider,
+      RenderFrameHost* host);
+
+  IdpNetworkRequestManager(const GURL& provider, RenderFrameHost* host);
+
+  virtual ~IdpNetworkRequestManager();
+
+  IdpNetworkRequestManager(const IdpNetworkRequestManager&) = delete;
+  IdpNetworkRequestManager& operator=(const IdpNetworkRequestManager&) = delete;
+
+  // Attempt to fetch the IDP's WebID parameters from the its .well-known file.
+  void FetchIDPWellKnown(FetchWellKnownCallback);
+
+  // Transmit the OAuth request to the IDP.
+  void SendSigninRequest(const GURL& signin_url,
+                         const std::string& request,
+                         SigninRequestCallback);
+
+ private:
+  void OnWellKnownLoaded(std::unique_ptr<std::string> response_body);
+  void OnWellKnownParsed(data_decoder::DataDecoder::ValueOrError result);
+  void OnSigninRequestResponse(std::unique_ptr<std::string> response_body);
+  void OnSigninRequestParsed(data_decoder::DataDecoder::ValueOrError result);
+
+  // URL of the Identity Provider.
+  GURL provider_;
+
+  RenderFrameHost* render_frame_host_;
+
+  FetchWellKnownCallback idp_well_known_callback_;
+  SigninRequestCallback signin_request_callback_;
+
+  std::unique_ptr<network::SimpleURLLoader> url_loader_;
+
+  base::WeakPtrFactory<IdpNetworkRequestManager> weak_ptr_factory_{this};
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_WEBID_IDP_NETWORK_REQUEST_MANAGER_H_
diff --git a/content/public/android/java/src/org/chromium/content/browser/AppWebMessagePort.java b/content/public/android/java/src/org/chromium/content/browser/AppWebMessagePort.java
index ce7cd031..6c4f3d6e 100644
--- a/content/public/android/java/src/org/chromium/content/browser/AppWebMessagePort.java
+++ b/content/public/android/java/src/org/chromium/content/browser/AppWebMessagePort.java
@@ -26,7 +26,7 @@
 import org.chromium.mojo.system.Core;
 import org.chromium.mojo.system.Pair;
 import org.chromium.mojo_base.BigBufferUtil;
-import org.chromium.skia.mojom.BitmapWithArbitraryBpp;
+import org.chromium.skia.mojom.BitmapN32;
 
 /**
  * Represents the MessageChannel MessagePort object. Inspired from
@@ -299,7 +299,7 @@
         msg.message.nativeFileSystemTokens = new NativeFileSystemTransferToken[0];
         msg.message.senderOrigin = null;
         msg.arrayBufferContentsArray = new SerializedArrayBufferContents[0];
-        msg.imageBitmapContentsArray = new BitmapWithArbitraryBpp[0];
+        msg.imageBitmapContentsArray = new BitmapN32[0];
         msg.ports = ports;
         msg.streamChannels = new MessagePortDescriptor[0];
         mConnector.accept(msg.serializeWithHeader(mMojoCore, MESSAGE_HEADER));
diff --git a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
index 42c2a06..94eb9f4f 100644
--- a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
@@ -38,7 +38,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import org.chromium.base.BuildInfo;
 import org.chromium.base.Log;
 import org.chromium.base.PackageManagerUtils;
 import org.chromium.base.UserData;
@@ -1447,7 +1446,7 @@
 
     @VisibleForTesting
     /* package */ void performHapticFeedback() {
-        if (BuildInfo.isAtLeastQ() && mView != null) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && mView != null) {
             mView.performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE);
         }
     }
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index aee0191..80da4829 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -180,6 +180,7 @@
     "hid_chooser.h",
     "hid_delegate.h",
     "histogram_fetcher.h",
+    "identity_request_dialog_controller.h",
     "idle_manager.h",
     "installability_error.cc",
     "installability_error.h",
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 8aa1b194..e1c6c1f7 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -26,6 +26,7 @@
 #include "content/public/browser/browser_accessibility_state.h"
 #include "content/public/browser/browser_main_parts.h"
 #include "content/public/browser/client_certificate_delegate.h"
+#include "content/public/browser/identity_request_dialog_controller.h"
 #include "content/public/browser/login_delegate.h"
 #include "content/public/browser/navigation_throttle.h"
 #include "content/public/browser/navigation_ui_data.h"
@@ -1145,4 +1146,9 @@
   return false;
 }
 
+std::unique_ptr<IdentityRequestDialogController>
+ContentBrowserClient::CreateIdentityRequestDialogController() {
+  return nullptr;
+}
+
 }  // namespace content
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 4cdd98db..2a341fb 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -192,6 +192,7 @@
 class FeatureObserverClient;
 class FontAccessDelegate;
 class HidDelegate;
+class IdentityRequestDialogController;
 class LoginDelegate;
 class MediaObserver;
 class NavigationHandle;
@@ -1903,6 +1904,10 @@
   // Returns true if the embedder has an error page to show for the given http
   // status code.
   virtual bool HasErrorPage(int http_status_code);
+
+  // Creates a modal window that intermediates the exchange of ID tokens.
+  virtual std::unique_ptr<IdentityRequestDialogController>
+  CreateIdentityRequestDialogController();
 };
 
 }  // namespace content
diff --git a/content/public/browser/identity_request_dialog_controller.h b/content/public/browser/identity_request_dialog_controller.h
new file mode 100644
index 0000000..d51fd08
--- /dev/null
+++ b/content/public/browser/identity_request_dialog_controller.h
@@ -0,0 +1,47 @@
+// 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 CONTENT_PUBLIC_BROWSER_IDENTITY_REQUEST_DIALOG_CONTROLLER_H_
+#define CONTENT_PUBLIC_BROWSER_IDENTITY_REQUEST_DIALOG_CONTROLLER_H_
+
+#include "base/callback.h"
+#include "content/common/content_export.h"
+
+class GURL;
+
+namespace content {
+
+// IdentityRequestDialogController is in interface for control of the UI
+// surfaces that are displayed to intermediate the exchange of ID tokens.
+class CONTENT_EXPORT IdentityRequestDialogController {
+ public:
+  enum class UserApproval {
+    kApproved,
+    kDenied,
+  };
+
+  using InitialApprovalCallback = base::OnceCallback<void(UserApproval)>;
+  using IdProviderWindowClosedCallback = base::OnceCallback<void()>;
+  using TokenExchangeApprovalCallback = base::OnceCallback<void(UserApproval)>;
+
+  IdentityRequestDialogController() = default;
+
+  IdentityRequestDialogController(const IdentityRequestDialogController&) =
+      delete;
+  IdentityRequestDialogController& operator=(
+      const IdentityRequestDialogController&) = delete;
+
+  virtual ~IdentityRequestDialogController() = default;
+
+  // Permission-oriented flow methods.
+  virtual void ShowInitialPermissionDialog(InitialApprovalCallback) = 0;
+  virtual void ShowIdProviderWindow(const GURL& idp_signin_url,
+                                    IdProviderWindowClosedCallback) = 0;
+  virtual void ShowTokenExchangePermissionDialog(
+      TokenExchangeApprovalCallback) = 0;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_IDENTITY_REQUEST_DIALOG_CONTROLLER_H_
diff --git a/content/renderer/webgraphicscontext3d_provider_impl.cc b/content/renderer/webgraphicscontext3d_provider_impl.cc
index 2c62653..1ea5a4b 100644
--- a/content/renderer/webgraphicscontext3d_provider_impl.cc
+++ b/content/renderer/webgraphicscontext3d_provider_impl.cc
@@ -198,7 +198,12 @@
     media::PaintCanvasVideoRenderer* video_renderer,
     media::VideoFrame* video_frame,
     cc::PaintCanvas* canvas) {
-  video_renderer->Copy(video_frame, canvas, context_provider());
+  video_renderer->Copy(video_frame, canvas, provider_.get());
+}
+
+viz::RasterContextProvider*
+WebGraphicsContext3DProviderImpl::RasterContextProvider() const {
+  return provider_.get();
 }
 
 }  // namespace content
diff --git a/content/renderer/webgraphicscontext3d_provider_impl.h b/content/renderer/webgraphicscontext3d_provider_impl.h
index 8f0f714..a60e488 100644
--- a/content/renderer/webgraphicscontext3d_provider_impl.h
+++ b/content/renderer/webgraphicscontext3d_provider_impl.h
@@ -57,10 +57,7 @@
   void CopyVideoFrame(media::PaintCanvasVideoRenderer* video_render,
                       media::VideoFrame* video_frame,
                       cc::PaintCanvas* canvas) override;
-
-  viz::ContextProviderCommandBuffer* context_provider() const {
-    return provider_.get();
-  }
+  viz::RasterContextProvider* RasterContextProvider() const override;
 
  private:
   // viz::ContextLostObserver implementation.
diff --git a/content/test/data/accessibility/aria/aria-grid-expected-blink.txt b/content/test/data/accessibility/aria/aria-grid-expected-blink.txt
index a3f0af0..b922b90 100644
--- a/content/test/data/accessibility/aria/aria-grid-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-grid-expected-blink.txt
@@ -1,18 +1,18 @@
 rootWebArea
 ++genericContainer ignored
 ++++genericContainer ignored
-++++++grid
-++++++++row
-++++++++++columnHeader name='Browser'
+++++++grid tableRowCount=2 tableColumnCount=2
+++++++++row tableRowIndex=0
+++++++++++columnHeader name='Browser' tableCellColumnIndex=0 tableCellColumnSpan=1 tableCellRowIndex=0 tableCellRowSpan=1
 ++++++++++++staticText name='Browser'
 ++++++++++++++inlineTextBox name='Browser'
-++++++++++columnHeader name='Rendering Engine'
+++++++++++columnHeader name='Rendering Engine' tableCellColumnIndex=1 tableCellColumnSpan=1 tableCellRowIndex=0 tableCellRowSpan=1
 ++++++++++++staticText name='Rendering Engine'
 ++++++++++++++inlineTextBox name='Rendering Engine'
-++++++++row
-++++++++++cell name='Chrome'
+++++++++row tableRowIndex=1
+++++++++++cell name='Chrome' tableCellColumnIndex=0 tableCellColumnSpan=1 tableCellRowIndex=1 tableCellRowSpan=1
 ++++++++++++staticText name='Chrome'
 ++++++++++++++inlineTextBox name='Chrome'
-++++++++++cell name='Blink'
+++++++++++cell name='Blink' tableCellColumnIndex=1 tableCellColumnSpan=1 tableCellRowIndex=1 tableCellRowSpan=1
 ++++++++++++staticText name='Blink'
 ++++++++++++++inlineTextBox name='Blink'
diff --git a/content/test/data/accessibility/aria/aria-grid.html b/content/test/data/accessibility/aria/aria-grid.html
index 1ab692f..9537fce 100644
--- a/content/test/data/accessibility/aria/aria-grid.html
+++ b/content/test/data/accessibility/aria/aria-grid.html
@@ -1,4 +1,5 @@
 <!--
+@BLINK-ALLOW:table*
 @MAC-ALLOW:AXRoleDescription
 @WIN-ALLOW:*SELECTABLE
 @WIN-ALLOW:xml-roles*
diff --git a/content/test/data/accessibility/aria/aria-owns-from-display-none-expected-auralinux.txt b/content/test/data/accessibility/aria/aria-owns-from-display-none-expected-auralinux.txt
new file mode 100644
index 0000000..958214d
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-owns-from-display-none-expected-auralinux.txt
@@ -0,0 +1,2 @@
+[document web]
+++[entry] selectable-text
diff --git a/content/test/data/accessibility/aria/aria-owns-from-display-none-expected-blink.txt b/content/test/data/accessibility/aria/aria-owns-from-display-none-expected-blink.txt
new file mode 100644
index 0000000..3806553
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-owns-from-display-none-expected-blink.txt
@@ -0,0 +1,6 @@
+rootWebArea
+++genericContainer ignored
+++++genericContainer ignored
+++++++group ignored invisible
+++++++++textField multiline
+++++++++++genericContainer
diff --git a/content/test/data/accessibility/aria/aria-owns-from-display-none.html b/content/test/data/accessibility/aria/aria-owns-from-display-none.html
new file mode 100644
index 0000000..f0057b7b
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-owns-from-display-none.html
@@ -0,0 +1,8 @@
+<style>
+  [role=group] { display: none; }
+  textarea { display: block; }
+</style>
+<textarea id="input-0"></textarea>
+<div role="group" aria-owns="input-0"></div>
+
+
diff --git a/content/test/data/accessibility/aria/aria-owns-grid-expected-blink.txt b/content/test/data/accessibility/aria/aria-owns-grid-expected-blink.txt
new file mode 100644
index 0000000..b922b90
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-owns-grid-expected-blink.txt
@@ -0,0 +1,18 @@
+rootWebArea
+++genericContainer ignored
+++++genericContainer ignored
+++++++grid tableRowCount=2 tableColumnCount=2
+++++++++row tableRowIndex=0
+++++++++++columnHeader name='Browser' tableCellColumnIndex=0 tableCellColumnSpan=1 tableCellRowIndex=0 tableCellRowSpan=1
+++++++++++++staticText name='Browser'
+++++++++++++++inlineTextBox name='Browser'
+++++++++++columnHeader name='Rendering Engine' tableCellColumnIndex=1 tableCellColumnSpan=1 tableCellRowIndex=0 tableCellRowSpan=1
+++++++++++++staticText name='Rendering Engine'
+++++++++++++++inlineTextBox name='Rendering Engine'
+++++++++row tableRowIndex=1
+++++++++++cell name='Chrome' tableCellColumnIndex=0 tableCellColumnSpan=1 tableCellRowIndex=1 tableCellRowSpan=1
+++++++++++++staticText name='Chrome'
+++++++++++++++inlineTextBox name='Chrome'
+++++++++++cell name='Blink' tableCellColumnIndex=1 tableCellColumnSpan=1 tableCellRowIndex=1 tableCellRowSpan=1
+++++++++++++staticText name='Blink'
+++++++++++++++inlineTextBox name='Blink'
diff --git a/content/test/data/accessibility/aria/aria-owns-grid.html b/content/test/data/accessibility/aria/aria-owns-grid.html
new file mode 100644
index 0000000..5d95f10
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-owns-grid.html
@@ -0,0 +1,14 @@
+<!--
+@BLINK-ALLOW:table*
+-->
+<div role="grid" aria-owns="row1 row2">
+</div>
+
+<div id="row1" role="row">
+  <span role="columnheader">Browser</span>
+  <span role="columnheader">Rendering Engine</span>
+</div>
+<div id="row2" role="row">
+  <span role="gridcell">Chrome</span>
+  <span role="gridcell">Blink</span>
+</div>
diff --git a/content/test/data/accessibility/aria/aria-table-illegal-roles-expected-blink.txt b/content/test/data/accessibility/aria/aria-table-illegal-roles-expected-blink.txt
deleted file mode 100644
index 9ced58f5..0000000
--- a/content/test/data/accessibility/aria/aria-table-illegal-roles-expected-blink.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-rootWebArea
-++genericContainer ignored
-++++genericContainer ignored
-++++++genericContainer
-++++++++staticText name='illegal row'
-++++++++++inlineTextBox name='illegal row'
-++++++genericContainer
-++++++++staticText name='illegal cell'
-++++++++++inlineTextBox name='illegal cell'
-++++++genericContainer
-++++++++staticText name='illegal rowheader'
-++++++++++inlineTextBox name='illegal rowheader'
-++++++genericContainer
-++++++++staticText name='illegal columnheader'
-++++++++++inlineTextBox name='illegal columnheader'
-++++++genericContainer
-++++++++staticText name='illegal rowgroup'
-++++++++++inlineTextBox name='illegal rowgroup'
-++++++table
-++++++++row
-++++++++++cell name='legal cell'
-++++++++++++genericContainer
-++++++++++++++staticText name='illegal cell inside another cell'
-++++++++++++++++inlineTextBox name='illegal cell inside another cell'
diff --git a/content/test/data/accessibility/aria/aria-table-illegal-roles.html b/content/test/data/accessibility/aria/aria-table-illegal-roles.html
deleted file mode 100644
index 8736ca2..0000000
--- a/content/test/data/accessibility/aria/aria-table-illegal-roles.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Rows, cells, etc. require containing table, grid or treegrid.
--->
-<!DOCTYPE html>
-<html>
-<body>
-  <div role="row">illegal row</div>
-  <div role="cell">illegal cell</div>
-  <div role="rowheader">illegal rowheader</div>
-  <div role="columnheader">illegal columnheader</div>
-  <div role="rowgroup">illegal rowgroup</div>
-  <div role="table">
-    <div role="row">
-      <div role="cell" aria-label="legal cell">
-        <div role="cell">
-          illegal cell inside another cell
-        </div>
-      </div>
-    </div>
-  </div>
-</body>
-</html>
diff --git a/device/bluetooth/adapter.cc b/device/bluetooth/adapter.cc
index c4ebcb40..c21ba66 100644
--- a/device/bluetooth/adapter.cc
+++ b/device/bluetooth/adapter.cc
@@ -175,6 +175,11 @@
     const std::string& address,
     const device::BluetoothUUID& service_uuid,
     ConnectToServiceInsecurelyCallback callback) {
+  if (!base::Contains(allowed_uuids_, service_uuid)) {
+    std::move(callback).Run(/*result=*/nullptr);
+    return;
+  }
+
   auto* device = adapter_->GetDevice(address);
   if (device) {
     OnDeviceFetchedForInsecureServiceConnection(service_uuid,
@@ -203,6 +208,11 @@
     const std::string& service_name,
     const device::BluetoothUUID& service_uuid,
     CreateRfcommServiceInsecurelyCallback callback) {
+  if (!base::Contains(allowed_uuids_, service_uuid)) {
+    std::move(callback).Run(/*server_socket=*/mojo::NullRemote());
+    return;
+  }
+
   device::BluetoothAdapter::ServiceOptions service_options;
   service_options.name = service_name;
   service_options.require_authentication = false;
@@ -286,6 +296,11 @@
   }
 }
 
+void Adapter::AllowConnectionsForUuid(
+    const device::BluetoothUUID& service_uuid) {
+  allowed_uuids_.emplace(service_uuid);
+}
+
 void Adapter::OnDeviceFetchedForInsecureServiceConnection(
     const device::BluetoothUUID& service_uuid,
     ConnectToServiceInsecurelyCallback callback,
diff --git a/device/bluetooth/adapter.h b/device/bluetooth/adapter.h
index bf1721d..d58e726 100644
--- a/device/bluetooth/adapter.h
+++ b/device/bluetooth/adapter.h
@@ -48,8 +48,6 @@
                        SetDiscoverableCallback callback) override;
   void SetName(const std::string& name, SetNameCallback callback) override;
   void StartDiscoverySession(StartDiscoverySessionCallback callback) override;
-  // TODO(b/162975217): Add a mechanism to allowlist which address and UUID
-  // pairs clients are allowed to create a connection to.
   void ConnectToServiceInsecurely(
       const std::string& address,
       const device::BluetoothUUID& service_uuid,
@@ -77,6 +75,10 @@
   void GattServicesDiscovered(device::BluetoothAdapter* adapter,
                               device::BluetoothDevice* device) override;
 
+  // Permit untrusted clients to initiate outgoing connections, or listen on
+  // incoming connections, with |service_uuid|.
+  void AllowConnectionsForUuid(const device::BluetoothUUID& service_uuid);
+
  private:
   void OnDeviceFetchedForInsecureServiceConnection(
       const device::BluetoothUUID& service_uuid,
@@ -132,6 +134,10 @@
                          ConnectToServiceInsecurelyCallback>>
       pending_connect_to_service_args_;
 
+  // Allowed UUIDs for untrusted clients to initiate outgoing connections, or
+  // listen on incoming connections.
+  std::set<device::BluetoothUUID> allowed_uuids_;
+
   base::WeakPtrFactory<Adapter> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(Adapter);
diff --git a/device/bluetooth/adapter_unittest.cc b/device/bluetooth/adapter_unittest.cc
index b982308..7c2ed4f 100644
--- a/device/bluetooth/adapter_unittest.cc
+++ b/device/bluetooth/adapter_unittest.cc
@@ -32,6 +32,7 @@
 
 const char kKnownDeviceAddress[] = "00:00:00:00:01";
 const char kUnknownDeviceAddress[] = "00:00:00:00:02";
+const char kServiceName[] = "ServiceName";
 const char kServiceId[] = "0000abcd-0000-0000-0000-000000000001";
 const char kDeviceServiceDataStr[] = "ServiceData";
 
@@ -192,12 +193,28 @@
   VerifyAdvertisementWithScanData();
 }
 
+TEST_F(AdapterTest, TestConnectToServiceInsecurely_DisallowedUuid) {
+  // Do not call Adapter::AllowConnectionsForUuid();
+
+  base::RunLoop run_loop;
+  adapter_->ConnectToServiceInsecurely(
+      kKnownDeviceAddress, device::BluetoothUUID(kServiceId),
+      base::BindLambdaForTesting(
+          [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
+            EXPECT_FALSE(connect_to_service_result);
+            run_loop.Quit();
+          }));
+  run_loop.Run();
+}
+
 TEST_F(AdapterTest, TestConnectToServiceInsecurely_KnownDevice_Success) {
   EXPECT_CALL(
       *mock_known_bluetooth_device_,
       ConnectToServiceInsecurely(device::BluetoothUUID(kServiceId), _, _))
       .WillOnce(RunOnceCallback<1>(mock_bluetooth_socket_));
 
+  adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
+
   base::RunLoop run_loop;
   adapter_->ConnectToServiceInsecurely(
       kKnownDeviceAddress, device::BluetoothUUID(kServiceId),
@@ -215,6 +232,8 @@
       ConnectToServiceInsecurely(device::BluetoothUUID(kServiceId), _, _))
       .WillOnce(RunOnceCallback<2>("Error"));
 
+  adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
+
   base::RunLoop run_loop;
   adapter_->ConnectToServiceInsecurely(
       kKnownDeviceAddress, device::BluetoothUUID(kServiceId),
@@ -241,6 +260,8 @@
               IsGattServicesDiscoveryComplete())
       .WillOnce(Return(true));
 
+  adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
+
   base::RunLoop run_loop;
   adapter_->ConnectToServiceInsecurely(
       kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
@@ -279,6 +300,8 @@
                       Return(false)))
       .WillRepeatedly(Return(true));
 
+  adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
+
   base::RunLoop run_loop;
   adapter_->ConnectToServiceInsecurely(
       kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
@@ -295,6 +318,8 @@
               ConnectDevice(kUnknownDeviceAddress, _, _, _))
       .WillOnce(RunOnceCallback<3>());
 
+  adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
+
   base::RunLoop run_loop;
   adapter_->ConnectToServiceInsecurely(
       kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
@@ -307,6 +332,8 @@
 }
 #else
 TEST_F(AdapterTest, TestConnectToServiceInsecurely_UnknownDevice) {
+  adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
+
   base::RunLoop run_loop;
   adapter_->ConnectToServiceInsecurely(
       kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
@@ -319,4 +346,54 @@
 }
 #endif
 
+TEST_F(AdapterTest, TestCreateRfcommServiceInsecurely_DisallowedUuid) {
+  // Do not call Adapter::AllowConnectionsForUuid();
+
+  base::RunLoop run_loop;
+  adapter_->CreateRfcommServiceInsecurely(
+      kServiceName, device::BluetoothUUID(kServiceId),
+      base::BindLambdaForTesting(
+          [&](mojo::PendingRemote<mojom::ServerSocket> pending_server_socket) {
+            EXPECT_FALSE(pending_server_socket);
+            run_loop.Quit();
+          }));
+  run_loop.Run();
+}
+
+TEST_F(AdapterTest, TestCreateRfcommServiceInsecurely_Error) {
+  EXPECT_CALL(*mock_bluetooth_adapter_,
+              CreateRfcommService(device::BluetoothUUID(kServiceId), _, _, _))
+      .WillOnce(RunOnceCallback<3>("Error"));
+
+  adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
+
+  base::RunLoop run_loop;
+  adapter_->CreateRfcommServiceInsecurely(
+      kServiceName, device::BluetoothUUID(kServiceId),
+      base::BindLambdaForTesting(
+          [&](mojo::PendingRemote<mojom::ServerSocket> pending_server_socket) {
+            EXPECT_FALSE(pending_server_socket);
+            run_loop.Quit();
+          }));
+  run_loop.Run();
+}
+
+TEST_F(AdapterTest, TestCreateRfcommServiceInsecurely_Success) {
+  EXPECT_CALL(*mock_bluetooth_adapter_,
+              CreateRfcommService(device::BluetoothUUID(kServiceId), _, _, _))
+      .WillOnce(RunOnceCallback<2>(mock_bluetooth_socket_));
+
+  adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
+
+  base::RunLoop run_loop;
+  adapter_->CreateRfcommServiceInsecurely(
+      kServiceName, device::BluetoothUUID(kServiceId),
+      base::BindLambdaForTesting(
+          [&](mojo::PendingRemote<mojom::ServerSocket> pending_server_socket) {
+            EXPECT_TRUE(pending_server_socket);
+            run_loop.Quit();
+          }));
+  run_loop.Run();
+}
+
 }  // namespace bluetooth
diff --git a/device/vr/public/mojom/vr_service.mojom b/device/vr/public/mojom/vr_service.mojom
index 2fa8d92..b867178 100644
--- a/device/vr/public/mojom/vr_service.mojom
+++ b/device/vr/public/mojom/vr_service.mojom
@@ -111,7 +111,7 @@
 };
 
 struct XRTrackedImage {
-  skia.mojom.BitmapWithArbitraryBpp bitmap;
+  skia.mojom.BitmapN32 bitmap;
   gfx.mojom.Size size_in_pixels;
   float width_in_meters;
 };
diff --git a/fuchsia/engine/browser/web_engine_content_browser_client.cc b/fuchsia/engine/browser/web_engine_content_browser_client.cc
index d894b24..99f470d 100644
--- a/fuchsia/engine/browser/web_engine_content_browser_client.cc
+++ b/fuchsia/engine/browser/web_engine_content_browser_client.cc
@@ -8,7 +8,6 @@
 #include <string>
 #include <utility>
 
-#include "base/command_line.h"
 #include "base/stl_util.h"
 #include "base/strings/string_split.h"
 #include "components/version_info/version_info.h"
@@ -103,22 +102,13 @@
 }
 
 std::string WebEngineContentBrowserClient::GetUserAgent() {
-  std::string user_agent;
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kUseLegacyAndroidUserAgent)) {
-    user_agent =
-        content::BuildUserAgentFromOSAndProduct("Linux; Android", GetProduct());
-  } else {
-    user_agent = content::BuildUserAgentFromProduct(GetProduct());
-  }
-
+  std::string user_agent = content::BuildUserAgentFromProduct(GetProduct());
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kUserAgentProductAndVersion)) {
     user_agent +=
         " " + base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(
                   switches::kUserAgentProductAndVersion);
   }
-
   return user_agent;
 }
 
diff --git a/fuchsia/engine/context_provider_impl.cc b/fuchsia/engine/context_provider_impl.cc
index bc9d584..352b67d4 100644
--- a/fuchsia/engine/context_provider_impl.cc
+++ b/fuchsia/engine/context_provider_impl.cc
@@ -169,7 +169,6 @@
       switches::kForceMaxTextureSize,
       switches::kMaxDecodedImageSizeMb,
       switches::kRendererProcessLimit,
-      switches::kUseLegacyAndroidUserAgent,
       switches::kWebglAntialiasingMode,
       switches::kWebglMSAASampleCount,
   };
diff --git a/fuchsia/engine/switches.cc b/fuchsia/engine/switches.cc
index 7e9c6a9a..09c9420a 100644
--- a/fuchsia/engine/switches.cc
+++ b/fuchsia/engine/switches.cc
@@ -19,7 +19,6 @@
 const char kEnableCastStreamingReceiver[] = "enable-cast-streaming-receiver";
 const char kCdmDataDirectory[] = "cdm-data-directory";
 const char kCdmDataQuotaBytes[] = "cdm-data-quota-bytes";
-const char kUseLegacyAndroidUserAgent[] = "use-legacy-android-user-agent";
 const char kDataQuotaBytes[] = "data-quota-bytes";
 
 }  // namespace switches
diff --git a/fuchsia/engine/switches.h b/fuchsia/engine/switches.h
index b851228..5a020301 100644
--- a/fuchsia/engine/switches.h
+++ b/fuchsia/engine/switches.h
@@ -56,9 +56,6 @@
 // Quota to apply to the CDM user data directory, in bytes.
 extern const char kCdmDataQuotaBytes[];
 
-// Enables reporting of an Android-like User Agent string.
-extern const char kUseLegacyAndroidUserAgent[];
-
 // Soft quota to apply to the Context's persistent data directory, in bytes.
 extern const char kDataQuotaBytes[];
 
diff --git a/fuchsia/engine/web_engine_integration_logging_test.cc b/fuchsia/engine/web_engine_integration_logging_test.cc
index 2dffdbbf..a4acf58 100644
--- a/fuchsia/engine/web_engine_integration_logging_test.cc
+++ b/fuchsia/engine/web_engine_integration_logging_test.cc
@@ -19,8 +19,26 @@
 
 constexpr char kLogTestPageFileName[] = "console_logging.html";
 constexpr char kWebEngineLogTag[] = "web_engine_exe";
-constexpr char kNormalizedLineNumber[] = "123";
-constexpr char kNormalizedPortNumber[] = "456";
+constexpr char kNormalizedLineNumber[] = "12345";
+constexpr char kNormalizedPortNumber[] = "678";
+
+// Replaces the line number in frame_impl.cc with kNormalizedLineNumber and
+// the port with kNormalizedPortNumber to enable reliable comparison of
+// console log messages.
+std::string NormalizeConsoleLogMessage(base::StringPiece original) {
+  size_t line_number_begin = original.find("(") + 1;
+  size_t close_parenthesis = original.find(")", line_number_begin);
+  std::string normalized = original.as_string().replace(
+      line_number_begin, close_parenthesis - line_number_begin,
+      kNormalizedLineNumber);
+
+  const char kSchemePortColon[] = "http://127.0.0.1:";
+  size_t port_begin =
+      normalized.find(kSchemePortColon) + strlen(kSchemePortColon);
+  size_t path_begin = normalized.find("/", port_begin);
+  return normalized.replace(port_begin, path_begin - port_begin,
+                            kNormalizedPortNumber);
+}
 
 }  // namespace
 
@@ -132,24 +150,6 @@
             .spec()));
   }
 
-  // Replaces the line number in frame_impl.cc with kNormalizedLineNumber and
-  // the port with kNormalizedPortNumber to enable reliable comparison of
-  // console log messages.
-  std::string NormalizeConsoleLogMessage(base::StringPiece original) {
-    size_t line_number_begin = original.find("(") + 1;
-    size_t close_parenthesis = original.find(")", line_number_begin);
-
-    const char kSchemePortColon[] = "http://127.0.0.1:";
-    size_t port_begin =
-        original.find(kSchemePortColon) + strlen(kSchemePortColon);
-    size_t path_begin = original.find("/", port_begin);
-
-    return original.as_string()
-        .replace(line_number_begin, close_parenthesis - line_number_begin,
-                 kNormalizedLineNumber)
-        .replace(port_begin, path_begin - port_begin, kNormalizedPortNumber);
-  }
-
   fuchsia::sys::ComponentControllerPtr archivist_controller_;
   sys::ServiceDirectory isolated_archivist_service_dir_;
 
diff --git a/fuchsia/engine/web_engine_integration_test.cc b/fuchsia/engine/web_engine_integration_test.cc
index 02d93b2..18c2d9d4 100644
--- a/fuchsia/engine/web_engine_integration_test.cc
+++ b/fuchsia/engine/web_engine_integration_test.cc
@@ -71,7 +71,7 @@
   std::unique_ptr<media::FakeAudioConsumerService> fake_audio_consumer_service_;
 };
 
-class WebEngineIntegrationUserAgentTest : public WebEngineIntegrationTestBase {
+class WebEngineIntegrationUserAgentTest : public WebEngineIntegrationTest {
  protected:
   GURL GetEchoUserAgentUrl() {
     static std::string echo_user_agent_header_path =
@@ -81,8 +81,6 @@
 };
 
 TEST_F(WebEngineIntegrationUserAgentTest, ValidProductOnly) {
-  StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM));
-
   // Create a Context with just an embedder product specified.
   fuchsia::web::CreateContextParams create_params = DefaultContextParams();
   create_params.set_user_agent_product(kValidUserAgentProduct);
@@ -95,19 +93,12 @@
       ExecuteJavaScriptWithStringResult("document.body.innerText;");
   EXPECT_TRUE(result.find(kValidUserAgentProduct) != std::string::npos);
 
-  // The UserAgent header should also include the desktop-ish Fuchsia
-  // platform & OS information, by default.
-  constexpr char kFuchsiaUserAgentContents[] = "(X11; Fuchsia)";
-  EXPECT_TRUE(result.find(kFuchsiaUserAgentContents) != std::string::npos);
-
   // Query & verify that the navigator.userAgent contains the product tag.
   result = ExecuteJavaScriptWithStringResult("navigator.userAgent;");
   EXPECT_TRUE(result.find(kValidUserAgentProduct) != std::string::npos);
 }
 
 TEST_F(WebEngineIntegrationUserAgentTest, ValidProductAndVersion) {
-  StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM));
-
   // Create a Context with both product and version specified.
   fuchsia::web::CreateContextParams create_params = DefaultContextParams();
   create_params.set_user_agent_product(kValidUserAgentProduct);
@@ -129,8 +120,6 @@
 }
 
 TEST_F(WebEngineIntegrationUserAgentTest, InvalidProduct) {
-  StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM));
-
   // Try to create a Context with an invalid embedder product tag.
   fuchsia::web::CreateContextParams create_params = DefaultContextParams();
   create_params.set_user_agent_product(kInvalidUserAgentProduct);
@@ -138,8 +127,6 @@
 }
 
 TEST_F(WebEngineIntegrationUserAgentTest, VersionOnly) {
-  StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM));
-
   // Try to create a Context with an embedder version but no product.
   fuchsia::web::CreateContextParams create_params = DefaultContextParams();
   create_params.set_user_agent_version(kValidUserAgentVersion);
@@ -147,8 +134,6 @@
 }
 
 TEST_F(WebEngineIntegrationUserAgentTest, ValidProductAndInvalidVersion) {
-  StartWebEngine(base::CommandLine(base::CommandLine::NO_PROGRAM));
-
   // Try to create a Context with valid product tag, but invalid version.
   fuchsia::web::CreateContextParams create_params = DefaultContextParams();
   create_params.set_user_agent_product(kValidUserAgentProduct);
@@ -156,25 +141,6 @@
   CreateContextAndExpectError(std::move(create_params), ZX_ERR_INVALID_ARGS);
 }
 
-TEST_F(WebEngineIntegrationUserAgentTest, UseLegacyAndroidUserAgent) {
-  base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
-  command_line.AppendSwitch("use-legacy-android-user-agent");
-  StartWebEngine(std::move(command_line));
-
-  // Create a Context with both product and version specified.
-  fuchsia::web::CreateContextParams create_params = DefaultContextParams();
-  CreateContextAndFrameAndLoadUrl(std::move(create_params),
-                                  GetEchoUserAgentUrl());
-
-  // Query & verify that the UserAgent header echoed into the document body
-  // contains the legacy Android data.
-  constexpr char kLegacyAndroidUserAgentContents[] = "(Linux; Android)";
-  std::string result =
-      ExecuteJavaScriptWithStringResult("document.body.innerText;");
-  EXPECT_TRUE(result.find(kLegacyAndroidUserAgentContents) !=
-              std::string::npos);
-}
-
 // Check that if the CreateContextParams has |remote_debugging_port| set then:
 // - DevTools becomes available when the first debuggable Frame is created.
 // - DevTools closes when the last debuggable Frame is closed.
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index b9e0e53..c1b4481 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -27192,12 +27192,11 @@
       name: "chromeos-arm-generic-rel"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
-      dimensions: "builderless:1"
+      dimensions: "builder:chromeos-arm-generic-rel"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-16.04"
       dimensions: "pool:luci.chromium.try"
-      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
@@ -34044,12 +34043,11 @@
       name: "linux-libfuzzer-asan-rel"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
-      dimensions: "builderless:1"
+      dimensions: "builder:linux-libfuzzer-asan-rel"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-16.04"
       dimensions: "pool:luci.chromium.try"
-      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index db0df76d..40853b0 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -661,6 +661,7 @@
 try_.chromium_chromiumos_builder(
     name = "chromeos-arm-generic-rel",
     branch_selector = branches.ALL_BRANCHES,
+    builderless = not settings.is_master,
     main_list_view = "try",
     tryjob = try_.job(),
 )
@@ -991,6 +992,7 @@
 try_.chromium_linux_builder(
     name = "linux-libfuzzer-asan-rel",
     branch_selector = branches.STANDARD_MILESTONE,
+    builderless = not settings.is_master,
     executable = "recipe:chromium_libfuzzer_trybot",
     main_list_view = "try",
     tryjob = try_.job(),
diff --git a/ios/chrome/browser/flags/BUILD.gn b/ios/chrome/browser/flags/BUILD.gn
index e29b443..61253fab 100644
--- a/ios/chrome/browser/flags/BUILD.gn
+++ b/ios/chrome/browser/flags/BUILD.gn
@@ -57,6 +57,7 @@
     "//ios/chrome/browser/ui/content_suggestions:feature_flags",
     "//ios/chrome/browser/ui/fullscreen:feature_flags",
     "//ios/chrome/browser/ui/infobars:feature_flags",
+    "//ios/chrome/browser/ui/ntp:feature_flags",
     "//ios/chrome/browser/ui/tab_switcher/tab_grid:features",
     "//ios/chrome/browser/ui/table_view:feature_flags",
     "//ios/chrome/browser/ui/toolbar_container:feature_flags",
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 0d1f13d..2be2f22 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -68,6 +68,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h"
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_features.h"
 #import "ios/chrome/browser/ui/infobars/infobar_feature.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/features.h"
 #import "ios/chrome/browser/ui/table_view/feature_flags.h"
 #import "ios/chrome/browser/ui/toolbar_container/toolbar_container_features.h"
@@ -488,6 +489,9 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(kDiscoverFeedInNtp,
                                     kDiscoverFeedInNtpVariations,
                                     "IOSDiscoverFeed")},
+    {"refactored-ntp", flag_descriptions::kRefactoredNTPName,
+     flag_descriptions::kRefactoredNTPDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(kRefactoredNTP)},
     {"autofill-enable-card-nickname-upstream",
      flag_descriptions::kAutofillEnableCardNicknameUpstreamName,
      flag_descriptions::kAutofillEnableCardNicknameUpstreamDescription,
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index cd467fe..ec99cf9 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -350,6 +350,11 @@
     "Enables pointer support on tablets on iOS 13.4 and above.";
 #endif  // defined(__IPHONE_13_4)
 
+const char kRefactoredNTPName[] = "Enables refactored new tab page";
+const char kRefactoredNTPDescription[] =
+    "When enabled, the new tab page is replaced with the refactored version, "
+    "which changes the ownership and containment of views.";
+
 const char kRestoreGaiaCookiesIfDeletedName[] =
     "Restore GAIA cookies if deleted";
 const char kRestoreGaiaCookiesIfDeletedDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index fdfb0e31..c16db8e 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -312,6 +312,10 @@
 extern const char kPointerSupportDescription[];
 #endif  // defined(__IPHONE_13_4)
 
+// Title and description for the flag that enables the refactored new tab page.
+extern const char kRefactoredNTPName[];
+extern const char kRefactoredNTPDescription[];
+
 // Title and description for the flag that makes Safe Browsing available.
 extern const char kSafeBrowsingAvailableName[];
 extern const char kSafeBrowsingAvailableDescription[];
diff --git a/ios/chrome/browser/translate/translate_app_interface.mm b/ios/chrome/browser/translate/translate_app_interface.mm
index 600b513cb..ba3d02a 100644
--- a/ios/chrome/browser/translate/translate_app_interface.mm
+++ b/ios/chrome/browser/translate/translate_app_interface.mm
@@ -252,8 +252,8 @@
   std::unique_ptr<translate::TranslatePrefs> prefs(
       ChromeIOSTranslateClient::CreateTranslatePrefs(
           chrome_test_util::GetOriginalBrowserState()->GetPrefs()));
-  return prefs->IsLanguagePairWhitelisted(base::SysNSStringToUTF8(source),
-                                          base::SysNSStringToUTF8(target));
+  return prefs->IsLanguagePairOnAlwaysTranslateList(
+      base::SysNSStringToUTF8(source), base::SysNSStringToUTF8(target));
 }
 
 + (BOOL)isBlockedLanguage:(NSString*)language {
@@ -267,7 +267,7 @@
   std::unique_ptr<translate::TranslatePrefs> prefs(
       ChromeIOSTranslateClient::CreateTranslatePrefs(
           chrome_test_util::GetOriginalBrowserState()->GetPrefs()));
-  return prefs->IsSiteBlacklisted(base::SysNSStringToUTF8(hostName));
+  return prefs->IsSiteOnNeverPromptList(base::SysNSStringToUTF8(hostName));
 }
 
 + (int)infobarAutoAlwaysThreshold {
diff --git a/ios/chrome/browser/ui/ntp/BUILD.gn b/ios/chrome/browser/ui/ntp/BUILD.gn
index d4b4ab3..45fa2a9d 100644
--- a/ios/chrome/browser/ui/ntp/BUILD.gn
+++ b/ios/chrome/browser/ui/ntp/BUILD.gn
@@ -19,6 +19,7 @@
   ]
   configs += [ "//build/config/compiler:enable_arc" ]
   deps = [
+    ":feature_flags",
     ":ntp",
     ":ntp_internal",
     "//ios/chrome/browser/browser_state",
@@ -56,6 +57,8 @@
     "incognito_view_controller.mm",
     "metrics.h",
     "metrics.mm",
+    "new_tab_page_view_controller.h",
+    "new_tab_page_view_controller.mm",
     "notification_promo_whats_new.h",
     "notification_promo_whats_new.mm",
     "ntp_tile_saver.h",
@@ -150,6 +153,7 @@
   ]
   deps = [
     ":coordinator",
+    ":feature_flags",
     ":ntp",
     ":ntp_internal",
     "//base",
@@ -231,3 +235,12 @@
     "//ios/chrome/browser/ui/commands",
   ]
 }
+
+source_set("feature_flags") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "new_tab_page_feature.h",
+    "new_tab_page_feature.mm",
+  ]
+  public_deps = [ "//base" ]
+}
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
index c1ebaac2..8ff3efdd 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
@@ -15,6 +15,8 @@
 #import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
 #import "ios/chrome/browser/ui/main/scene_state_observer.h"
 #import "ios/chrome/browser/ui/ntp/incognito_view_controller.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h"
 #import "ios/chrome/browser/url_loading/url_loading_browser_agent.h"
 #import "ios/web/public/navigation/navigation_context.h"
 #import "ios/web/public/navigation/navigation_item.h"
@@ -31,7 +33,10 @@
 @property(nonatomic, strong)
     ContentSuggestionsCoordinator* contentSuggestionsCoordinator;
 
-// View controller for incognito.
+// View controller for the regular NTP.
+@property(nonatomic, strong) NewTabPageViewController* ntpViewController;
+
+// View controller for the incognito NTP.
 @property(nonatomic, strong) IncognitoViewController* incognitoViewController;
 
 // The timetick of the last time the NTP was displayed.
@@ -83,6 +88,11 @@
         self.panGestureHandler;
 
     [self.contentSuggestionsCoordinator start];
+
+    self.ntpViewController = [[NewTabPageViewController alloc]
+        initWithContentSuggestionsViewController:
+            self.contentSuggestionsCoordinator.viewController];
+
     base::RecordAction(base::UserMetricsAction("MobileNTPShowMostVisited"));
     SceneState* sceneState =
         SceneStateBrowserAgent::FromBrowser(self.browser)->GetSceneState();
@@ -139,7 +149,9 @@
   if (self.browser->GetBrowserState()->IsOffTheRecord()) {
     return self.incognitoViewController;
   } else {
-    return self.contentSuggestionsCoordinator.viewController;
+    return IsRefactoredNTP()
+               ? self.ntpViewController
+               : self.contentSuggestionsCoordinator.viewController;
   }
 }
 
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator_unittest.mm b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator_unittest.mm
index f0cdc9b2..63ba1c1a 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator_unittest.mm
@@ -21,6 +21,8 @@
 #import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
 #import "ios/chrome/browser/ui/ntp/incognito_view_controller.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_controller_delegate.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h"
 #include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_state_manager.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #include "ios/web/public/test/web_task_environment.h"
@@ -101,8 +103,13 @@
                    forProtocol:@protocol(SnackbarCommands)];
   [coordinator_ start];
   UIViewController* viewController = [coordinator_ viewController];
-  EXPECT_TRUE(
-      [viewController isKindOfClass:[ContentSuggestionsViewController class]]);
+  if (IsRefactoredNTP()) {
+    EXPECT_TRUE(
+        [viewController isKindOfClass:[NewTabPageViewController class]]);
+  } else {
+    EXPECT_TRUE([viewController
+        isKindOfClass:[ContentSuggestionsViewController class]]);
+  }
   [coordinator_ stop];
 }
 
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_feature.h b/ios/chrome/browser/ui/ntp/new_tab_page_feature.h
new file mode 100644
index 0000000..45164a9
--- /dev/null
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_feature.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 IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_FEATURE_H_
+#define IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_FEATURE_H_
+
+#include "base/feature_list.h"
+
+// Feature to choose between the legacy new tab page or the refactored one.
+// Use IsRefactoredNTP() instead of this constant directly.
+extern const base::Feature kRefactoredNTP;
+
+// Whether the refactored NTP is used instead of the legacy one.
+bool IsRefactoredNTP();
+
+#endif  // IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_FEATURE_H_
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_feature.mm b/ios/chrome/browser/ui/ntp/new_tab_page_feature.mm
new file mode 100644
index 0000000..7922da2
--- /dev/null
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_feature.mm
@@ -0,0 +1,18 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Feature disabled by default to keep the legacy NTP until the refactored one
+// covers all existing functionality.
+const base::Feature kRefactoredNTP{"RefactoredNTP",
+                                   base::FEATURE_DISABLED_BY_DEFAULT};
+
+bool IsRefactoredNTP() {
+  return base::FeatureList::IsEnabled(kRefactoredNTP);
+}
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
new file mode 100644
index 0000000..6630327
--- /dev/null
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
@@ -0,0 +1,25 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_VIEW_CONTROLLER_H_
+
+#import <UIKit/UIKit.h>
+
+// View controller containing all the content presented on a standard,
+// non-incognito new tab page.
+@interface NewTabPageViewController : UIViewController
+
+// Initializes view controller with content suggestions.
+- (instancetype)initWithContentSuggestionsViewController:
+    (UIViewController*)contentSuggestionsViewController
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithNibName:(NSString*)name
+                         bundle:(NSBundle*)bundle NS_UNAVAILABLE;
+- (instancetype)initWithCoder:(NSCoder*)coder NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
new file mode 100644
index 0000000..460d1e0
--- /dev/null
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
@@ -0,0 +1,45 @@
+// 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 <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h"
+
+#import "ios/chrome/common/ui/util/constraints_ui_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface NewTabPageViewController ()
+
+// View controller representing the NTP content suggestions.
+@property(nonatomic, strong) UIViewController* contentSuggestionsViewController;
+
+@end
+
+@implementation NewTabPageViewController
+
+- (instancetype)initWithContentSuggestionsViewController:
+    (UIViewController*)contentSuggestionsViewController {
+  self = [super initWithNibName:nil bundle:nil];
+  if (self) {
+    _contentSuggestionsViewController = contentSuggestionsViewController;
+  }
+
+  return self;
+}
+
+- (void)viewDidLoad {
+  [super viewDidLoad];
+  [self addChildViewController:self.contentSuggestionsViewController];
+  [self.view addSubview:self.contentSuggestionsViewController.view];
+  [self.contentSuggestionsViewController didMoveToParentViewController:self];
+
+  self.contentSuggestionsViewController.view
+      .translatesAutoresizingMaskIntoConstraints = NO;
+  AddSameConstraints(self.contentSuggestionsViewController.view, self.view);
+}
+
+@end
diff --git a/ios/chrome/browser/ui/settings/translate_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/translate_table_view_controller_unittest.mm
index fb15f60..9ce8917 100644
--- a/ios/chrome/browser/ui/settings/translate_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/translate_table_view_controller_unittest.mm
@@ -94,13 +94,13 @@
   // Set some preferences.
   std::unique_ptr<translate::TranslatePrefs> translate_prefs(
       ChromeIOSTranslateClient::CreateTranslatePrefs(pref_service_.get()));
-  translate_prefs->BlacklistSite(kBlockedSite);
-  ASSERT_TRUE(translate_prefs->IsSiteBlacklisted(kBlockedSite));
+  translate_prefs->AddSiteToNeverPromptList(kBlockedSite);
+  ASSERT_TRUE(translate_prefs->IsSiteOnNeverPromptList(kBlockedSite));
   translate_prefs->AddToLanguageList(kLanguage1, /*force_blocked=*/true);
   ASSERT_TRUE(translate_prefs->IsBlockedLanguage(kLanguage1));
-  translate_prefs->WhitelistLanguagePair(kLanguage1, kLanguage2);
-  ASSERT_TRUE(
-      translate_prefs->IsLanguagePairWhitelisted(kLanguage1, kLanguage2));
+  translate_prefs->AddLanguagePairToAlwaysTranslateList(kLanguage1, kLanguage2);
+  ASSERT_TRUE(translate_prefs->IsLanguagePairOnAlwaysTranslateList(kLanguage1,
+                                                                   kLanguage2));
   // Reset the preferences through the UI.
   CreateController();
   TranslateTableViewController* controller =
@@ -109,10 +109,10 @@
   [controller tableView:controller.tableView
       didSelectRowAtIndexPath:[NSIndexPath indexPathForItem:1 inSection:0]];
   // Check that preferences are gone.
-  EXPECT_FALSE(translate_prefs->IsSiteBlacklisted(kBlockedSite));
+  EXPECT_FALSE(translate_prefs->IsSiteOnNeverPromptList(kBlockedSite));
   EXPECT_FALSE(translate_prefs->IsBlockedLanguage(kLanguage1));
-  EXPECT_FALSE(
-      translate_prefs->IsLanguagePairWhitelisted(kLanguage1, kLanguage2));
+  EXPECT_FALSE(translate_prefs->IsLanguagePairOnAlwaysTranslateList(
+      kLanguage1, kLanguage2));
 }
 
 }  // namespace
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
index 0b11282..79d08db 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-2b3d204e5918d247b8e90f73e12835955dcc58d5
\ No newline at end of file
+1f29c01fcd8536a960c41e59b2f2895db5e0e934
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
index 9e1deefc..7c7e4a0 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-1315245f706a6c58602a83f60bf2a1ad1220a44d
\ No newline at end of file
+e87b0850b41c2c2320b941cebac1235f45eb5341
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
index ecc56cc..6d351544 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-562263f237c7382847b69ea22c1b0dba5853073f
\ No newline at end of file
+8da2fa5876d6c33918a8813896ea4732d55e1b80
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
index b242833..82fb39d 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-1389d75fb3829ebedeae6955b19b41a21dcd93e1
\ No newline at end of file
+a33019a2d2e360621a84dabb62b84b913aa5a70e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
index ea84dcf..203ab4412 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-2980294d0e3dcd2d0bf77b9d0141398d109b0246
\ No newline at end of file
+c1e238436617a03dc73071f2941b6ab9679afa67
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
index 04bd590..b4f453a1 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-9453ec87331f63de623d0482efa16c3036c7ca86
\ No newline at end of file
+8881d8a684e5affd5f19aabe79b80ce0e2e64a25
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
index 68ca5c1a..837d062 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-5dfe95b60108c50d96450b9b22a1b997b7dd8ca7
\ No newline at end of file
+02e75aaa90d4003bfd3a23bd4a0d87b45d841bb2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
index 6596e1c..3c089fb 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-0a0913037c289c2a95507ace0e373259b847fc2f
\ No newline at end of file
+8e5df053811ae750948aa3eb646e80a13913763d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
index 4c6355b..5e9e473d 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-ce6382cadc89ca61b10f8e6b2ab2c18b88af2b5f
\ No newline at end of file
+9f628c8c45c31ca57dd1c93fb7a539f2a35418fe
\ No newline at end of file
diff --git a/ios/web_view/internal/translate/cwv_translation_controller.mm b/ios/web_view/internal/translate/cwv_translation_controller.mm
index 62b03d3..18a1852 100644
--- a/ios/web_view/internal/translate/cwv_translation_controller.mm
+++ b/ios/web_view/internal/translate/cwv_translation_controller.mm
@@ -199,8 +199,8 @@
   switch (policy.type) {
     case CWVTranslationPolicyAsk: {
       _translatePrefs->UnblockLanguage(languageCode);
-      _translatePrefs->RemoveLanguagePairFromWhitelist(languageCode,
-                                                       std::string());
+      _translatePrefs->RemoveLanguagePairFromAlwaysTranslateList(languageCode,
+                                                                 std::string());
       break;
     }
     case CWVTranslationPolicyNever: {
@@ -210,7 +210,7 @@
     }
     case CWVTranslationPolicyAuto: {
       _translatePrefs->UnblockLanguage(languageCode);
-      _translatePrefs->WhitelistLanguagePair(
+      _translatePrefs->AddLanguagePairToAlwaysTranslateList(
           languageCode, base::SysNSStringToUTF8(policy.language.languageCode));
       break;
     }
@@ -242,12 +242,13 @@
   DCHECK(pageHost.length);
   switch (policy.type) {
     case CWVTranslationPolicyAsk: {
-      _translatePrefs->RemoveSiteFromBlacklist(
+      _translatePrefs->RemoveSiteFromNeverPromptList(
           base::SysNSStringToUTF8(pageHost));
       break;
     }
     case CWVTranslationPolicyNever: {
-      _translatePrefs->BlacklistSite(base::SysNSStringToUTF8(pageHost));
+      _translatePrefs->AddSiteToNeverPromptList(
+          base::SysNSStringToUTF8(pageHost));
       break;
     }
     case CWVTranslationPolicyAuto: {
@@ -260,9 +261,9 @@
 
 - (CWVTranslationPolicy*)translationPolicyForPageHost:(NSString*)pageHost {
   // TODO(crbug.com/706289): Return translationPolicyAuto when implemented.
-  bool isSiteBlackListed =
-      _translatePrefs->IsSiteBlacklisted(base::SysNSStringToUTF8(pageHost));
-  if (isSiteBlackListed) {
+  bool isSiteOnNeverPromptList = _translatePrefs->IsSiteOnNeverPromptList(
+      base::SysNSStringToUTF8(pageHost));
+  if (isSiteOnNeverPromptList) {
     return [CWVTranslationPolicy translationPolicyNever];
   }
   return [CWVTranslationPolicy translationPolicyAsk];
diff --git a/ios/web_view/internal/translate/cwv_translation_controller_unittest.mm b/ios/web_view/internal/translate/cwv_translation_controller_unittest.mm
index d12df31..cb6af4a 100644
--- a/ios/web_view/internal/translate/cwv_translation_controller_unittest.mm
+++ b/ios/web_view/internal/translate/cwv_translation_controller_unittest.mm
@@ -99,11 +99,11 @@
     pref_service_.registry()->RegisterBooleanPref(prefs::kOfferTranslateEnabled,
                                                   true);
     pref_service_.registry()->RegisterListPref(
-        translate::TranslatePrefs::kPrefTranslateSiteBlacklistDeprecated);
+        translate::TranslatePrefs::kPrefNeverPromptSitesDeprecated);
     pref_service_.registry()->RegisterDictionaryPref(
-        translate::TranslatePrefs::kPrefTranslateSiteBlacklistWithTime);
+        translate::TranslatePrefs::kPrefNeverPromptSitesWithTime);
     pref_service_.registry()->RegisterDictionaryPref(
-        translate::TranslatePrefs::kPrefTranslateWhitelists);
+        translate::TranslatePrefs::kPrefAlwaysTranslateLists);
     pref_service_.registry()->RegisterDictionaryPref(
         translate::TranslatePrefs::kPrefTranslateDeniedCount);
     pref_service_.registry()->RegisterDictionaryPref(
@@ -254,13 +254,14 @@
   CWVTranslationPolicy* policy = [CWVTranslationPolicy translationPolicyNever];
   [translation_controller_ setTranslationPolicy:policy
                                     forPageHost:kTestPageHost];
-  EXPECT_TRUE(translate_prefs_->IsSiteBlacklisted(
+  EXPECT_TRUE(translate_prefs_->IsSiteOnNeverPromptList(
       base::SysNSStringToUTF8(kTestPageHost)));
 }
 
 // Tests CWVTranslationController properly reads page host policies.
 TEST_F(CWVTranslationControllerTest, ReadPageHostPolicy) {
-  translate_prefs_->BlacklistSite(base::SysNSStringToUTF8(kTestPageHost));
+  translate_prefs_->AddSiteToNeverPromptList(
+      base::SysNSStringToUTF8(kTestPageHost));
   CWVTranslationPolicy* policy =
       [translation_controller_ translationPolicyForPageHost:kTestPageHost];
   EXPECT_EQ(CWVTranslationPolicyNever, policy.type);
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
index 480c0ff..d1438ab5 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
@@ -18,7 +18,6 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 
-import org.chromium.base.BuildInfo;
 import org.chromium.base.Log;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
@@ -427,7 +426,7 @@
                 return false;
             }
         } else if (mime.equals(MimeTypes.VIDEO_AV1)) {
-            if (!BuildInfo.isAtLeastQ()) return false;
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) return false;
         }
         // *************************************************************
         // *** DO NOT ADD ANY NEW CODECS WITHOUT UPDATING MIME_UTIL. ***
diff --git a/media/base/supported_types.cc b/media/base/supported_types.cc
index 05de5ab..2bf2fe92 100644
--- a/media/base/supported_types.cc
+++ b/media/base/supported_types.cc
@@ -300,7 +300,8 @@
       return IsColorSpaceSupported(type.color_space);
 #else
 #if defined(OS_ANDROID)
-      if (base::android::BuildInfo::GetInstance()->is_at_least_q() &&
+      if (base::android::BuildInfo::GetInstance()->sdk_int() >=
+              base::android::SDK_VERSION_Q &&
           IsColorSpaceSupported(type.color_space)) {
         return true;
       }
diff --git a/media/capture/video/mac/sample_buffer_transformer_mac_unittest.mm b/media/capture/video/mac/sample_buffer_transformer_mac_unittest.mm
index f481f8b2..a4bf34c 100644
--- a/media/capture/video/mac/sample_buffer_transformer_mac_unittest.mm
+++ b/media/capture/video/mac/sample_buffer_transformer_mac_unittest.mm
@@ -543,7 +543,7 @@
       PixelBufferIsSingleColor(output_pixel_buffer, kColorR, kColorG, kColorB));
 }
 
-TEST_P(SampleBufferTransformerLibyuvTest, CanConvertAndScaleDown) {
+TEST_P(SampleBufferTransformerLibyuvTest, MAYBE_CanConvertAndScaleDown) {
   OSType input_pixel_format;
   OSType output_pixel_format;
   std::tie(input_pixel_format, output_pixel_format) = GetParam();
@@ -673,7 +673,7 @@
       PixelBufferIsSingleColor(output_pixel_buffer, kColorR, kColorG, kColorB));
 }
 
-TEST_P(SampleBufferTransformerMjpegTest, CanConvertAndScaleDown) {
+TEST_P(SampleBufferTransformerMjpegTest, MAYBE_CanConvertAndScaleDown) {
   OSType output_pixel_format = GetParam();
 
   base::ScopedCFTypeRef<CMSampleBufferRef> input_sample_buffer =
diff --git a/media/gpu/vaapi/h264_vaapi_video_decoder_delegate.cc b/media/gpu/vaapi/h264_vaapi_video_decoder_delegate.cc
index b0ea9eb..3a06792 100644
--- a/media/gpu/vaapi/h264_vaapi_video_decoder_delegate.cc
+++ b/media/gpu/vaapi/h264_vaapi_video_decoder_delegate.cc
@@ -366,11 +366,12 @@
 
   bool uses_crypto = false;
   VAEncryptionParameters crypto_params = {};
-  if ((!subsamples.empty() && subsamples[0].cypher_bytes) ||
-      IsProtectedSession()) {
+  const bool encrypted_bytes_present =
+      !subsamples.empty() && subsamples[0].cypher_bytes;
+  if (encrypted_bytes_present || IsProtectedSession()) {
     // If there is only one clear byte, then this is CENC v1, full sample
     // encryption (i.e. only the NALU header is unencrypted).
-    ProtectedSessionState state = SetupDecryptDecode(
+    const ProtectedSessionState state = SetupDecryptDecode(
         !subsamples.empty() && subsamples[0].clear_bytes == 1, size,
         &crypto_params, &encryption_segment_info_, subsamples);
     if (state == ProtectedSessionState::kFailed) {
diff --git a/media/gpu/vaapi/h265_vaapi_video_decoder_delegate.cc b/media/gpu/vaapi/h265_vaapi_video_decoder_delegate.cc
index 72ba400b..5351f92e 100644
--- a/media/gpu/vaapi/h265_vaapi_video_decoder_delegate.cc
+++ b/media/gpu/vaapi/h265_vaapi_video_decoder_delegate.cc
@@ -297,9 +297,10 @@
 
   bool uses_crypto = false;
   VAEncryptionParameters crypto_params = {};
-  if ((!subsamples.empty() && subsamples[0].cypher_bytes) ||
-      IsProtectedSession()) {
-    ProtectedSessionState state =
+  const bool encrypted_bytes_present =
+      !subsamples.empty() && subsamples[0].cypher_bytes;
+  if (encrypted_bytes_present || IsProtectedSession()) {
+    const ProtectedSessionState state =
         SetupDecryptDecode(/*full_sample=*/false, size, &crypto_params,
                            &encryption_segment_info_, subsamples);
     if (state == ProtectedSessionState::kFailed) {
diff --git a/media/gpu/vaapi/vaapi_utils.cc b/media/gpu/vaapi/vaapi_utils.cc
index 450f0c8..876c144 100644
--- a/media/gpu/vaapi/vaapi_utils.cc
+++ b/media/gpu/vaapi/vaapi_utils.cc
@@ -15,6 +15,7 @@
 #include "media/gpu/vaapi/vaapi_wrapper.h"
 #include "media/gpu/vp8_picture.h"
 #include "media/gpu/vp8_reference_frame_vector.h"
+#include "third_party/libva_protected_content/va_protected_content.h"
 
 namespace media {
 
@@ -79,7 +80,7 @@
   DCHECK(lock);
   DCHECK(va_display);
   DCHECK_NE(va_context_id, VA_INVALID_ID);
-  DCHECK_LT(va_buffer_type, VABufferTypeMax);
+  DCHECK(IsValidVABufferType(va_buffer_type));
   DCHECK_NE(size, 0u);
   lock->AssertAcquired();
   unsigned int va_buffer_size;
@@ -347,4 +348,10 @@
   for (size_t i = 0; i < frame_header.num_of_dct_partitions; ++i)
     slice_param->partition_size[i + 1] = frame_header.dct_partition_sizes[i];
 }
+
+bool IsValidVABufferType(VABufferType type) {
+  return type < VABufferTypeMax || type == VAEncryptionParameterBufferType ||
+         type == VACencStatusParameterBufferType;
+}
+
 }  // namespace media
diff --git a/media/gpu/vaapi/vaapi_utils.h b/media/gpu/vaapi/vaapi_utils.h
index 7420256e..9bcee23 100644
--- a/media/gpu/vaapi/vaapi_utils.h
+++ b/media/gpu/vaapi/vaapi_utils.h
@@ -181,6 +181,9 @@
                            VAProbabilityDataBufferVP8* prob_buf,
                            VAPictureParameterBufferVP8* pic_param,
                            VASliceParameterBufferVP8* slice_param);
+
+bool IsValidVABufferType(VABufferType type);
+
 }  // namespace media
 
 #endif  // MEDIA_GPU_VAAPI_VAAPI_UTILS_H_
diff --git a/media/gpu/vaapi/vaapi_video_decoder.cc b/media/gpu/vaapi/vaapi_video_decoder.cc
index a38145a..c54588e0 100644
--- a/media/gpu/vaapi/vaapi_video_decoder.cc
+++ b/media/gpu/vaapi/vaapi_video_decoder.cc
@@ -704,7 +704,7 @@
   if (profile_ >= H264PROFILE_MIN && profile_ <= H264PROFILE_MAX) {
     auto accelerator = std::make_unique<H264VaapiVideoDecoderDelegate>(
         this, vaapi_wrapper_, std::move(protected_update_cb),
-        cdm_context_ref_->GetCdmContext());
+        cdm_context_ref_ ? cdm_context_ref_->GetCdmContext() : nullptr);
     decoder_delegate_ = accelerator.get();
 
     decoder_.reset(
@@ -718,7 +718,7 @@
   } else if (profile_ >= VP9PROFILE_MIN && profile_ <= VP9PROFILE_MAX) {
     auto accelerator = std::make_unique<VP9VaapiVideoDecoderDelegate>(
         this, vaapi_wrapper_, std::move(protected_update_cb),
-        cdm_context_ref_->GetCdmContext());
+        cdm_context_ref_ ? cdm_context_ref_->GetCdmContext() : nullptr);
     decoder_delegate_ = accelerator.get();
 
     decoder_.reset(
@@ -728,7 +728,7 @@
   else if (profile_ >= HEVCPROFILE_MIN && profile_ <= HEVCPROFILE_MAX) {
     auto accelerator = std::make_unique<H265VaapiVideoDecoderDelegate>(
         this, vaapi_wrapper_, std::move(protected_update_cb),
-        cdm_context_ref_->GetCdmContext());
+        cdm_context_ref_ ? cdm_context_ref_->GetCdmContext() : nullptr);
     decoder_delegate_ = accelerator.get();
 
     decoder_.reset(
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index 503c6ce..b91266b8 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -484,11 +484,6 @@
   return false;
 }
 
-bool IsValidVABufferType(VABufferType type) {
-  return type < VABufferTypeMax || type == VAEncryptionParameterBufferType ||
-         type == VACencStatusParameterBufferType;
-}
-
 // This class is a wrapper around its |va_display_| (and its associated
 // |va_lock_|) to guarantee mutual exclusion and singleton behaviour.
 class VADisplayState {
@@ -830,8 +825,16 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   if (mode == VaapiWrapper::kDecodeProtected && profile != VAProfileProtected) {
+    // TODO(jkardatzke): Remove this workarond once the iHD bug for full vs.
+    // subsample dependency here is fixed. VA_ENCRYPTION_TYPE_CTR_128 works for
+    // VP9. VA_ENCRYPTION_TYPE_CENC_CTR is needed for H264 full sample (and also
+    // works for H264 subsample). We can't know full vs. subsample at this point
+    // though, we only know codec.
     required_attribs->push_back(
-        {VAConfigAttribEncryption, VA_ENCRYPTION_TYPE_CENC_CTR});
+        {VAConfigAttribEncryption,
+         (profile == VAProfileVP9Profile0 || profile == VAProfileVP9Profile2)
+             ? VA_ENCRYPTION_TYPE_CTR_128
+             : VA_ENCRYPTION_TYPE_CENC_CTR});
   }
 #endif
 
diff --git a/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.cc b/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.cc
index 0da2be8..f53c94a 100644
--- a/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.cc
+++ b/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.cc
@@ -58,7 +58,6 @@
 
   VADecPictureParameterBufferVP9 pic_param{};
   VASliceParameterBufferVP9 slice_param{};
-  VAEncryptionParameters crypto_param{};
 
   if (!picture_params_) {
     picture_params_ = vaapi_wrapper_->CreateVABuffer(
@@ -83,29 +82,33 @@
   bool uses_crypto = false;
   const DecryptConfig* decrypt_config = pic->decrypt_config();
   std::vector<VAEncryptionSegmentInfo> encryption_segment_info;
-  if (decrypt_config) {
-    if (!SetDecryptConfig(decrypt_config->Clone()))
+  if (decrypt_config && !SetDecryptConfig(decrypt_config->Clone()))
+    return DecodeStatus::kFail;
+
+  VAEncryptionParameters crypto_param{};
+  const bool encrypted_bytes_present =
+      decrypt_config && !decrypt_config->subsamples().empty() &&
+      decrypt_config->subsamples()[0].cypher_bytes;
+  if (encrypted_bytes_present || IsProtectedSession()) {
+    const ProtectedSessionState state = SetupDecryptDecode(
+        /*full_sample=*/false, frame_hdr->frame_size, &crypto_param,
+        &encryption_segment_info,
+        decrypt_config ? decrypt_config->subsamples()
+                       : std::vector<SubsampleEntry>());
+    if (state == ProtectedSessionState::kFailed) {
+      LOG(ERROR)
+          << "SubmitDecode fails because we couldn't setup the protected "
+             "session";
       return DecodeStatus::kFail;
-    if (!decrypt_config->subsamples().empty() &&
-        decrypt_config->subsamples()[0].cypher_bytes) {
-      ProtectedSessionState state = SetupDecryptDecode(
-          false /* full_sample */, frame_hdr->frame_size, &crypto_param,
-          &encryption_segment_info, decrypt_config->subsamples());
-      if (state == ProtectedSessionState::kFailed) {
-        LOG(ERROR)
-            << "SubmitDecode fails because we couldn't setup the protected "
-               "session";
+    } else if (state != ProtectedSessionState::kCreated) {
+      return DecodeStatus::kTryAgain;
+    }
+    uses_crypto = true;
+    if (!crypto_params_) {
+      crypto_params_ = vaapi_wrapper_->CreateVABuffer(
+          VAEncryptionParameterBufferType, sizeof(crypto_param));
+      if (!crypto_params_)
         return DecodeStatus::kFail;
-      } else if (state != ProtectedSessionState::kCreated) {
-        return DecodeStatus::kTryAgain;
-      }
-      uses_crypto = true;
-      if (!crypto_params_) {
-        crypto_params_ = vaapi_wrapper_->CreateVABuffer(
-            VAEncryptionParameterBufferType, sizeof(crypto_param));
-        if (!crypto_params_)
-          return DecodeStatus::kFail;
-      }
     }
   }
 
@@ -242,6 +245,7 @@
   // that will be destroyed soon.
   picture_params_.reset();
   slice_params_.reset();
+  crypto_params_.reset();
 }
 
 }  // namespace media
diff --git a/media/gpu/vp9_decoder.cc b/media/gpu/vp9_decoder.cc
index fda81b91..d10b5a1a 100644
--- a/media/gpu/vp9_decoder.cc
+++ b/media/gpu/vp9_decoder.cc
@@ -135,16 +135,14 @@
   while (true) {
     // If we have a pending picture to decode, try that first.
     if (pending_pic_) {
-      VP9Accelerator::Status status = DecodeAndOutputPicture(pending_pic_);
+      VP9Accelerator::Status status =
+          DecodeAndOutputPicture(std::move(pending_pic_));
       if (status == VP9Accelerator::Status::kFail) {
-        pending_pic_.reset();
         SetError();
         return kDecodeError;
       }
       if (status == VP9Accelerator::Status::kTryAgain)
         return kTryAgain;
-
-      pending_pic_.reset();
     }
 
     // Read a new frame header if one is not awaiting decoding already.
@@ -286,35 +284,32 @@
       return kConfigChange;
     }
 
-    pending_pic_ = accelerator_->CreateVP9Picture();
-    if (!pending_pic_) {
+    scoped_refptr<VP9Picture> pic = accelerator_->CreateVP9Picture();
+    if (!pic) {
       return kRanOutOfSurfaces;
     }
     DVLOG(2) << "Render resolution: " << new_render_rect.ToString();
 
-    pending_pic_->set_visible_rect(new_render_rect);
-    pending_pic_->set_bitstream_id(stream_id_);
+    pic->set_visible_rect(new_render_rect);
+    pic->set_bitstream_id(stream_id_);
 
-    pending_pic_->set_decrypt_config(std::move(decrypt_config));
+    pic->set_decrypt_config(std::move(decrypt_config));
 
     // For VP9, container color spaces override video stream color spaces.
     if (container_color_space_.IsSpecified())
-      pending_pic_->set_colorspace(container_color_space_);
+      pic->set_colorspace(container_color_space_);
     else if (curr_frame_hdr_)
-      pending_pic_->set_colorspace(curr_frame_hdr_->GetColorSpace());
+      pic->set_colorspace(curr_frame_hdr_->GetColorSpace());
 
-    pending_pic_->frame_hdr = std::move(curr_frame_hdr_);
+    pic->frame_hdr = std::move(curr_frame_hdr_);
 
-    VP9Accelerator::Status status = DecodeAndOutputPicture(pending_pic_);
+    VP9Accelerator::Status status = DecodeAndOutputPicture(std::move(pic));
     if (status == VP9Accelerator::Status::kFail) {
-      pending_pic_.reset();
       SetError();
       return kDecodeError;
     }
     if (status == VP9Accelerator::Status::kTryAgain)
       return kTryAgain;
-
-    pending_pic_.reset();
   }
 }
 
@@ -351,8 +346,11 @@
   VP9Accelerator::Status status = accelerator_->SubmitDecode(
       pic, context.segmentation(), context.loop_filter(), ref_frames_,
       std::move(done_cb));
-  if (status != VP9Accelerator::Status::kOk)
+  if (status != VP9Accelerator::Status::kOk) {
+    if (status == VP9Accelerator::Status::kTryAgain)
+      pending_pic_ = std::move(pic);
     return status;
+  }
 
   if (pic->frame_hdr->show_frame) {
     if (!accelerator_->OutputPicture(pic))
diff --git a/net/quic/quic_event_logger.cc b/net/quic/quic_event_logger.cc
index a37a691..7f0154b 100644
--- a/net/quic/quic_event_logger.cc
+++ b/net/quic/quic_event_logger.cc
@@ -174,6 +174,26 @@
     const quic::QuicConnectionCloseFrame* frame) {
   base::Value dict(base::Value::Type::DICTIONARY);
   dict.SetIntKey("quic_error", frame->quic_error_code);
+  if (frame->wire_error_code != frame->quic_error_code) {
+    dict.SetIntKey("quic_wire_error", frame->wire_error_code);
+  }
+  std::string close_type;
+  switch (frame->close_type) {
+    case quic::GOOGLE_QUIC_CONNECTION_CLOSE:
+      close_type = "gQUIC";
+      break;
+    case quic::IETF_QUIC_TRANSPORT_CONNECTION_CLOSE:
+      close_type = "Transport";
+      break;
+    case quic::IETF_QUIC_APPLICATION_CONNECTION_CLOSE:
+      close_type = "Application";
+      break;
+  }
+  dict.SetStringKey("close_type", close_type);
+  if (frame->transport_close_frame_type != 0) {
+    dict.SetKey("transport_close_frame_type",
+                NetLogNumberValue(frame->transport_close_frame_type));
+  }
   dict.SetStringKey("details", frame->error_details);
   return dict;
 }
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index 7792005..ea8eb93 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -5841,7 +5841,8 @@
 
 // Check that we're correctly logging 0-rtt success when the handshake
 // concludes during a Read.
-TEST_F(SSLClientSocketZeroRTTTest, EarlyDataReasonReadServerHello) {
+// Disabled due to flake, see crbug.com/1057921 .
+TEST_F(SSLClientSocketZeroRTTTest, DISABLED_EarlyDataReasonReadServerHello) {
   const char kReasonHistogram[] = "Net.SSLHandshakeEarlyDataReason";
   ASSERT_TRUE(StartServer());
   ASSERT_TRUE(RunInitialConnection());
diff --git a/printing/BUILD.gn b/printing/BUILD.gn
index e71403b..14a948d3 100644
--- a/printing/BUILD.gn
+++ b/printing/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/features.gni")
 import("//build/config/sysroot.gni")
 import("//build/config/ui.gni")
@@ -14,7 +15,7 @@
 if (is_android) {
   import("//build/config/android/rules.gni")
 }
-if (use_cups && is_chromeos) {
+if (use_cups && is_chromeos_ash) {
   import("//printing/backend/tools/code_generator.gni")
 }
 
@@ -28,7 +29,7 @@
 # Enable the CUPS IPP printing backend.
 # TODO(crbug.com/226176): Remove this after CUPS PPD API calls are removed.
 declare_args() {
-  use_cups_ipp = use_cups && !is_linux
+  use_cups_ipp = use_cups && !(is_linux || is_chromeos_lacros)
 }
 
 # Several targets want to include this header file. We separate it out
@@ -37,7 +38,7 @@
   sources = [ "printing_export.h" ]
 }
 
-if (use_cups_ipp && is_chromeos) {
+if (use_cups_ipp && is_chromeos_ash) {
   ipp_handler_map_path = "$target_gen_dir/backend/ipp_handler_map.cc"
 
   ipp_code_generate("ipp_handlers_generate") {
@@ -125,7 +126,7 @@
     ]
   }
 
-  if (is_chromeos) {
+  if (is_chromeos_ash) {
     defines += [ "PRINT_BACKEND_AVAILABLE" ]
 
     sources += [
@@ -136,7 +137,7 @@
     ]
   }
 
-  if (is_linux) {
+  if (is_linux || is_chromeos_lacros) {
     sources += [
       "printed_document_linux.cc",
       "printing_context_linux.cc",
@@ -239,7 +240,7 @@
       ]
     }
 
-    if (is_chromeos) {
+    if (is_chromeos_ash) {
       deps += [ ":ipp_handlers_generate" ]
 
       sources += [
@@ -272,7 +273,7 @@
     }
   }
 
-  if (use_cups_ipp || is_chromeos) {
+  if (use_cups_ipp || is_chromeos_ash) {
     sources += [
       "printer_query_result.h",
       "printer_status.cc",
@@ -381,7 +382,7 @@
       sources += [ "backend/cups_ipp_helper_unittest.cc" ]
     }
 
-    if (is_chromeos) {
+    if (is_chromeos_ash) {
       sources += [ "printing_context_chromeos_unittest.cc" ]
     } else {
       sources += [
diff --git a/printing/backend/cups_ipp_constants.cc b/printing/backend/cups_ipp_constants.cc
index 5981f8d..1473d48 100644
--- a/printing/backend/cups_ipp_constants.cc
+++ b/printing/backend/cups_ipp_constants.cc
@@ -24,7 +24,7 @@
 constexpr char kCollated[] = "separate-documents-collated-copies";
 constexpr char kUncollated[] = "separate-documents-uncollated-copies";
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 
 constexpr char kIppDocumentAttributes[] =
     "document-creation-attributes";                              // PWG 5100.5
@@ -35,6 +35,6 @@
 constexpr char kOptionFalse[] = "false";
 constexpr char kOptionTrue[] = "true";
 
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 }  // namespace printing
diff --git a/printing/backend/cups_ipp_constants.h b/printing/backend/cups_ipp_constants.h
index 83fa9a2..38d1c46a 100644
--- a/printing/backend/cups_ipp_constants.h
+++ b/printing/backend/cups_ipp_constants.h
@@ -25,7 +25,7 @@
 PRINTING_EXPORT extern const char kCollated[];
 PRINTING_EXPORT extern const char kUncollated[];
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 
 PRINTING_EXPORT extern const char kIppDocumentAttributes[];
 PRINTING_EXPORT extern const char kIppJobAttributes[];
@@ -35,7 +35,7 @@
 PRINTING_EXPORT extern const char kOptionFalse[];
 PRINTING_EXPORT extern const char kOptionTrue[];
 
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 }  // namespace printing
 
diff --git a/printing/backend/cups_ipp_helper.cc b/printing/backend/cups_ipp_helper.cc
index 4e86ba71..570a3a9 100644
--- a/printing/backend/cups_ipp_helper.cc
+++ b/printing/backend/cups_ipp_helper.cc
@@ -24,19 +24,19 @@
 #include "printing/printing_utils.h"
 #include "printing/units.h"
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "base/callback.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/no_destructor.h"
 #include "printing/backend/ipp_handler_map.h"
 #include "printing/printing_features.h"
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 namespace printing {
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 constexpr int kPinMinimumLength = 4;
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 namespace {
 
@@ -256,7 +256,7 @@
   return name && !base::StringPiece(name).compare(kCollated);
 }
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 bool PinSupported(const CupsOptionProvider& printer) {
   ipp_attribute_t* attr = printer.GetSupportedOptionValues(kIppPin);
   if (!attr)
@@ -311,7 +311,7 @@
   attr_count += AddAttributes(printer, kIppDocumentAttributes, options);
   base::UmaHistogramCounts1000("Printing.CUPS.IppAttributesCount", attr_count);
 }
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 }  // namespace
 
@@ -334,11 +334,11 @@
   printer_info->default_paper = DefaultPaper(printer);
   printer_info->papers = SupportedPapers(printer);
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   printer_info->pin_supported = PinSupported(printer);
   if (base::FeatureList::IsEnabled(printing::features::kAdvancedPpdAttributes))
     ExtractAdvancedCapabilities(printer, printer_info);
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   ExtractCopies(printer, printer_info);
   ExtractColor(printer, printer_info);
diff --git a/printing/backend/cups_ipp_helper_unittest.cc b/printing/backend/cups_ipp_helper_unittest.cc
index 0713dc5..5bb5c7e 100644
--- a/printing/backend/cups_ipp_helper_unittest.cc
+++ b/printing/backend/cups_ipp_helper_unittest.cc
@@ -285,7 +285,7 @@
                          "iso b0")));
 }
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 TEST_F(PrintBackendCupsIppHelperTest, PinSupported) {
   printer_->SetSupportedOptions("job-password", MakeInteger(ipp_, 4));
   printer_->SetSupportedOptions("job-password-encryption",
@@ -364,6 +364,6 @@
   EXPECT_EQ(3u, caps.advanced_capabilities[5].values.size());
   histograms.ExpectUniqueSample("Printing.CUPS.IppAttributesCount", 5, 1);
 }
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 }  // namespace printing
diff --git a/printing/backend/mojom/BUILD.gn b/printing/backend/mojom/BUILD.gn
index 3433bc1a..5a4fdc6 100644
--- a/printing/backend/mojom/BUILD.gn
+++ b/printing/backend/mojom/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromeos/ui_mode.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 
 mojom("mojom") {
@@ -30,11 +31,14 @@
       ]
       traits_sources = [ "print_backend_mojom_traits.cc" ]
       traits_headers = [ "print_backend_mojom_traits.h" ]
-      traits_deps = [ "//printing:printing" ]
+      traits_deps = [
+        "//build:chromeos_buildflags",
+        "//printing:printing",
+      ]
     },
   ]
 
-  if (is_chromeos) {
+  if (is_chromeos_ash) {
     cpp_typemaps += [
       {
         types = [
diff --git a/printing/backend/mojom/print_backend_mojom_traits.cc b/printing/backend/mojom/print_backend_mojom_traits.cc
index 473be1b..ca7a99e 100644
--- a/printing/backend/mojom/print_backend_mojom_traits.cc
+++ b/printing/backend/mojom/print_backend_mojom_traits.cc
@@ -7,6 +7,7 @@
 #include <map>
 
 #include "base/logging.h"
+#include "build/chromeos_buildflags.h"
 #include "ui/gfx/geometry/mojom/geometry.mojom-shared.h"
 #include "ui/gfx/geometry/mojom/geometry_mojom_traits.h"
 
@@ -38,7 +39,7 @@
   }
 };
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 template <>
 struct less<::printing::AdvancedCapability> {
   bool operator()(const ::printing::AdvancedCapability& lhs,
@@ -48,7 +49,7 @@
     return lhs.display_name < rhs.display_name;
   }
 };
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 }  // namespace std
 
@@ -114,7 +115,7 @@
          data.ReadVendorId(&out->vendor_id) && data.ReadSizeUm(&out->size_um);
 }
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 // static
 printing::mojom::AdvancedCapabilityType
 EnumTraits<printing::mojom::AdvancedCapabilityType,
@@ -176,7 +177,7 @@
          data.ReadDefaultValue(&out->default_value) &&
          data.ReadValues(&out->values);
 }
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 // static
 bool StructTraits<printing::mojom::PrinterSemanticCapsAndDefaultsDataView,
@@ -200,11 +201,11 @@
     return false;
   }
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   out->pin_supported = data.pin_supported();
   if (!data.ReadAdvancedCapabilities(&out->advanced_capabilities))
     return false;
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   // Extra validity checks.
 
@@ -247,7 +248,7 @@
     return false;
   }
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   DuplicateChecker<printing::AdvancedCapability>
       advanced_capabilities_dup_checker;
   if (advanced_capabilities_dup_checker.HasDuplicates(
@@ -255,7 +256,7 @@
     DLOG(ERROR) << "Duplicate advanced_capabilities detected.";
     return false;
   }
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   return true;
 }
diff --git a/printing/backend/mojom/print_backend_mojom_traits.h b/printing/backend/mojom/print_backend_mojom_traits.h
index aea6a89..13b3249f 100644
--- a/printing/backend/mojom/print_backend_mojom_traits.h
+++ b/printing/backend/mojom/print_backend_mojom_traits.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "build/chromeos_buildflags.h"
 #include "printing/backend/mojom/print_backend.mojom-shared.h"
 #include "printing/backend/print_backend.h"
 #include "printing/mojom/print.mojom.h"
@@ -63,7 +64,7 @@
                    printing::PrinterSemanticCapsAndDefaults::Paper* out);
 };
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 template <>
 struct EnumTraits<printing::mojom::AdvancedCapabilityType,
                   ::printing::AdvancedCapability::Type> {
@@ -114,7 +115,7 @@
   static bool Read(printing::mojom::AdvancedCapabilityDataView data,
                    ::printing::AdvancedCapability* out);
 };
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 template <>
 struct StructTraits<printing::mojom::PrinterSemanticCapsAndDefaultsDataView,
@@ -174,7 +175,7 @@
     return p.default_dpi;
   }
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   static bool pin_supported(const printing::PrinterSemanticCapsAndDefaults& p) {
     return p.pin_supported;
   }
@@ -182,7 +183,7 @@
       const printing::PrinterSemanticCapsAndDefaults& p) {
     return p.advanced_capabilities;
   }
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   static bool Read(printing::mojom::PrinterSemanticCapsAndDefaultsDataView data,
                    printing::PrinterSemanticCapsAndDefaults* out);
diff --git a/printing/backend/mojom/print_backend_mojom_traits_unittest.cc b/printing/backend/mojom/print_backend_mojom_traits_unittest.cc
index b0360c3..1457d63 100644
--- a/printing/backend/mojom/print_backend_mojom_traits_unittest.cc
+++ b/printing/backend/mojom/print_backend_mojom_traits_unittest.cc
@@ -6,6 +6,7 @@
 #include <string>
 #include <vector>
 
+#include "build/chromeos_buildflags.h"
 #include "mojo/public/cpp/test_support/test_utils.h"
 #include "printing/backend/mojom/print_backend.mojom.h"
 #include "printing/backend/print_backend.h"
@@ -29,7 +30,7 @@
     /*display_name=*/"Ledger", /*vendor_id=*/"89",
     /*size_um=*/gfx::Size(6600, 10200)};
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 const printing::AdvancedCapability kAdvancedCapability1(
     /*name=*/"advanced_cap_bool",
     /*display_name=*/"Advanced Capability #1 (bool)",
@@ -55,7 +56,7 @@
     });
 const printing::AdvancedCapabilities kAdvancedCapabilities{
     kAdvancedCapability1, kAdvancedCapability2};
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 static constexpr bool kCollateCapable = true;
 static constexpr bool kCollateDefault = true;
@@ -83,7 +84,7 @@
 static constexpr gfx::Size kDpi1200x600(1200, 600);
 const std::vector<gfx::Size> kDpis{kDpi600, kDpi1200, kDpi1200x600};
 static constexpr gfx::Size kDefaultDpi = kDpi600;
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 static constexpr bool kPinSupported = true;
 #endif
 
@@ -105,10 +106,10 @@
   caps.default_paper = kPaperLetter;
   caps.dpis = kDpis;
   caps.default_dpi = kDefaultDpi;
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   caps.pin_supported = kPinSupported;
   caps.advanced_capabilities = kAdvancedCapabilities;
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   return caps;
 }
@@ -189,7 +190,7 @@
   }
 }
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 TEST(PrintBackendMojomTraitsTest,
      TestSerializeAndDeserializeAdvancedCapability) {
   for (const auto& advanced_capability : kAdvancedCapabilities) {
@@ -200,7 +201,7 @@
     EXPECT_EQ(advanced_capability, output);
   }
 }
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 TEST(PrintBackendMojomTraitsTest,
      TestSerializeAndDeserializePrinterSemanticCapsAndDefaults) {
@@ -225,10 +226,10 @@
   EXPECT_TRUE(kDefaultPaper == output.default_paper);
   EXPECT_EQ(kDpis, output.dpis);
   EXPECT_EQ(kDefaultDpi, output.default_dpi);
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   EXPECT_EQ(kPinSupported, output.pin_supported);
   EXPECT_EQ(kAdvancedCapabilities, output.advanced_capabilities);
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 }
 
 TEST(PrintBackendMojomTraitsTest,
@@ -257,14 +258,14 @@
   const printing::PrinterSemanticCapsAndDefaults::Papers
       kEmptyUserDefinedPapers{};
   const std::vector<gfx::Size> kEmptyDpis{};
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   const printing::AdvancedCapabilities kEmptyAdvancedCapabilities{};
 #endif
 
   input.duplex_modes = kEmptyDuplexModes;
   input.user_defined_papers = kEmptyUserDefinedPapers;
   input.dpis = kEmptyDpis;
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   input.advanced_capabilities = kEmptyAdvancedCapabilities;
 #endif
 
@@ -274,7 +275,7 @@
   EXPECT_EQ(kEmptyDuplexModes, output.duplex_modes);
   EXPECT_EQ(kEmptyUserDefinedPapers, output.user_defined_papers);
   EXPECT_EQ(kEmptyDpis, output.dpis);
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   EXPECT_EQ(kEmptyAdvancedCapabilities, output.advanced_capabilities);
 #endif
 }
@@ -331,7 +332,7 @@
   EXPECT_FALSE(mojo::test::SerializeAndDeserialize<
                printing::mojom::PrinterSemanticCapsAndDefaults>(input, output));
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   // Use an advanced capability with same name but different other fields.
   printing::AdvancedCapability advancedCapability1Prime = kAdvancedCapability1;
   advancedCapability1Prime.type = printing::AdvancedCapability::Type::kInteger;
diff --git a/printing/backend/print_backend.cc b/printing/backend/print_backend.cc
index 36661cc..9e5820fa 100644
--- a/printing/backend/print_backend.cc
+++ b/printing/backend/print_backend.cc
@@ -45,7 +45,7 @@
          is_default == other.is_default && options == other.options;
 }
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 
 AdvancedCapabilityValue::AdvancedCapabilityValue() = default;
 
@@ -89,7 +89,7 @@
          values == other.values;
 }
 
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 bool PrinterSemanticCapsAndDefaults::Paper::operator==(
     const PrinterSemanticCapsAndDefaults::Paper& other) const {
diff --git a/printing/backend/print_backend.h b/printing/backend/print_backend.h
index 4aeb423..e853381 100644
--- a/printing/backend/print_backend.h
+++ b/printing/backend/print_backend.h
@@ -20,7 +20,7 @@
 #include "printing/printing_export.h"
 #include "ui/gfx/geometry/size.h"
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 #include <stdint.h>
 #endif
 
@@ -61,7 +61,7 @@
 
 using PrinterList = std::vector<PrinterBasicInfo>;
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 
 struct PRINTING_EXPORT AdvancedCapabilityValue {
   AdvancedCapabilityValue();
@@ -111,7 +111,7 @@
 
 using AdvancedCapabilities = std::vector<AdvancedCapability>;
 
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 struct PRINTING_EXPORT PrinterSemanticCapsAndDefaults {
   PrinterSemanticCapsAndDefaults();
@@ -149,10 +149,10 @@
   std::vector<gfx::Size> dpis;
   gfx::Size default_dpi;
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   bool pin_supported = false;
   AdvancedCapabilities advanced_capabilities;
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 };
 
 struct PRINTING_EXPORT PrinterCapsAndDefaults {
diff --git a/printing/backend/printing_restrictions.cc b/printing/backend/printing_restrictions.cc
index 17a4c94..b23f89f 100644
--- a/printing/backend/printing_restrictions.cc
+++ b/printing/backend/printing_restrictions.cc
@@ -8,14 +8,14 @@
 
 namespace printing {
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 const char kAllowedColorModes[] = "allowedColorModes";
 const char kAllowedDuplexModes[] = "allowedDuplexModes";
 const char kAllowedPinModes[] = "allowedPinModes";
 const char kDefaultColorMode[] = "defaultColorMode";
 const char kDefaultDuplexMode[] = "defaultDuplexMode";
 const char kDefaultPinMode[] = "defaultPinMode";
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 const char kPaperSizeName[] = "name";
 const char kPaperSizeNameCustomOption[] = "custom";
diff --git a/printing/backend/printing_restrictions.h b/printing/backend/printing_restrictions.h
index 99d281e..c1c206d 100644
--- a/printing/backend/printing_restrictions.h
+++ b/printing/backend/printing_restrictions.h
@@ -10,7 +10,7 @@
 
 namespace printing {
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 // Allowed printing modes as a bitmask.
 // This is used in pref file and should never change.
 enum class ColorModeRestriction {
@@ -46,7 +46,7 @@
 PRINTING_EXPORT extern const char kDefaultColorMode[];
 PRINTING_EXPORT extern const char kDefaultDuplexMode[];
 PRINTING_EXPORT extern const char kDefaultPinMode[];
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 // Allowed background graphics modes.
 // This is used in pref file and should never change.
diff --git a/printing/buildflags/buildflags.gni b/printing/buildflags/buildflags.gni
index 86febe49..058e7f7 100644
--- a/printing/buildflags/buildflags.gni
+++ b/printing/buildflags/buildflags.gni
@@ -4,6 +4,7 @@
 
 import("//build/config/chromecast_build.gni")
 import("//build/config/chromeos/args.gni")
+import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/features.gni")
 import("//build/config/sanitizers/sanitizers.gni")
 
@@ -24,7 +25,7 @@
     # For fuzzing, just restrict to chromeos and linux.
     use_cups = true
   } else {
-    use_cups = (is_chromeos_device || is_linux || is_mac) && !is_chromecast &&
-               !is_fuchsia
+    use_cups = (is_chromeos_device || (is_linux || is_chromeos_lacros) ||
+                is_mac) && !is_chromecast && !is_fuchsia
   }
 }
diff --git a/printing/print_settings.cc b/printing/print_settings.cc
index 7c43dde..c22c0502 100644
--- a/printing/print_settings.cc
+++ b/printing/print_settings.cc
@@ -10,7 +10,7 @@
 #include "build/chromeos_buildflags.h"
 #include "printing/units.h"
 
-#if defined(USE_CUPS) && (defined(OS_MAC) || BUILDFLAG(IS_ASH))
+#if defined(USE_CUPS) && (defined(OS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH))
 #include <cups/cups.h>
 #endif
 
@@ -183,7 +183,7 @@
   // all ColorModel values are determinantly handled.
 }
 
-#if defined(OS_MAC) || BUILDFLAG(IS_ASH)
+#if defined(OS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
 std::string GetIppColorModelForModel(mojom::ColorModel color_model) {
   // Accept |kUnknownColorModel| for consistency with GetColorModelForModel().
   if (color_model == mojom::ColorModel::kUnknownColorModel)
@@ -198,7 +198,7 @@
   return is_color.value() ? CUPS_PRINT_COLOR_MODE_COLOR
                           : CUPS_PRINT_COLOR_MODE_MONOCHROME;
 }
-#endif  // defined(OS_MAC) || BUILDFLAG(IS_ASH)
+#endif  // defined(OS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
 #endif  // defined(USE_CUPS)
 
 base::Optional<bool> IsColorModelSelected(mojom::ColorModel color_model) {
@@ -281,11 +281,11 @@
 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
   advanced_settings_.clear();
 #endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   send_user_info_ = false;
   username_.clear();
   pin_value_.clear();
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 }
 
 void PrintSettings::SetPrinterPrintableArea(
diff --git a/printing/print_settings.h b/printing/print_settings.h
index f15f1f84..c3277c6 100644
--- a/printing/print_settings.h
+++ b/printing/print_settings.h
@@ -44,7 +44,7 @@
                                            std::string* color_setting_name,
                                            std::string* color_value);
 
-#if defined(OS_MAC) || BUILDFLAG(IS_ASH)
+#if defined(OS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
 // Convert from |color_model| to a print-color-mode value from PWG 5100.13.
 PRINTING_EXPORT std::string GetIppColorModelForModel(
     mojom::ColorModel color_model);
@@ -229,7 +229,7 @@
   }
 #endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   void set_send_user_info(bool send_user_info) {
     send_user_info_ = send_user_info;
   }
@@ -240,7 +240,7 @@
 
   void set_pin_value(const std::string& pin_value) { pin_value_ = pin_value; }
   const std::string& pin_value() const { return pin_value_; }
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   // Cookie generator. It is used to initialize PrintedDocument with its
   // associated PrintSettings, to be sure that each generated PrintedPage is
@@ -326,7 +326,7 @@
   AdvancedSettings advanced_settings_;
 #endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   // Whether to send user info.
   bool send_user_info_;
 
diff --git a/printing/print_settings_conversion.cc b/printing/print_settings_conversion.cc
index 686a2fa1..15e52e7 100644
--- a/printing/print_settings_conversion.cc
+++ b/printing/print_settings_conversion.cc
@@ -211,16 +211,21 @@
 #endif
   }
 
-#if defined(OS_CHROMEOS) || (defined(OS_LINUX) && defined(USE_CUPS))
+// TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is
+// complete.
+#if defined(OS_CHROMEOS) ||                                  \
+    ((defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \
+     defined(USE_CUPS))
   const base::Value* advanced_settings =
       job_settings.FindDictKey(kSettingAdvancedSettings);
   if (advanced_settings) {
     for (const auto& item : advanced_settings->DictItems())
       settings->advanced_settings().emplace(item.first, item.second.Clone());
   }
-#endif  // defined(OS_CHROMEOS) || (defined(OS_LINUX) && defined(USE_CUPS))
+#endif  // defined(OS_CHROMEOS) || ((defined(OS_LINUX) ||
+        // BUILDFLAG(IS_CHROMEOS_LACROS)) && defined(USE_CUPS))
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   bool send_user_info =
       job_settings.FindBoolKey(kSettingSendUserInfo).value_or(false);
   settings->set_send_user_info(send_user_info);
@@ -233,7 +238,7 @@
   const std::string* pin_value = job_settings.FindStringKey(kSettingPinValue);
   if (pin_value)
     settings->set_pin_value(*pin_value);
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   return settings;
 }
diff --git a/printing/print_settings_conversion_unittest.cc b/printing/print_settings_conversion_unittest.cc
index 2f410d2..75a0b794 100644
--- a/printing/print_settings_conversion_unittest.cc
+++ b/printing/print_settings_conversion_unittest.cc
@@ -61,7 +61,7 @@
   std::unique_ptr<PrintSettings> settings =
       PrintSettingsFromJobSettings(value.value());
   ASSERT_TRUE(settings);
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   EXPECT_TRUE(settings->send_user_info());
   EXPECT_EQ("username@domain.net", settings->username());
   EXPECT_EQ("0000", settings->pin_value());
@@ -78,7 +78,7 @@
   EXPECT_FALSE(settings);
 }
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 TEST(PrintSettingsConversionTest, ConversionTest_DontSendUsername) {
   base::Optional<base::Value> value = base::JSONReader::Read(kPrinterSettings);
   ASSERT_TRUE(value.has_value());
diff --git a/printing/print_settings_unittest.cc b/printing/print_settings_unittest.cc
index c66d82ea8..56c0c430 100644
--- a/printing/print_settings_unittest.cc
+++ b/printing/print_settings_unittest.cc
@@ -56,7 +56,7 @@
   }
 }
 
-#if defined(OS_MAC) || BUILDFLAG(IS_ASH)
+#if defined(OS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
 TEST(PrintSettingsTest, GetIppColorModelForModel) {
   for (int model = static_cast<int>(mojom::ColorModel::kUnknownColorModel);
        model <= static_cast<int>(mojom::ColorModel::kColorModelLast); ++model) {
@@ -64,7 +64,7 @@
                      .empty());
   }
 }
-#endif  // defined(OS_MAC) || BUILDFLAG(IS_ASH)
+#endif  // defined(OS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
 #endif  // defined(USE_CUPS)
 
 }  // namespace printing
diff --git a/printing/printed_document_linux.cc b/printing/printed_document_linux.cc
index e3ef9ae6..c2e2a5d 100644
--- a/printing/printed_document_linux.cc
+++ b/printing/printed_document_linux.cc
@@ -8,7 +8,7 @@
 #include "build/chromeos_buildflags.h"
 #include "printing/printing_context_linux.h"
 
-#if defined(OS_ANDROID) || BUILDFLAG(IS_ASH)
+#if defined(OS_ANDROID) || BUILDFLAG(IS_CHROMEOS_ASH)
 #error "This file is not used on Android / ChromeOS ash-chrome"
 #endif
 
diff --git a/printing/printing_context.cc b/printing/printing_context.cc
index 49e0e83..92561ff 100644
--- a/printing/printing_context.cc
+++ b/printing/printing_context.cc
@@ -146,7 +146,7 @@
       job_settings.FindIntKey(kSettingPreviewPageCount).value_or(0));
 }
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 PrintingContext::Result PrintingContext::UpdatePrintSettingsFromPOD(
     std::unique_ptr<PrintSettings> job_settings) {
   ResetSettings();
diff --git a/printing/printing_context.h b/printing/printing_context.h
index 896d1b26..268e03d 100644
--- a/printing/printing_context.h
+++ b/printing/printing_context.h
@@ -87,7 +87,7 @@
   // settings information.
   Result UpdatePrintSettings(base::Value job_settings);
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   // Updates Print Settings.
   Result UpdatePrintSettingsFromPOD(
       std::unique_ptr<PrintSettings> job_settings);
diff --git a/printing/printing_features.cc b/printing/printing_features.cc
index 7feeeba1..637f7ff4 100644
--- a/printing/printing_features.cc
+++ b/printing/printing_features.cc
@@ -9,11 +9,11 @@
 namespace printing {
 namespace features {
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 // Enables Advanced PPD Attributes.
 const base::Feature kAdvancedPpdAttributes{"AdvancedPpdAttributes",
                                            base::FEATURE_ENABLED_BY_DEFAULT};
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if defined(OS_MAC)
 // Use the CUPS IPP printing backend instead of the original CUPS backend that
diff --git a/printing/printing_features.h b/printing/printing_features.h
index 7dda9dd..35faf4a 100644
--- a/printing/printing_features.h
+++ b/printing/printing_features.h
@@ -16,9 +16,9 @@
 // The following features are declared alphabetically. The features should be
 // documented with descriptions of their behaviors in the .cc file.
 
-#if BUILDFLAG(IS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 PRINTING_EXPORT extern const base::Feature kAdvancedPpdAttributes;
-#endif  // BUILDFLAG(IS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if defined(OS_MAC)
 PRINTING_EXPORT extern const base::Feature kCupsIppPrintingBackend;
diff --git a/services/device/usb/usb_device_android.cc b/services/device/usb/usb_device_android.cc
index 24a0ae1..2d4b18b9 100644
--- a/services/device/usb/usb_device_android.cc
+++ b/services/device/usb/usb_device_android.cc
@@ -56,7 +56,8 @@
   // Reading the serial number requires device access permission when
   // targeting the Q SDK.
   base::string16 serial_number;
-  if (service->HasDevicePermission(wrapper) || !build_info->is_at_least_q()) {
+  if (service->HasDevicePermission(wrapper) ||
+      build_info->sdk_int() < base::android::SDK_VERSION_Q) {
     ScopedJavaLocalRef<jstring> serial_jstring =
         Java_ChromeUsbDevice_getSerialNumber(env, wrapper);
     if (!serial_jstring.is_null())
diff --git a/services/viz/public/cpp/compositing/paint_filter_mojom_traits.cc b/services/viz/public/cpp/compositing/paint_filter_mojom_traits.cc
index 6a8155c..0356b7d 100644
--- a/services/viz/public/cpp/compositing/paint_filter_mojom_traits.cc
+++ b/services/viz/public/cpp/compositing/paint_filter_mojom_traits.cc
@@ -20,7 +20,7 @@
   // and serialization of PaintRecords.
   cc::PaintOp::SerializeOptions options(nullptr, nullptr, nullptr, nullptr,
                                         nullptr, nullptr, false, false, 0,
-                                        SkMatrix::I());
+                                        SkM44());
   cc::PaintOpWriter writer(memory.data(), memory.size(), options,
                            true /* enable_security_constraints */);
   writer.Write(filter.get());
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index d6eb2384..d3c336f 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -31996,7 +31996,6 @@
           "--recover-devices",
           "--avd-config=../../tools/android/avd/proto/generic_android23.textpb"
         ],
-        "experiment_percentage": 100,
         "merge": {
           "args": [
             "--bucket",
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 1ffce50b..571beeb6 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -728,11 +728,6 @@
     'remove_from': [
       'android-code-coverage-native', # https://crbug.com/1018780
     ],
-    'modifications': {
-      'android-marshmallow-x86-rel': {
-        'experiment_percentage': 100, # crbug.com/1127110
-      },
-    },
   },
   'chrome_public_test_apk': {
     'remove_from': [
diff --git a/third_party/blink/public/blink_resources.grd b/third_party/blink/public/blink_resources.grd
index 62bb0c7..b5318c5 100644
--- a/third_party/blink/public/blink_resources.grd
+++ b/third_party/blink/public/blink_resources.grd
@@ -14,6 +14,7 @@
       <include name="IDR_UASTYLE_VIEW_SOURCE_CSS" file="../renderer/core/css/view-source.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_THEME_CHROMIUM_ANDROID_CSS" file="../renderer/core/html/resources/android.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_FULLSCREEN_ANDROID_CSS" file="../renderer/core/css/fullscreenAndroid.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_PREDEFINED_COUNTER_STYLES_CSS" file="../renderer/core/css/predefined_counter_styles.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_THEME_CHROMIUM_LINUX_CSS" file="../renderer/core/html/resources/linux.css" type="BINDATA" compress="gzip"/>
       <if expr="is_macosx">
         <include name="IDR_UASTYLE_THEME_MAC_CSS" file="../renderer/core/html/resources/mac.css" type="BINDATA" compress="gzip"/>
diff --git a/third_party/blink/public/mojom/background_fetch/background_fetch.mojom b/third_party/blink/public/mojom/background_fetch/background_fetch.mojom
index 91688ea..0740ee7 100644
--- a/third_party/blink/public/mojom/background_fetch/background_fetch.mojom
+++ b/third_party/blink/public/mojom/background_fetch/background_fetch.mojom
@@ -131,7 +131,7 @@
         string developer_id,
         array<FetchAPIRequest> requests,
         BackgroundFetchOptions options,
-        skia.mojom.BitmapWithArbitraryBpp? icon,
+        skia.mojom.BitmapN32? icon,
         BackgroundFetchUkmData ukm_data)
       => (BackgroundFetchError error,
           BackgroundFetchRegistration? registration);
@@ -157,7 +157,7 @@
 interface BackgroundFetchRegistrationService {
   // Updates the user interface for the Background Fetch registration.
   UpdateUI(string? title,
-           skia.mojom.BitmapWithArbitraryBpp? icon)
+           skia.mojom.BitmapN32? icon)
       => (BackgroundFetchError error);
 
   // Aborts the Background Fetch registration.
diff --git a/third_party/blink/public/mojom/content_index/content_index.mojom b/third_party/blink/public/mojom/content_index/content_index.mojom
index f65a257..e269871 100644
--- a/third_party/blink/public/mojom/content_index/content_index.mojom
+++ b/third_party/blink/public/mojom/content_index/content_index.mojom
@@ -83,7 +83,7 @@
 
   Add(int64 service_worker_registration_id,
       ContentDescription description,
-      array<skia.mojom.BitmapWithArbitraryBpp> icon,
+      array<skia.mojom.BitmapN32> icon,
       url.mojom.Url launchUrl)
       => (ContentIndexError error);
   Delete(int64 service_worker_registration_id, string id)
diff --git a/third_party/blink/public/mojom/image_downloader/image_downloader.mojom b/third_party/blink/public/mojom/image_downloader/image_downloader.mojom
index 21cd816..444753d 100644
--- a/third_party/blink/public/mojom/image_downloader/image_downloader.mojom
+++ b/third_party/blink/public/mojom/image_downloader/image_downloader.mojom
@@ -21,6 +21,6 @@
                 uint32 max_bitmap_size,
                 bool bypass_cache)
       => (int32 http_status_code,
-          array<skia.mojom.BitmapWithArbitraryBpp> images,
+          array<skia.mojom.BitmapN32> images,
           array<gfx.mojom.Size> original_image_sizes);
 };
diff --git a/third_party/blink/public/mojom/messaging/transferable_message.mojom b/third_party/blink/public/mojom/messaging/transferable_message.mojom
index bccedfae5..aa0089b1 100644
--- a/third_party/blink/public/mojom/messaging/transferable_message.mojom
+++ b/third_party/blink/public/mojom/messaging/transferable_message.mojom
@@ -30,7 +30,7 @@
   // Any ArrayBuffers being transferred as part of this message.
   array<SerializedArrayBufferContents> array_buffer_contents_array;
   // Any ImageBitmaps being transferred as part of this message.
-  array<skia.mojom.BitmapWithArbitraryBpp> image_bitmap_contents_array;
+  array<skia.mojom.BitmapN32> image_bitmap_contents_array;
   // The user activation state, null if the frame isn't providing it.
   UserActivationSnapshot? user_activation;
 };
diff --git a/third_party/blink/public/mojom/webid/federated_auth_request.mojom b/third_party/blink/public/mojom/webid/federated_auth_request.mojom
index b74368ca..d3680ed 100644
--- a/third_party/blink/public/mojom/webid/federated_auth_request.mojom
+++ b/third_party/blink/public/mojom/webid/federated_auth_request.mojom
@@ -8,14 +8,22 @@
 
 // Implementation of the proposed WebID API.
 //
-// Proposal: https://github.com/samuelgoto/WebID
+// Proposal: https://github.com/WICG/WebID
+
+enum RequestIdTokenStatus {
+  kSuccess,
+  kApprovalDeclined,
+  kErrorTooManyRequests,
+  kErrorWebIdNotSupportedByProvider,
+  kError,
+};
 
 // Create a federated sign-in request using the specified provider.
 // This interface is called from a renderer process and implemented in the
 // browser process.
 interface FederatedAuthRequest {
-  // Requests an IdToken to be generated.
+  // Requests an IdToken to be generated, given an IDP URL and an OAuth request.
   // Returns the raw content of the IdToken.
-  RequestIdToken(url.mojom.Url provider) => (string? id_token);
+  RequestIdToken(url.mojom.Url provider, string id_request) => (RequestIdTokenStatus status, string? id_token);
 };
 
diff --git a/third_party/blink/public/platform/web_graphics_context_3d_provider.h b/third_party/blink/public/platform/web_graphics_context_3d_provider.h
index 74217856..1155e20b 100644
--- a/third_party/blink/public/platform/web_graphics_context_3d_provider.h
+++ b/third_party/blink/public/platform/web_graphics_context_3d_provider.h
@@ -65,7 +65,11 @@
 namespace webgpu {
 class WebGPUInterface;
 }
-}
+}  // namespace gpu
+
+namespace viz {
+class RasterContextProvider;
+}  // namespace viz
 
 namespace blink {
 enum AntialiasingMode {
@@ -111,6 +115,7 @@
   virtual void CopyVideoFrame(media::PaintCanvasVideoRenderer* video_render,
                               media::VideoFrame* video_frame,
                               cc::PaintCanvas* canvas) = 0;
+  virtual viz::RasterContextProvider* RasterContextProvider() const = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc b/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc
index ee32f7c..47a74ecd 100644
--- a/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc
+++ b/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc
@@ -153,8 +153,7 @@
         script_state_->GetContext(),
         ScriptSourceCode(code_,
                          ScriptSourceLocationType::kEvalForScheduledAction),
-        KURL(), SanitizeScriptErrors::kDoNotSanitize, ScriptFetchOptions(),
-        ScriptController::kDoNotExecuteScriptWhenScriptsDisabled);
+        KURL(), SanitizeScriptErrors::kDoNotSanitize);
   } else {
     WorkerGlobalScope* worker = To<WorkerGlobalScope>(context);
     DCHECK(worker->GetThread()->IsCurrentThread());
diff --git a/third_party/blink/renderer/bindings/core/v8/script_controller.cc b/third_party/blink/renderer/bindings/core/v8/script_controller.cc
index f42ff0b..48fdc46 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_controller.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_controller.cc
@@ -87,17 +87,16 @@
     const ScriptSourceCode& source,
     const KURL& base_url,
     SanitizeScriptErrors sanitize_script_errors,
-    const ScriptFetchOptions& fetch_options,
-    ScriptController::ExecuteScriptPolicy policy) {
-  ScriptEvaluationResult result = V8ScriptRunner::CompileAndRunScript(
-      GetIsolate(), ScriptState::From(context), window_.Get(), source, base_url,
-      sanitize_script_errors, fetch_options, policy,
-      V8ScriptRunner::RethrowErrorsOption::DoNotRethrow());
+    const ScriptFetchOptions& fetch_options) {
+    ScriptEvaluationResult result = V8ScriptRunner::CompileAndRunScript(
+        GetIsolate(), ScriptState::From(context), window_.Get(), source,
+        base_url, sanitize_script_errors, fetch_options,
+        V8ScriptRunner::RethrowErrorsOption::DoNotRethrow());
 
-  if (result.GetResultType() == ScriptEvaluationResult::ResultType::kSuccess)
-    return result.GetSuccessValue();
+    if (result.GetResultType() == ScriptEvaluationResult::ResultType::kSuccess)
+      return result.GetSuccessValue();
 
-  return v8::Local<v8::Value>();
+    return v8::Local<v8::Value>();
 }
 
 TextPosition ScriptController::EventHandlerPosition() const {
@@ -275,6 +274,10 @@
     SanitizeScriptErrors sanitize_script_errors,
     const ScriptFetchOptions& fetch_options,
     ExecuteScriptPolicy policy) {
+  if (!CanExecuteScript(policy)) {
+    return v8::Local<v8::Value>();
+  }
+
   // |script_state->GetContext()| should be initialized already due to the
   // WindowProxy() call inside ToScriptStateForMainWorld().
   ScriptState* script_state = ToScriptStateForMainWorld(window_->GetFrame());
@@ -285,7 +288,7 @@
 
   return ExecuteScriptAndReturnValue(script_state->GetContext(), source_code,
                                      base_url, sanitize_script_errors,
-                                     fetch_options, policy);
+                                     fetch_options);
 }
 
 v8::Local<v8::Value> ScriptController::EvaluateMethodInMainWorld(
@@ -352,10 +355,8 @@
   // WindowProxy() inside ToScriptState() above. Add a helper which makes that
   // obvious?
 
-  return ExecuteScriptAndReturnValue(
-      script_state->GetContext(), source, base_url, sanitize_script_errors,
-      ScriptFetchOptions(),
-      ScriptController::kExecuteScriptWhenScriptsDisabled);
+  return ExecuteScriptAndReturnValue(script_state->GetContext(), source,
+                                     base_url, sanitize_script_errors);
 }
 
 scoped_refptr<DOMWrapperWorld>
diff --git a/third_party/blink/renderer/bindings/core/v8/script_controller.h b/third_party/blink/renderer/bindings/core/v8/script_controller.h
index 6bcc39aa..992b5ec 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_controller.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_controller.h
@@ -61,8 +61,6 @@
 class CORE_EXPORT ScriptController final
     : public GarbageCollected<ScriptController> {
  public:
-  // TODO(crbug.com/1111134): Move this enum to V8ScriptRunner and remove
-  // #include "script_controller.h" from v8_script_runner.h.
   enum ExecuteScriptPolicy {
     kExecuteScriptWhenScriptsDisabled,
     kDoNotExecuteScriptWhenScriptsDisabled
@@ -84,8 +82,7 @@
       const ScriptSourceCode&,
       const KURL& base_url,
       SanitizeScriptErrors,
-      const ScriptFetchOptions&,
-      ScriptController::ExecuteScriptPolicy);
+      const ScriptFetchOptions& = ScriptFetchOptions());
 
   v8::Local<v8::Value> EvaluateMethodInMainWorld(
       v8::Local<v8::Function> function,
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
index c4c6232..bb8584a 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
@@ -375,24 +375,13 @@
     const KURL& base_url,
     SanitizeScriptErrors sanitize_script_errors,
     const ScriptFetchOptions& fetch_options,
-    ScriptController::ExecuteScriptPolicy policy,
     RethrowErrorsOption rethrow_errors) {
   DCHECK_EQ(isolate, script_state->GetIsolate());
 
-  if (policy == ScriptController::kDoNotExecuteScriptWhenScriptsDisabled &&
-      !execution_context->CanExecuteScripts(kAboutToExecuteScript)) {
-    return ScriptEvaluationResult::FromClassicNotRun();
-  }
+  v8::Context::Scope scope(script_state->GetContext());
 
   LocalDOMWindow* window = DynamicTo<LocalDOMWindow>(execution_context);
   LocalFrame* frame = window ? window->GetFrame() : nullptr;
-
-  if (window && window->document()->IsInitialEmptyDocument()) {
-    window->GetFrame()->Loader().DidAccessInitialDocument();
-  }
-
-  v8::Context::Scope scope(script_state->GetContext());
-
   TRACE_EVENT1("devtools.timeline", "EvaluateScript", "data",
                inspector_evaluate_script_event::Data(
                    frame, source.Url().GetString(), source.StartPosition()));
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
index 43247ee..e5f21163 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
@@ -27,7 +27,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_SCRIPT_RUNNER_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
@@ -127,7 +126,6 @@
       const KURL&,
       SanitizeScriptErrors,
       const ScriptFetchOptions&,
-      ScriptController::ExecuteScriptPolicy,
       RethrowErrorsOption);
   static v8::MaybeLocal<v8::Value> CompileAndRunInternalScript(
       v8::Isolate*,
diff --git a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
index b5bde8d..bad464b 100644
--- a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
+++ b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
@@ -317,6 +317,9 @@
     const ScriptSourceCode& source_code,
     SanitizeScriptErrors sanitize_script_errors,
     V8ScriptRunner::RethrowErrorsOption rethrow_errors) {
+  if (IsExecutionForbidden())
+    return ScriptEvaluationResult::FromClassicNotRun();
+
   DCHECK(IsContextInitialized());
   DCHECK(is_ready_to_evaluate_);
 
@@ -329,9 +332,7 @@
   // TODO(crbug/1114989): Plumb ScriptFetchOptions from ClassicScript.
   ScriptEvaluationResult result = V8ScriptRunner::CompileAndRunScript(
       isolate_, script_state_, global_scope_, source_code, base_url,
-      sanitize_script_errors, ScriptFetchOptions(),
-      ScriptController::kDoNotExecuteScriptWhenScriptsDisabled,
-      std::move(rethrow_errors));
+      sanitize_script_errors, ScriptFetchOptions(), std::move(rethrow_errors));
 
   if (result.GetResultType() == ScriptEvaluationResult::ResultType::kAborted)
     ForbidExecution();
diff --git a/third_party/blink/renderer/core/css/css_default_style_sheets.cc b/third_party/blink/renderer/core/css/css_default_style_sheets.cc
index 285d47e..c87aa0b 100644
--- a/third_party/blink/renderer/core/css/css_default_style_sheets.cc
+++ b/third_party/blink/renderer/core/css/css_default_style_sheets.cc
@@ -94,9 +94,16 @@
       RuntimeEnabledFeatures::ForcedColorsEnabled()
           ? UncompressResourceAsASCIIString(IDR_UASTYLE_THEME_FORCED_COLORS_CSS)
           : String();
+  // Predefined @counter-style rules
+  String predefined_counter_styles_sheet =
+      RuntimeEnabledFeatures::CSSAtRuleCounterStyleEnabled()
+          ? UncompressResourceAsASCIIString(
+                IDR_UASTYLE_PREDEFINED_COUNTER_STYLES_CSS)
+          : String();
   String default_rules = UncompressResourceAsASCIIString(IDR_UASTYLE_HTML_CSS) +
                          LayoutTheme::GetTheme().ExtraDefaultStyleSheet() +
-                         forced_colors_style_sheet;
+                         forced_colors_style_sheet +
+                         predefined_counter_styles_sheet;
 
   default_style_sheet_ = ParseUASheet(default_rules);
 
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
index 92b9121..9d4136b 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
@@ -945,10 +945,15 @@
     return nullptr;
 
   if (name_token.GetType() != kIdentToken ||
-      !css_parsing_utils::IsCustomIdent<CSSValueID::kNone, CSSValueID::kDecimal,
-                                        CSSValueID::kDisc>(name_token.Id()))
+      !css_parsing_utils::IsCustomIdent<CSSValueID::kNone>(name_token.Id()))
     return nullptr;
 
+  if (GetContext()->Mode() != kUASheetMode) {
+    if (name_token.Id() == CSSValueID::kDecimal ||
+        name_token.Id() == CSSValueID::kDisc)
+      return nullptr;
+  }
+
   AtomicString name(name_token.Value().ToString());
 
   if (observer_) {
diff --git a/third_party/blink/renderer/core/css/predefined_counter_styles.css b/third_party/blink/renderer/core/css/predefined_counter_styles.css
new file mode 100644
index 0000000..7dde12a
--- /dev/null
+++ b/third_party/blink/renderer/core/css/predefined_counter_styles.css
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+/* TODO: Add the remaining counter styles */
+
+@counter-style decimal {
+  system: numeric;
+  symbols: '0' '1' '2' '3' '4' '5' '6' '7' '8' '9';
+}
+
+@counter-style disc {
+  system: cyclic;
+  symbols: \2022;
+  suffix: " ";
+}
+
diff --git a/third_party/blink/renderer/core/css/rule_set.cc b/third_party/blink/renderer/core/css/rule_set.cc
index 8228fed..3c325c1 100644
--- a/third_party/blink/renderer/core/css/rule_set.cc
+++ b/third_party/blink/renderer/core/css/rule_set.cc
@@ -370,6 +370,12 @@
   property_rules_.push_back(rule);
 }
 
+void RuleSet::AddCounterStyleRule(StyleRuleCounterStyle* rule) {
+  EnsurePendingRules();  // So that counter_style_rules_.ShrinkToFit() gets
+                         // called.
+  counter_style_rules_.push_back(rule);
+}
+
 void RuleSet::AddScrollTimelineRule(StyleRuleScrollTimeline* rule) {
   EnsurePendingRules();  // So that property_rules_.ShrinkToFit() gets called.
   scroll_timeline_rules_.push_back(rule);
@@ -410,6 +416,9 @@
       AddKeyframesRule(keyframes_rule);
     } else if (auto* property_rule = DynamicTo<StyleRuleProperty>(rule)) {
       AddPropertyRule(property_rule);
+    } else if (auto* counter_style_rule =
+                   DynamicTo<StyleRuleCounterStyle>(rule)) {
+      AddCounterStyleRule(counter_style_rule);
     } else if (auto* scroll_timeline_rule =
                    DynamicTo<StyleRuleScrollTimeline>(rule)) {
       AddScrollTimelineRule(scroll_timeline_rule);
@@ -498,6 +507,7 @@
   page_rules_.ShrinkToFit();
   font_face_rules_.ShrinkToFit();
   keyframes_rules_.ShrinkToFit();
+  counter_style_rules_.ShrinkToFit();
   property_rules_.ShrinkToFit();
   deep_combinator_or_shadow_pseudo_rules_.ShrinkToFit();
   part_pseudo_rules_.ShrinkToFit();
@@ -540,6 +550,7 @@
   visitor->Trace(font_face_rules_);
   visitor->Trace(keyframes_rules_);
   visitor->Trace(property_rules_);
+  visitor->Trace(counter_style_rules_);
   visitor->Trace(scroll_timeline_rules_);
   visitor->Trace(deep_combinator_or_shadow_pseudo_rules_);
   visitor->Trace(part_pseudo_rules_);
diff --git a/third_party/blink/renderer/core/css/rule_set.h b/third_party/blink/renderer/core/css/rule_set.h
index 594d774..3002946e 100644
--- a/third_party/blink/renderer/core/css/rule_set.h
+++ b/third_party/blink/renderer/core/css/rule_set.h
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/core/css/resolver/media_query_result.h"
 #include "third_party/blink/renderer/core/css/rule_feature_set.h"
 #include "third_party/blink/renderer/core/css/style_rule.h"
+#include "third_party/blink/renderer/core/css/style_rule_counter_style.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
@@ -265,6 +266,9 @@
   const HeapVector<Member<StyleRuleProperty>>& PropertyRules() const {
     return property_rules_;
   }
+  const HeapVector<Member<StyleRuleCounterStyle>>& CounterStyleRules() const {
+    return counter_style_rules_;
+  }
   const HeapVector<Member<StyleRuleScrollTimeline>>& ScrollTimelineRules()
       const {
     return scroll_timeline_rules_;
@@ -323,6 +327,7 @@
   void AddKeyframesRule(StyleRuleKeyframes*);
   void AddPropertyRule(StyleRuleProperty*);
   void AddScrollTimelineRule(StyleRuleScrollTimeline*);
+  void AddCounterStyleRule(StyleRuleCounterStyle*);
 
   bool MatchMediaForAddRules(const MediaQueryEvaluator& evaluator,
                              const MediaQuerySet* media_queries);
@@ -371,6 +376,7 @@
   HeapVector<Member<StyleRuleFontFace>> font_face_rules_;
   HeapVector<Member<StyleRuleKeyframes>> keyframes_rules_;
   HeapVector<Member<StyleRuleProperty>> property_rules_;
+  HeapVector<Member<StyleRuleCounterStyle>> counter_style_rules_;
   HeapVector<Member<StyleRuleScrollTimeline>> scroll_timeline_rules_;
   HeapVector<MinimalRuleData> deep_combinator_or_shadow_pseudo_rules_;
   HeapVector<MinimalRuleData> content_pseudo_element_rules_;
diff --git a/third_party/blink/renderer/core/css/rule_set_test.cc b/third_party/blink/renderer/core/css/rule_set_test.cc
index 54774b7..576531da 100644
--- a/third_party/blink/renderer/core/css/rule_set_test.cc
+++ b/third_party/blink/renderer/core/css/rule_set_test.cc
@@ -30,6 +30,7 @@
 #include "third_party/blink/renderer/core/css/rule_set.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/css/css_default_style_sheets.h"
 #include "third_party/blink/renderer/core/css/css_keyframes_rule.h"
 #include "third_party/blink/renderer/core/css/css_rule_list.h"
 #include "third_party/blink/renderer/core/css/css_test_helpers.h"
@@ -391,4 +392,14 @@
   EXPECT_FALSE(rule->IsVendorPrefixed());
 }
 
+TEST(RuleSetTest, UACounterStyleRules) {
+  ScopedCSSAtRuleCounterStyleForTest enabled_scope(true);
+
+  RuleSet* default_rule_set = CSSDefaultStyleSheets::Instance().DefaultStyle();
+  ASSERT_TRUE(default_rule_set);
+  ASSERT_FALSE(default_rule_set->CounterStyleRules().IsEmpty());
+
+  EXPECT_EQ("decimal", default_rule_set->CounterStyleRules()[0]->GetName());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/selection_modifier_test.cc b/third_party/blink/renderer/core/editing/selection_modifier_test.cc
index 9481466..d7f8069 100644
--- a/third_party/blink/renderer/core/editing/selection_modifier_test.cc
+++ b/third_party/blink/renderer/core/editing/selection_modifier_test.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/core/editing/editor.h"
 #include "third_party/blink/renderer/core/editing/testing/editing_test_base.h"
 #include "third_party/blink/renderer/core/editing/visible_position.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.cc b/third_party/blink/renderer/core/execution_context/execution_context.cc
index 644537a..39ecc21 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.cc
+++ b/third_party/blink/renderer/core/execution_context/execution_context.cc
@@ -355,47 +355,58 @@
 }
 
 void ExecutionContext::ParseAndSetReferrerPolicy(
-    const String& policies,
-    bool support_legacy_keywords,
-    bool from_meta_tag_with_list_of_policies) {
+    const String& policy,
+    const ReferrerPolicySource source) {
   network::mojom::ReferrerPolicy referrer_policy;
+  bool policy_is_valid = false;
 
-  if (!SecurityPolicy::ReferrerPolicyFromHeaderValue(
-          policies,
-          support_legacy_keywords ? kSupportReferrerPolicyLegacyKeywords
-                                  : kDoNotSupportReferrerPolicyLegacyKeywords,
-          &referrer_policy)) {
-    AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
-        mojom::ConsoleMessageSource::kRendering,
-        mojom::ConsoleMessageLevel::kError,
-        "Failed to set referrer policy: The value '" + policies +
-            "' is not one of " +
-            (support_legacy_keywords
-                 ? "'always', 'default', 'never', 'origin-when-crossorigin', "
-                 : "") +
-            "'no-referrer', 'no-referrer-when-downgrade', 'origin', "
-            "'origin-when-cross-origin', 'same-origin', 'strict-origin', "
-            "'strict-origin-when-cross-origin', or 'unsafe-url'. The referrer "
-            "policy "
-            "has been left unchanged."));
+  if (source == kPolicySourceHttpHeader) {
+    policy_is_valid = SecurityPolicy::ReferrerPolicyFromHeaderValue(
+        policy, kDoNotSupportReferrerPolicyLegacyKeywords, &referrer_policy);
+  } else if (source == kPolicySourceMetaTag) {
+    policy_is_valid = (SecurityPolicy::ReferrerPolicyFromString(
+        policy, kSupportReferrerPolicyLegacyKeywords, &referrer_policy));
+  } else {
+    NOTREACHED();
     return;
   }
 
-  SetReferrerPolicy(referrer_policy, from_meta_tag_with_list_of_policies);
+  if (policy_is_valid) {
+    SetReferrerPolicy(referrer_policy);
+  } else {
+    String error_reason;
+    if (source == kPolicySourceMetaTag && policy.Contains(',')) {
+      // Only a single token is permitted for Meta-specified policies
+      // (https://crbug.com/1093914).
+      error_reason =
+          "A policy specified by a meta element must contain only one token.";
+    } else {
+      error_reason =
+          "The value '" + policy + "' is not one of " +
+          ((source == kPolicySourceMetaTag)
+               ? "'always', 'default', 'never', 'origin-when-crossorigin', "
+               : "") +
+          "'no-referrer', 'no-referrer-when-downgrade', 'origin', "
+          "'origin-when-cross-origin', 'same-origin', 'strict-origin', "
+          "'strict-origin-when-cross-origin', or 'unsafe-url'.";
+    }
+
+    AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
+        mojom::ConsoleMessageSource::kRendering,
+        mojom::ConsoleMessageLevel::kError,
+        "Failed to set referrer policy: " + error_reason +
+            " The referrer policy has been left unchanged."));
+  }
 }
 
 void ExecutionContext::SetReferrerPolicy(
-    network::mojom::ReferrerPolicy referrer_policy,
-    bool from_meta_tag_with_list_of_policies) {
+    network::mojom::ReferrerPolicy referrer_policy) {
   // When a referrer policy has already been set, the latest value takes
   // precedence.
   UseCounter::Count(this, WebFeature::kSetReferrerPolicy);
   if (referrer_policy_ != network::mojom::ReferrerPolicy::kDefault)
     UseCounter::Count(this, WebFeature::kResetReferrerPolicy);
 
-  if (!from_meta_tag_with_list_of_policies)
-    referrer_policy_but_for_meta_tags_with_lists_of_policies_ = referrer_policy;
-
   referrer_policy_ = referrer_policy;
 }
 
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.h b/third_party/blink/renderer/core/execution_context/execution_context.h
index 64c0cf8..ad0d628 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.h
+++ b/third_party/blink/renderer/core/execution_context/execution_context.h
@@ -100,6 +100,8 @@
   kNotAboutToExecuteScript
 };
 
+enum ReferrerPolicySource { kPolicySourceHttpHeader, kPolicySourceMetaTag };
+
 // An environment in which script can execute. This class exposes the common
 // properties of script execution environments on the web (i.e, common between
 // script executing in a window and script executing in a worker), such as:
@@ -280,33 +282,20 @@
   // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
   virtual String OutgoingReferrer() const;
 
-  // Parses a comma-separated list of referrer policy tokens, and sets
-  // the context's referrer policy to the last one that is a valid
-  // policy. Logs a message to the console if none of the policy
-  // tokens are valid policies.
+  // Parses a referrer policy directive using either Header or Meta rules and
+  // sets the context to use that policy. If the supplied policy is invalid,
+  // the context's policy is unchanged and a message is logged to the console.
   //
-  // If |supportLegacyKeywords| is true, then the legacy keywords
-  // "never", "default", "always", and "origin-when-crossorigin" are
-  // parsed as valid policies.
-  //
-  // If |from_meta_tag_with_list_of_policies| is *false*, also updates
-  // |referrer_policy_but_for_meta_tags_with_lists_of_policies_|, which
-  // maintains a counterfactual to determine what the policy would look like if
-  // we started ignoring <meta name=referrer content=policy1,policy2,...> tags
-  // in order to align with the HTML standard (crbug.com/1092930).
-  void ParseAndSetReferrerPolicy(
-      const String& policies,
-      bool support_legacy_keywords = false,
-      bool from_meta_tag_with_list_of_policies = false);
-  void SetReferrerPolicy(network::mojom::ReferrerPolicy,
-                         bool from_meta_tag_with_list_of_policies = false);
+  // For a header-set policy, parses a comma-delimited list of tokens, and sets
+  // the context's policy to the last one that is a valid policy. For a meta-set
+  // policy, accepts only a single token, and allows the legacy tokens defined
+  // in the HTML specification.
+  void ParseAndSetReferrerPolicy(const String& policy,
+                                 ReferrerPolicySource source);
+  void SetReferrerPolicy(network::mojom::ReferrerPolicy);
   virtual network::mojom::ReferrerPolicy GetReferrerPolicy() const {
     return referrer_policy_;
   }
-  virtual network::mojom::blink::ReferrerPolicy
-  ReferrerPolicyButForMetaTagsWithListsOfPolicies() const {
-    return referrer_policy_but_for_meta_tags_with_lists_of_policies_;
-  }
 
   virtual CoreProbeSink* GetProbeSink() { return nullptr; }
 
@@ -469,14 +458,6 @@
 
   network::mojom::ReferrerPolicy referrer_policy_;
 
-  // This is the same value as |referrer_policy_| except that it ignores
-  // referrer policies set as the result of parsing <meta name=referrer> tags
-  // whose values are comma-separated lists of policies. Its purpose is to allow
-  // evaluating the impact of switching to a behavior of no longer supporting
-  // these lists (crbug.com/1092930).
-  network::mojom::blink::ReferrerPolicy
-      referrer_policy_but_for_meta_tags_with_lists_of_policies_;
-
   network::mojom::blink::IPAddressSpace address_space_;
 
   Member<OriginTrialContext> origin_trial_context_;
diff --git a/third_party/blink/renderer/core/frame/frame.h b/third_party/blink/renderer/core/frame/frame.h
index 481ea742..048a0e8 100644
--- a/third_party/blink/renderer/core/frame/frame.h
+++ b/third_party/blink/renderer/core/frame/frame.h
@@ -398,12 +398,6 @@
   bool ConsumeTransientUserActivationInFrameTree();
   void ClearUserActivationInFrameTree();
 
-  // Note: this is a temporary debug helper. Note that the despite the name,
-  // this is not the same thing as LocalFrame's FrameLifecycleState.
-  FrameLifecycle::State GetFrameLifecycle() const {
-    return lifecycle_.GetState();
-  }
-
   mutable FrameTree tree_node_;
 
   Member<Page> page_;
diff --git a/third_party/blink/renderer/core/frame/frame_lifecycle.cc b/third_party/blink/renderer/core/frame/frame_lifecycle.cc
index 61c7a12..8c5728b 100644
--- a/third_party/blink/renderer/core/frame/frame_lifecycle.cc
+++ b/third_party/blink/renderer/core/frame/frame_lifecycle.cc
@@ -16,12 +16,12 @@
     case kAttached:
     case kDetached:
       // Normally, only allow state to move forward.
-      CHECK_GT(state, state_);
+      DCHECK_GT(state, state_);
       break;
     case kDetaching:
       // We can go from Detaching to Detaching since the detach() method can be
       // re-entered.
-      CHECK_GE(state, state_);
+      DCHECK_GE(state, state_);
       break;
   }
   state_ = state;
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index 28c705a..fa07443f71 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -388,20 +388,6 @@
   return frame->DomWindow()->GetReferrerPolicy();
 }
 
-network::mojom::blink::ReferrerPolicy
-LocalDOMWindow::ReferrerPolicyButForMetaTagsWithListsOfPolicies() const {
-  network::mojom::ReferrerPolicy policy =
-      ExecutionContext::ReferrerPolicyButForMetaTagsWithListsOfPolicies();
-  // For srcdoc documents without their own policy, walk up the frame tree
-  // analogously to when getting the referrer policy.
-  if (!GetFrame() || policy != network::mojom::ReferrerPolicy::kDefault ||
-      !document()->IsSrcdocDocument()) {
-    return policy;
-  }
-  LocalFrame* frame = To<LocalFrame>(GetFrame()->Tree().Parent());
-  return frame->DomWindow()->ReferrerPolicyButForMetaTagsWithListsOfPolicies();
-}
-
 CoreProbeSink* LocalDOMWindow::GetProbeSink() {
   return probe::ToCoreProbeSink(GetFrame());
 }
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.h b/third_party/blink/renderer/core/frame/local_dom_window.h
index cae45e5c..06aa590 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.h
+++ b/third_party/blink/renderer/core/frame/local_dom_window.h
@@ -157,8 +157,6 @@
   EventTarget* ErrorEventTarget() final { return this; }
   String OutgoingReferrer() const final;
   network::mojom::ReferrerPolicy GetReferrerPolicy() const final;
-  network::mojom::blink::ReferrerPolicy
-  ReferrerPolicyButForMetaTagsWithListsOfPolicies() const final;
   CoreProbeSink* GetProbeSink() final;
   BrowserInterfaceBrokerProxy& GetBrowserInterfaceBroker() final;
   FrameOrWorkerScheduler* GetScheduler() final;
diff --git a/third_party/blink/renderer/core/frame/local_dom_window_test.cc b/third_party/blink/renderer/core/frame/local_dom_window_test.cc
index e2d212c..e7a798c 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window_test.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window_test.cc
@@ -68,7 +68,7 @@
   struct TestCase {
     const char* policy;
     network::mojom::ReferrerPolicy expected;
-    bool is_legacy;
+    bool uses_legacy_tokens;
   } tests[] = {
       {"", network::mojom::ReferrerPolicy::kDefault, false},
       // Test that invalid policy values are ignored.
@@ -101,22 +101,51 @@
       {"unsafe-url", network::mojom::ReferrerPolicy::kAlways},
   };
 
-  for (auto test : tests) {
+  for (const auto test : tests) {
     window->SetReferrerPolicy(network::mojom::ReferrerPolicy::kDefault);
-    if (test.is_legacy) {
-      // Legacy keyword support must be explicitly enabled for the policy to
-      // parse successfully.
-      window->ParseAndSetReferrerPolicy(test.policy);
+    if (test.uses_legacy_tokens) {
+      // Legacy tokens are supported only for meta-specified policy.
+      window->ParseAndSetReferrerPolicy(test.policy, kPolicySourceHttpHeader);
       EXPECT_EQ(network::mojom::ReferrerPolicy::kDefault,
                 window->GetReferrerPolicy());
-      window->ParseAndSetReferrerPolicy(test.policy, true);
+      window->ParseAndSetReferrerPolicy(test.policy, kPolicySourceMetaTag);
     } else {
-      window->ParseAndSetReferrerPolicy(test.policy);
+      window->ParseAndSetReferrerPolicy(test.policy, kPolicySourceHttpHeader);
     }
     EXPECT_EQ(test.expected, window->GetReferrerPolicy()) << test.policy;
   }
 }
 
+TEST_F(LocalDOMWindowTest, referrerPolicyParsingWithCommas) {
+  LocalDOMWindow* window = GetFrame().DomWindow();
+  EXPECT_EQ(network::mojom::ReferrerPolicy::kDefault,
+            window->GetReferrerPolicy());
+
+  struct TestCase {
+    const char* policy;
+    network::mojom::ReferrerPolicy expected;
+  } tests[] = {
+      {"same-origin,strict-origin",
+       network::mojom::ReferrerPolicy::kStrictOrigin},
+      {"same-origin,not-a-real-policy,strict-origin",
+       network::mojom::ReferrerPolicy::kStrictOrigin},
+      {"strict-origin, same-origin, not-a-real-policy",
+       network::mojom::ReferrerPolicy::kSameOrigin},
+  };
+
+  for (const auto test : tests) {
+    window->SetReferrerPolicy(network::mojom::ReferrerPolicy::kDefault);
+    // Policies containing commas are ignored when specified by a Meta element.
+    window->ParseAndSetReferrerPolicy(test.policy, kPolicySourceMetaTag);
+    EXPECT_EQ(network::mojom::ReferrerPolicy::kDefault,
+              window->GetReferrerPolicy());
+
+    // Header-specified policy permits commas and returns the last valid policy.
+    window->ParseAndSetReferrerPolicy(test.policy, kPolicySourceHttpHeader);
+    EXPECT_EQ(test.expected, window->GetReferrerPolicy()) << test.policy;
+  }
+}
+
 TEST_F(LocalDOMWindowTest, OutgoingReferrer) {
   NavigateTo(KURL("https://www.example.com/hoge#fuga?piyo"));
   EXPECT_EQ("https://www.example.com/hoge",
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index d4c116a..7494f3ea 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -34,7 +34,6 @@
 #include <memory>
 #include <utility>
 
-#include "base/debug/crash_logging.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/unguessable_token.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
@@ -2103,17 +2102,18 @@
 }
 
 bool LocalFrame::IsProvisional() const {
-  SCOPED_CRASH_KEY_NUMBER("inconsistent-frame-state", lifecycle,
-                          static_cast<int>(GetFrameLifecycle()));
   // Calling this after the frame is marked as completely detached is a bug, as
   // this state can no longer be accurately calculated.
   CHECK(!IsDetached());
-  // TODO(https://crbug.com/1154141): This are added for temporary debugging.
-  // If a LocalFrame is attached, it should have both a LocalFrameClient and a
-  // Page associated with it. Yet there are some crash reports that seem to
-  // indicate that GetPage() is somehow null... confirm this.
-  CHECK(Client());
-  CHECK(GetPage());
+
+  // TODO(https://crbug.com/838348): Sadly, there are situations where Blink may
+  // attempt to detach a main frame twice due to a bug. That rewinds
+  // FrameLifecycle from kDetached to kDetaching, but GetPage() will already be
+  // null. Early returning false in that case is "safe enough", as the frame has
+  // already been detached, so any detach work gated on IsProvisional() has
+  // already been done.
+  if (!GetPage())
+    return false;
 
   if (IsMainFrame()) {
     return GetPage()->MainFrame() != this;
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 01e85aa..76be566 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -341,24 +341,24 @@
   }
 }
 
-// Call function for each non-throttled frame view in pre-order. If this logic
-// is updated, consider updating |ForAllThrottledLocalFrameViews| too.
+// Call function for each non-throttled frame view in pre-order (by default) or
+// post-order. If this logic is updated, consider updating
+// |ForAllThrottledLocalFrameViews| too.
 template <typename Function>
-void LocalFrameView::ForAllNonThrottledLocalFrameViews(
-    const Function& function) {
+void LocalFrameView::ForAllNonThrottledLocalFrameViews(const Function& function,
+                                                       TraversalOrder order) {
   if (ShouldThrottleRendering())
     return;
 
-  function(*this);
+  if (order == kPreOrder)
+    function(*this);
 
-  for (Frame* child = frame_->Tree().FirstChild(); child;
-       child = child->Tree().NextSibling()) {
-    auto* child_local_frame = DynamicTo<LocalFrame>(child);
-    if (!child_local_frame)
-      continue;
-    if (LocalFrameView* child_view = child_local_frame->View())
-      child_view->ForAllNonThrottledLocalFrameViews(function);
-  }
+  ForAllChildLocalFrameViews([&function, order](LocalFrameView& child_view) {
+    child_view.ForAllNonThrottledLocalFrameViews(function, order);
+  });
+
+  if (order == kPostOrder)
+    function(*this);
 }
 
 // Call function for each throttled frame view in pre-order. If this logic is
@@ -368,14 +368,9 @@
   if (ShouldThrottleRendering())
     function(*this);
 
-  for (Frame* child = frame_->Tree().FirstChild(); child;
-       child = child->Tree().NextSibling()) {
-    auto* child_local_frame = DynamicTo<LocalFrame>(child);
-    if (!child_local_frame)
-      continue;
-    if (LocalFrameView* child_view = child_local_frame->View())
-      child_view->ForAllThrottledLocalFrameViews(function);
-  }
+  ForAllChildLocalFrameViews([&function](LocalFrameView& child_view) {
+    child_view.ForAllThrottledLocalFrameViews(function);
+  });
 }
 
 void LocalFrameView::ForAllThrottledLocalFrameViewsForTesting(
@@ -2976,10 +2971,34 @@
   auto* layout_view = GetLayoutView();
   DCHECK(layout_view);
   paint_frame_count_++;
-  ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
-    frame_view.MarkFirstEligibleToPaint();
-    frame_view.Lifecycle().AdvanceTo(DocumentLifecycle::kInPaint);
-  });
+  ForAllNonThrottledLocalFrameViews(
+      [](LocalFrameView& frame_view) {
+        frame_view.MarkFirstEligibleToPaint();
+        frame_view.Lifecycle().AdvanceTo(DocumentLifecycle::kInPaint);
+        // Propagate child frame PaintLayer NeedsRepaint flag into the owner
+        // frame.
+        if (auto* frame_layout_view = frame_view.GetLayoutView()) {
+          if (auto* owner = frame_view.GetFrame().OwnerLayoutObject()) {
+            PaintLayer* frame_root_layer = frame_layout_view->Layer();
+            DCHECK(frame_root_layer);
+            DCHECK(owner->Layer());
+            // In pre-CompositeAfterPaint the root layer's SelfNeedsRepaint()
+            // means it's compositing state has changed, so propagate the flag
+            // to owner. Or propagate DescendantNeedsRepaint only if it is not
+            // composited. In CompositeAfterPaint, the whole condition can be
+            // changed to |if
+            // (frame_root_layer->SelfOrDescendantNeedsRepaint())|.
+            if (frame_root_layer->SelfNeedsRepaint() ||
+                (frame_root_layer->DescendantNeedsRepaint() &&
+                 frame_root_layer->GetCompositingState() !=
+                     kPaintsIntoOwnBacking))
+              owner->Layer()->SetDescendantNeedsRepaint();
+          }
+        }
+      },
+      // Use post-order to ensure correct flag propagation for nested frames.
+      kPostOrder);
+
   ForAllThrottledLocalFrameViews(
       [](LocalFrameView& frame_view) { frame_view.MarkIneligibleToPaint(); });
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 20677e3..299ff19 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -940,8 +940,10 @@
   template <typename Function>
   void ForAllChildLocalFrameViews(const Function&);
 
+  enum TraversalOrder { kPreOrder, kPostOrder };
   template <typename Function>
-  void ForAllNonThrottledLocalFrameViews(const Function&);
+  void ForAllNonThrottledLocalFrameViews(const Function&,
+                                         TraversalOrder = kPreOrder);
 
   template <typename Function>
   void ForAllThrottledLocalFrameViews(const Function&);
diff --git a/third_party/blink/renderer/core/geometry/dom_rect.h b/third_party/blink/renderer/core/geometry/dom_rect.h
index 058c60a9..c647bc4f 100644
--- a/third_party/blink/renderer/core/geometry/dom_rect.h
+++ b/third_party/blink/renderer/core/geometry/dom_rect.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_GEOMETRY_DOM_RECT_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_GEOMETRY_DOM_RECT_H_
 
-#include "third_party/blink/renderer/bindings/core/v8/dictionary.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/geometry/dom_rect_read_only.h"
 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
diff --git a/third_party/blink/renderer/core/html/html_meta_element.cc b/third_party/blink/renderer/core/html/html_meta_element.cc
index fd0b909..57a9554 100644
--- a/third_party/blink/renderer/core/html/html_meta_element.cc
+++ b/third_party/blink/renderer/core/html/html_meta_element.cc
@@ -39,6 +39,7 @@
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/weborigin/security_policy.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_to_number.h"
 
 namespace blink {
@@ -589,18 +590,9 @@
       UseCounter::Count(&GetDocument(),
                         WebFeature::kHTMLMetaElementReferrerPolicyOutsideHead);
     }
-    bool comma_in_content_value = false;
-    if (content_value.Contains(',')) {
-      comma_in_content_value = true;
-      UseCounter::Count(
-          &GetDocument(),
-          WebFeature::kHTMLMetaElementReferrerPolicyMultipleTokens);
-    }
 
-    GetExecutionContext()->ParseAndSetReferrerPolicy(
-        content_value, true /* support legacy keywords */,
-        /*from_meta_tag_with_list_of_policies=*/
-        comma_in_content_value);
+    GetExecutionContext()->ParseAndSetReferrerPolicy(content_value,
+                                                     kPolicySourceMetaTag);
     if (base::FeatureList::IsEnabled(blink::features::kPolicyContainer)) {
       LocalFrame* frame = GetDocument().GetFrame();
       // If frame is null, this document is not attached to a frame, hence it
@@ -649,4 +641,4 @@
 const AtomicString& HTMLMetaElement::GetName() const {
   return FastGetAttribute(html_names::kNameAttr);
 }
-}
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
index 10a93fa..6f932564 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
@@ -69,7 +69,59 @@
 
 MinMaxSizesResult NGGridLayoutAlgorithm::ComputeMinMaxSizes(
     const MinMaxSizesInput& input) const {
-  return {MinMaxSizes(), /* depends_on_percentage_block_size */ true};
+  // TODO(janewman): Handle the cases typically done via:
+  // CalculateMinMaxSizesIgnoringChildren.
+
+  // Measure Items
+  Vector<GridItemData> grid_items;
+  ConstructAndAppendGridItems(&grid_items);
+
+  NGGridLayoutAlgorithmTrackCollection algorithm_column_track_collection;
+  NGGridLayoutAlgorithmTrackCollection algorithm_row_track_collection;
+  BuildAlgorithmTrackCollections(&grid_items,
+                                 &algorithm_column_track_collection,
+                                 &algorithm_row_track_collection);
+
+  // Cache set indices.
+  CacheItemSetIndices(algorithm_column_track_collection, &grid_items);
+
+  // Resolve inline size.
+  ComputeUsedTrackSizes(&algorithm_column_track_collection, &grid_items);
+
+  const LayoutUnit grid_gap = GridGap(kForColumns);
+
+  // Now the columns should have their used track size and growth limit, each
+  // adding up to match the min and max size of the grid respectively.
+  MinMaxSizes grid_min_max_sizes;
+
+  // If the track collection does not have any tracks, then we do not want to
+  // subtract the grid gap from the last track.
+  bool has_tracks = false;
+  for (auto column_set_iterator =
+           algorithm_column_track_collection.GetSetIterator();
+       !column_set_iterator.IsAtEnd(); column_set_iterator.MoveToNextSet()) {
+    const auto& set = column_set_iterator.CurrentSet();
+    has_tracks |= set.TrackCount();
+    LayoutUnit gap = set.TrackCount() * grid_gap;
+
+    // Aggregate min/max size contributions for this set of tracks.
+    LayoutUnit min_size_contribution = set.BaseSize() + gap;
+    grid_min_max_sizes.min_size += min_size_contribution;
+    grid_min_max_sizes.max_size += set.GrowthLimit() == kIndefiniteSize
+                                       ? min_size_contribution
+                                       : set.GrowthLimit() + gap;
+  }
+
+  // Subtract the gap from the end of the last track. Only do this if there is
+  // at least one track.
+  if (has_tracks)
+    grid_min_max_sizes -= grid_gap;
+
+  grid_min_max_sizes += BorderScrollbarPadding().InlineSum();
+
+  // TODO(janewman): determine what cases need depends_on_percentage_block_size
+  // to be set.
+  return {grid_min_max_sizes, /* depends_on_percentage_block_size */ true};
 }
 
 NGGridLayoutAlgorithm::GridItemData::GridItemData(const NGBlockNode node)
@@ -220,14 +272,13 @@
     Vector<GridItemData>* grid_items,
     Vector<GridItemData>* out_of_flow_items) const {
   DCHECK(grid_items);
-  DCHECK(out_of_flow_items);
   NGGridChildIterator iterator(Node());
   for (NGBlockNode child = iterator.NextChild(); child;
        child = iterator.NextChild()) {
     GridItemData grid_item = MeasureGridItem(child);
     // Store out-of-flow items separately, as they do not contribute to track
     // sizing or auto placement.
-    if (child.IsOutOfFlowPositioned())
+    if (out_of_flow_items && child.IsOutOfFlowPositioned())
       out_of_flow_items->emplace_back(grid_item);
     else
       grid_items->emplace_back(grid_item);
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
index 208f92b..95473be 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
@@ -133,7 +133,7 @@
 
   void ConstructAndAppendGridItems(
       Vector<GridItemData>* grid_items,
-      Vector<GridItemData>* out_of_flow_items) const;
+      Vector<GridItemData>* out_of_flow_items = nullptr) const;
   GridItemData MeasureGridItem(const NGBlockNode node) const;
   NGConstraintSpace BuildSpaceForGridItem(const NGBlockNode node) const;
 
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
index 0c14c5a1..ef70da5 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
@@ -39,13 +39,6 @@
   return false;
 }
 
-// Another MinMax variant of |ShouldIgnorePercentagesForMinMax|.
-// This one is only for flexbox/grid. Fixed table-layout "infinite" max-size
-// should not be applied.
-bool ShouldIgnoreInfiniteMaxSizeForMinMax(const LayoutBlock& table) {
-  return false;
-}
-
 NGTableTypes::Caption ComputeCaptionConstraint(
     const ComputedStyle& table_style,
     const NGTableGroupedChildren& grouped_children) {
@@ -545,10 +538,8 @@
       std::max(grid_min_max.min_size, caption_constraint.min_size),
       std::max(grid_min_max.max_size, caption_constraint.min_size)};
 
-  if (!ShouldIgnoreInfiniteMaxSizeForMinMax(*layout_table)) {
-    if (is_fixed_layout && Style().LogicalWidth().IsPercentOrCalc())
-      min_max.max_size = NGTableTypes::kTableMaxInlineSize;
-  }
+  if (is_fixed_layout && Style().LogicalWidth().IsPercentOrCalc())
+    min_max.max_size = NGTableTypes::kTableMaxInlineSize;
 
   DCHECK_LE(min_max.min_size, min_max.max_size);
   return MinMaxSizesResult{min_max,
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
index 28dc2d7..e00b27c8 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
@@ -76,7 +76,8 @@
 // "outer min-content and outer max-content widths for colgroups"
 NGTableTypes::Column NGTableTypes::CreateColumn(
     const ComputedStyle& style,
-    base::Optional<LayoutUnit> default_inline_size) {
+    base::Optional<LayoutUnit> default_inline_size,
+    bool is_table_fixed) {
   base::Optional<LayoutUnit> inline_size;
   base::Optional<LayoutUnit> min_inline_size;
   base::Optional<LayoutUnit> max_inline_size;
@@ -97,7 +98,8 @@
                 percentage_inline_size,
                 LayoutUnit() /* percent_border_padding */,
                 is_constrained,
-                is_collapsed};
+                is_collapsed,
+                is_table_fixed};
 }
 
 // Implements https://www.w3.org/TR/css-tables-3/#computing-cell-measures
@@ -282,6 +284,10 @@
   if (!cell)
     return;
 
+  // Constrained columns in fixed tables take precedence over cells.
+  if (is_constrained && is_table_fixed)
+    return;
+
   if (min_inline_size) {
     if (min_inline_size < cell->min_inline_size) {
       min_inline_size = cell->min_inline_size;
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
index e63d160..4319491 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
@@ -66,6 +66,7 @@
     // True if any cell for this column is constrained.
     bool is_constrained = false;
     bool is_collapsed = false;
+    bool is_table_fixed = false;
 
     void Encompass(const base::Optional<NGTableTypes::CellInlineConstraint>&);
     LayoutUnit ResolvePercentInlineSize(
@@ -180,7 +181,8 @@
   };
 
   static Column CreateColumn(const ComputedStyle&,
-                             base::Optional<LayoutUnit> default_inline_size);
+                             base::Optional<LayoutUnit> default_inline_size,
+                             bool is_table_fixed);
 
   static CellInlineConstraint CreateCellInlineConstraint(
       const NGBlockNode&,
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
index 68f8009..3264b755 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
@@ -243,10 +243,12 @@
                 wtf_size_t span) {
     // COL creates SPAN constraints. Its width is col css width, or enclosing
     // colgroup css width.
-    NGTableTypes::Column col_constraint = NGTableTypes::CreateColumn(
-        column.Style(), !is_fixed_layout_ && colgroup_constraint_
-                            ? colgroup_constraint_->max_inline_size
-                            : base::nullopt);
+    NGTableTypes::Column col_constraint =
+        NGTableTypes::CreateColumn(column.Style(),
+                                   !is_fixed_layout_ && colgroup_constraint_
+                                       ? colgroup_constraint_->max_inline_size
+                                       : base::nullopt,
+                                   is_fixed_layout_);
     for (wtf_size_t i = 0; i < span; ++i)
       column_constraints_->data.push_back(col_constraint);
     column.GetLayoutBox()->ClearNeedsLayout();
@@ -254,8 +256,8 @@
 
   void EnterColgroup(const NGLayoutInputNode& colgroup,
                      wtf_size_t start_column_index) {
-    colgroup_constraint_ =
-        NGTableTypes::CreateColumn(colgroup.Style(), base::nullopt);
+    colgroup_constraint_ = NGTableTypes::CreateColumn(
+        colgroup.Style(), base::nullopt, is_fixed_layout_);
   }
 
   void LeaveColgroup(const NGLayoutInputNode& colgroup,
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 3af3b624..7d5e731 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -1645,14 +1645,15 @@
     // ignore them.
     if (frame_->GetPolicyContainer()) {
       frame_->DomWindow()->SetReferrerPolicy(
-          frame_->GetPolicyContainer()->GetReferrerPolicy(), false);
+          frame_->GetPolicyContainer()->GetReferrerPolicy());
     }
   }
   String referrer_policy_header =
       response_.HttpHeaderField(http_names::kReferrerPolicy);
   if (!referrer_policy_header.IsNull()) {
     CountUse(WebFeature::kReferrerPolicyHeader);
-    frame_->DomWindow()->ParseAndSetReferrerPolicy(referrer_policy_header);
+    frame_->DomWindow()->ParseAndSetReferrerPolicy(referrer_policy_header,
+                                                   kPolicySourceHttpHeader);
     if (base::FeatureList::IsEnabled(blink::features::kPolicyContainer)) {
       if (frame_->GetPolicyContainer()) {
         frame_->GetPolicyContainer()->UpdateReferrerPolicy(
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index 5851f84..a7db535 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -3589,6 +3589,13 @@
   static_cast<DisplayItemClient*>(this)->Invalidate();
 }
 
+void PaintLayer::SetDescendantNeedsRepaint() {
+  if (descendant_needs_repaint_)
+    return;
+  descendant_needs_repaint_ = true;
+  MarkCompositingContainerChainForNeedsRepaint();
+}
+
 void PaintLayer::MarkCompositingContainerChainForNeedsRepaint() {
   PaintLayer* layer = this;
   while (true) {
@@ -3597,13 +3604,13 @@
       // when compositingState() changes.
       DisableCompositingQueryAsserts disabler;
       if (layer->GetCompositingState() == kPaintsIntoOwnBacking)
-        return;
+        break;
       if (CompositedLayerMapping* grouped_mapping = layer->GroupedMapping()) {
         // TODO(wkorman): As we clean up the CompositedLayerMapping needsRepaint
         // logic to delegate to scrollbars, we may be able to remove the line
         // below as well.
         grouped_mapping->OwningLayer().SetNeedsRepaint();
-        return;
+        break;
       }
     }
 
@@ -3615,23 +3622,18 @@
     if (layer->Parent() && !layer->IsSelfPaintingLayer())
       layer->Parent()->SetNeedsRepaint();
 
+    // Don't mark across frame boundary here. LocalFrameView::PaintTree() will
+    // propagate child frame NeedsRepaint flag into the owning frame.
     PaintLayer* container = layer->CompositingContainer();
-    if (!container) {
-      auto* owner = layer->GetLayoutObject().GetFrame()->OwnerLayoutObject();
-      if (!owner)
-        break;
-      container = owner->EnclosingLayer();
-    }
-
-    // If the container already needs descendants repaint, break out of the
-    // loop. Also, if the layer doesn't need painting itself (which means we're
-    // propagating a bit from its children) and it blocks child painting via
-    // display lock, then stop propagating the dirty bit.
-    if (container->descendant_needs_repaint_ ||
-        (!layer->SelfNeedsRepaint() &&
-         layer->GetLayoutObject().ChildPaintBlockedByDisplayLock())) {
+    if (!container || container->descendant_needs_repaint_)
       break;
-    }
+
+    // If the layer doesn't need painting itself (which means we're propagating
+    // a bit from its children) and it blocks child painting via display lock,
+    // then stop propagating the dirty bit.
+    if (!layer->SelfNeedsRepaint() &&
+        layer->GetLayoutObject().ChildPaintBlockedByDisplayLock())
+      break;
 
     container->descendant_needs_repaint_ = true;
     layer = container;
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h
index a415f1d8..c07d66b 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.h
+++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -1019,6 +1019,7 @@
     return self_needs_repaint_ || descendant_needs_repaint_;
   }
   void SetNeedsRepaint();
+  void SetDescendantNeedsRepaint();
   void ClearNeedsRepaintRecursively();
 
   // These previousXXX() functions are for subsequence caching. They save the
diff --git a/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc b/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc
index def5e9d8..0123339d 100644
--- a/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc
+++ b/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc
@@ -1247,8 +1247,14 @@
   // And a throttled full lifecycle update.
   UpdateAllLifecyclePhases();
 
-  // The inner div is no longer composited.
-  EXPECT_EQ(0u, CcLayersByDOMElementId(root_layer, "div").size());
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+    // Leave the composited layer of the inner div as-is because we don't
+    // repaint it.
+    EXPECT_EQ(1u, CcLayersByDOMElementId(root_layer, "div").size());
+  } else {
+    // The inner div is no longer composited.
+    EXPECT_EQ(0u, CcLayersByDOMElementId(root_layer, "div").size());
+  }
 
   auto commands_throttled1 = CompositeFrame();
   EXPECT_LT(commands_throttled1.DrawCount(), full_draw_count);
@@ -1754,4 +1760,54 @@
   EXPECT_FALSE(child_view->CanThrottleRendering());
 }
 
+TEST_P(FrameThrottlingTest, LifecycleThrottledFrameNeedsRepaint) {
+  SimRequest main_resource("https://example.com/", "text/html");
+  SimRequest frame_resource("https://example.com/iframe.html", "text/html");
+
+  LoadURL("https://example.com/");
+  // The frame is initially throttled.
+  main_resource.Complete("<iframe id='frame' src='iframe.html'></iframe>");
+  frame_resource.Complete("<body style='background: red'></body>");
+
+  auto commands = CompositeFrame();
+  EXPECT_TRUE(commands.Contains(SimCanvas::kRect, "red"));
+
+  auto* frame_element =
+      To<HTMLIFrameElement>(GetDocument().getElementById("frame"));
+  auto* frame_document = frame_element->contentDocument();
+  frame_document->View()->SetLifecycleUpdatesThrottledForTesting(true);
+  GetDocument().View()->ScheduleAnimation();
+  EXPECT_TRUE(frame_document->View()->ShouldThrottleRenderingForTest());
+
+  commands = CompositeFrame();
+  // The throttled frame is omitted for paint.
+  EXPECT_FALSE(commands.Contains(SimCanvas::kRect, "red"));
+
+  frame_document->body()->setAttribute(kStyleAttr, "background: green");
+  // Update life cycle update except paint without throttling, which will do
+  // paint invalidation.
+  GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(
+      DocumentUpdateReason::kTest);
+  EXPECT_TRUE(
+      frame_document->GetLayoutView()->Layer()->SelfOrDescendantNeedsRepaint());
+  // The NeedsRepaint flag doesn't propagte across frame boundary for now.
+  EXPECT_FALSE(
+      GetDocument().GetLayoutView()->Layer()->SelfOrDescendantNeedsRepaint());
+
+  commands = CompositeFrame();
+  EXPECT_FALSE(commands.Contains(SimCanvas::kRect, "green"));
+  EXPECT_TRUE(
+      frame_document->GetLayoutView()->Layer()->SelfOrDescendantNeedsRepaint());
+  EXPECT_FALSE(
+      GetDocument().GetLayoutView()->Layer()->SelfOrDescendantNeedsRepaint());
+
+  frame_document->View()->BeginLifecycleUpdates();
+  commands = CompositeFrame();
+  EXPECT_TRUE(commands.Contains(SimCanvas::kRect, "green"));
+  EXPECT_FALSE(
+      frame_document->GetLayoutView()->Layer()->SelfOrDescendantNeedsRepaint());
+  EXPECT_FALSE(
+      GetDocument().GetLayoutView()->Layer()->SelfOrDescendantNeedsRepaint());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/script/fetch_client_settings_object_impl.cc b/third_party/blink/renderer/core/script/fetch_client_settings_object_impl.cc
index 4bb932d..69789c9 100644
--- a/third_party/blink/renderer/core/script/fetch_client_settings_object_impl.cc
+++ b/third_party/blink/renderer/core/script/fetch_client_settings_object_impl.cc
@@ -38,13 +38,6 @@
   return execution_context_->GetReferrerPolicy();
 }
 
-base::Optional<network::mojom::blink::ReferrerPolicy>
-FetchClientSettingsObjectImpl::
-    GetReferrerPolicyDisregardingMetaTagsContainingLists() const {
-  DCHECK(execution_context_->IsContextThread());
-  return execution_context_->ReferrerPolicyButForMetaTagsWithListsOfPolicies();
-}
-
 const String FetchClientSettingsObjectImpl::GetOutgoingReferrer() const {
   DCHECK(execution_context_->IsContextThread());
   return execution_context_->OutgoingReferrer();
diff --git a/third_party/blink/renderer/core/script/fetch_client_settings_object_impl.h b/third_party/blink/renderer/core/script/fetch_client_settings_object_impl.h
index a744576..a00c6605 100644
--- a/third_party/blink/renderer/core/script/fetch_client_settings_object_impl.h
+++ b/third_party/blink/renderer/core/script/fetch_client_settings_object_impl.h
@@ -36,8 +36,6 @@
   const KURL& BaseUrl() const override;
   const SecurityOrigin* GetSecurityOrigin() const override;
   network::mojom::ReferrerPolicy GetReferrerPolicy() const override;
-  base::Optional<network::mojom::blink::ReferrerPolicy>
-  GetReferrerPolicyDisregardingMetaTagsContainingLists() const override;
 
   const String GetOutgoingReferrer() const override;
 
diff --git a/third_party/blink/renderer/core/script/import_map.cc b/third_party/blink/renderer/core/script/import_map.cc
index 45f8939..1e1a78b6 100644
--- a/third_party/blink/renderer/core/script/import_map.cc
+++ b/third_party/blink/renderer/core/script/import_map.cc
@@ -492,7 +492,17 @@
     return NullURL();
   }
 
-  // <spec step="1.2.8">Return url.</spec>
+  // <spec step="1.2.8">If the serialization of url does not start with the
+  // serialization of resolutionResult, then throw a TypeError indicating that
+  // resolution of normalizedSpecifier was blocked due to it backtracking above
+  // its prefix specifierKey.</spec>
+  if (!url.GetString().StartsWith(matched->value.GetString())) {
+    *debug_message = "Import Map: \"" + key + "\" matches with \"" +
+                     matched->key + "\" but is blocked due to backtracking";
+    return NullURL();
+  }
+
+  // <spec step="1.2.9">Return url.</spec>
   *debug_message = "Import Map: \"" + key + "\" matches with \"" +
                    matched->key + "\" and is mapped to " + url.ElidedString();
   return url;
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 3a37bfe..c9c498a 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -2441,14 +2441,6 @@
   AccessibilityChildrenFromAOMProperty(AOMRelationListProperty::kOwns, owns);
 }
 
-bool AXNodeObject::SupportsARIAOwns() const {
-  if (!GetLayoutObject())
-    return false;
-  const AtomicString& aria_owns = GetAttribute(html_names::kAriaOwnsAttr);
-
-  return !aria_owns.IsEmpty();
-}
-
 // TODO(accessibility): Aria-dropeffect and aria-grabbed are deprecated in
 // aria 1.1 Also those properties are expected to be replaced by a new feature
 // in a future version of WAI-ARIA. After that we will re-implement them
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.h b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
index 18b97cc..edc1999 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
@@ -178,7 +178,6 @@
   bool HasAriaAttribute() const override;
   void AriaDescribedbyElements(AXObjectVector&) const override;
   void AriaOwnsElements(AXObjectVector&) const override;
-  bool SupportsARIAOwns() const override;
   bool SupportsARIADragging() const override;
   void Dropeffects(
       Vector<ax::mojom::blink::Dropeffect>& dropeffects) const override;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index b0f5f63..95190f2 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -1855,7 +1855,7 @@
   if (RuntimeEnabledFeatures::AccessibilityExposeIgnoredNodesEnabled())
     return true;
 
-  if (AXObjectCache().IsAriaOwned(this)) {
+  if (AXObjectCache().IsAriaOwned(this) || HasARIAOwns(GetElement())) {
     // Always include an aria-owned object. It must be a child of the
     // element with aria-owns.
     return true;
@@ -2984,8 +2984,6 @@
   if (role == ax::mojom::blink::Role::kButton)
     role = ButtonRoleType();
 
-  role = RemapAriaRoleDueToParent(role);
-
   // Distinguish between different uses of the "combobox" role:
   //
   // ax::mojom::blink::Role::kComboBoxGrouping:
@@ -3007,60 +3005,6 @@
   return ax::mojom::blink::Role::kUnknown;
 }
 
-ax::mojom::blink::Role AXObject::RemapAriaRoleDueToParent(
-    ax::mojom::blink::Role role) const {
-  // Some objects change their role based on their parent.
-  // However, asking for the unignoredParent calls accessibilityIsIgnored(),
-  // which can trigger a loop.  While inside the call stack of creating an
-  // element, we need to avoid accessibilityIsIgnored().
-  // https://bugs.webkit.org/show_bug.cgi?id=65174
-
-  // Don't return table roles unless inside a table-like container.
-  switch (role) {
-    case ax::mojom::blink::Role::kRow:
-    case ax::mojom::blink::Role::kRowGroup:
-    case ax::mojom::blink::Role::kCell:
-    case ax::mojom::blink::Role::kRowHeader:
-    case ax::mojom::blink::Role::kColumnHeader:
-      for (AXObject* ancestor = ParentObjectUnignored(); ancestor;
-           ancestor = ancestor->ParentObjectUnignored()) {
-        ax::mojom::blink::Role ancestor_aria_role =
-            ancestor->AriaRoleAttribute();
-        if (ancestor_aria_role == ax::mojom::blink::Role::kCell)
-          return ax::mojom::blink::Role::kGenericContainer;  // In another cell,
-                                                             // illegal.
-        if (ancestor->IsTableLikeRole())
-          return role;  // Inside a table: ARIA role is legal.
-      }
-      return ax::mojom::blink::Role::kGenericContainer;  // Not in a table.
-    default:
-      break;
-  }
-
-  if (role != ax::mojom::blink::Role::kListBoxOption &&
-      role != ax::mojom::blink::Role::kMenuItem)
-    return role;
-
-  for (AXObject* parent = ParentObject();
-       parent && !parent->AccessibilityIsIgnored();
-       parent = parent->ParentObject()) {
-    ax::mojom::blink::Role parent_aria_role = parent->AriaRoleAttribute();
-
-    // Selects and listboxes both have options as child roles, but they map to
-    // different roles within WebCore.
-    if (role == ax::mojom::blink::Role::kListBoxOption &&
-        parent_aria_role == ax::mojom::blink::Role::kMenu)
-      return ax::mojom::blink::Role::kMenuItem;
-
-    // If the parent had a different role, then we don't need to continue
-    // searching up the chain.
-    if (parent_aria_role != ax::mojom::blink::Role::kUnknown)
-      break;
-  }
-
-  return role;
-}
-
 bool AXObject::IsEditableRoot() const {
   UpdateCachedAttributeValuesIfNeeded();
   return cached_is_editable_root_;
@@ -4366,6 +4310,25 @@
          aria_role == ax::mojom::blink::Role::kTextFieldWithComboBox;
 }
 
+// static
+bool AXObject::HasARIAOwns(Element* element) {
+  if (!element)
+    return false;
+
+  // A LayoutObject is not required, because an invisible object can still
+  // use aria-owns to point to visible children.
+
+  const AtomicString& aria_owns =
+      element->FastGetAttribute(html_names::kAriaOwnsAttr);
+
+  // TODO: do we need to check !AriaOwnsElements.empty() ? Is that fundamentally
+  // different from HasExplicitlySetAttrAssociatedElements()? And is an element
+  // even necessary in the case of virtual nodes?
+  return !aria_owns.IsEmpty() ||
+         element->HasExplicitlySetAttrAssociatedElements(
+             html_names::kAriaOwnsAttr);
+}
+
 ax::mojom::blink::Role AXObject::AriaRoleToWebCoreRole(const String& value) {
   DCHECK(!value.IsEmpty());
 
@@ -4791,7 +4754,7 @@
     }
 
     // Add properties of interest that often contribute to errors:
-    if (SupportsARIAOwns())
+    if (HasARIAOwns(GetElement()))
       string_builder = string_builder + " @aria-owns";
     if (GetAOMPropertyOrARIAAttribute(AOMRelationProperty::kActiveDescendant))
       string_builder = string_builder + " @aria-activedescendant";
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index 4c8a0fd8..ec93198a 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -807,7 +807,6 @@
   virtual bool SupportsARIADragging() const { return false; }
   virtual void Dropeffects(
       Vector<ax::mojom::blink::Dropeffect>& dropeffects) const {}
-  virtual bool SupportsARIAOwns() const { return false; }
   bool SupportsARIAReadOnly() const;
 
   // Returns 0-based index.
@@ -1199,8 +1198,10 @@
   virtual void SelectionChanged();
 
   // Static helper functions.
+  // TODO(accessibility) Move these to a static helper util class.
   static bool IsARIAControl(ax::mojom::blink::Role);
   static bool IsARIAInput(ax::mojom::blink::Role);
+  static bool HasARIAOwns(Element* element);
   // Is this a widget that requires container widget.
   bool IsSubWidget() const;
   static ax::mojom::blink::Role AriaRoleToWebCoreRole(const String&);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index b80d736..3917c6e 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -1191,11 +1191,8 @@
 
   // Force computation of aria-owns, so that original parents that already
   // computed their children get the aria-owned children removed.
-  if (element->FastHasAttribute(html_names::kAriaOwnsAttr) ||
-      element->HasExplicitlySetAttrAssociatedElements(
-          html_names::kAriaOwnsAttr)) {
+  if (AXObject::HasARIAOwns(element))
     HandleAttributeChangedWithCleanLayout(html_names::kAriaOwnsAttr, element);
-  }
 
   MaybeNewRelationTarget(node, Get(node));
 
diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc
index 5fe5153..ece7570 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request.cc
@@ -808,14 +808,22 @@
     return ScriptPromise();
   }
 
-  // TODO(crbug.com/825270): Reject with SecurityError DOMException if triggered
-  // without user activation.
   bool is_user_gesture =
       LocalFrame::HasTransientUserActivation(DomWindow()->GetFrame());
   if (!is_user_gesture) {
     UseCounter::Count(GetExecutionContext(),
                       WebFeature::kPaymentRequestShowWithoutGesture);
   }
+  if (RuntimeEnabledFeatures::PaymentRequestShowConsumesUserActivationEnabled(
+          GetExecutionContext())) {
+    if (!is_user_gesture) {
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kNotAllowedError,
+          "show() must be called with transient user activation");
+      return ScriptPromise();
+    }
+    // TODO(crbug.com/1130553): consume the user activation as well.
+  }
 
   // TODO(crbug.com/825270): Pretend that a user gesture is provided to allow
   // origins that are part of the Secure Payment Confirmation Origin Trial to
diff --git a/third_party/blink/renderer/modules/payments/payment_request_test.cc b/third_party/blink/renderer/modules/payments/payment_request_test.cc
index 84873d73..89bbf3a 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_test.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request_test.cc
@@ -5,13 +5,16 @@
 #include "third_party/blink/renderer/modules/payments/payment_request.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom-blink-forward.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/event_type_names.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/modules/payments/payment_test_helper.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 namespace {
@@ -299,6 +302,35 @@
   ;
 }
 
+TEST(PaymentRequestTest, CannotShowWithoutUserActivation) {
+  RuntimeEnabledFeatures::SetPaymentRequestShowConsumesUserActivationEnabled(
+      true);
+  PaymentRequestV8TestingScope scope;
+  PaymentRequestMockFunctionScope funcs(scope.GetScriptState());
+  PaymentRequest* request = PaymentRequest::Create(
+      scope.GetExecutionContext(), BuildPaymentMethodDataForTest(),
+      BuildPaymentDetailsInitForTest(), ASSERT_NO_EXCEPTION);
+  request->show(scope.GetScriptState(), scope.GetExceptionState());
+  EXPECT_EQ(scope.GetExceptionState().Code(),
+            ToExceptionCode(DOMExceptionCode::kNotAllowedError));
+  ;
+}
+
+TEST(PaymentRequestTest, ShowConsumesUserActivation) {
+  RuntimeEnabledFeatures::SetPaymentRequestShowConsumesUserActivationEnabled(
+      true);
+  PaymentRequestV8TestingScope scope;
+  PaymentRequestMockFunctionScope funcs(scope.GetScriptState());
+  PaymentRequest* request = PaymentRequest::Create(
+      scope.GetExecutionContext(), BuildPaymentMethodDataForTest(),
+      BuildPaymentDetailsInitForTest(), ASSERT_NO_EXCEPTION);
+
+  LocalFrame::NotifyUserActivation(
+      &(scope.GetFrame()), mojom::UserActivationNotificationType::kTest);
+  request->show(scope.GetScriptState(), ASSERT_NO_EXCEPTION)
+      .Then(funcs.ExpectNoCall(), funcs.ExpectNoCall());
+}
+
 TEST(PaymentRequestTest, RejectShowPromiseOnErrorPaymentMethodNotSupported) {
   PaymentRequestV8TestingScope scope;
   PaymentRequestMockFunctionScope funcs(scope.GetScriptState());
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame.cc b/third_party/blink/renderer/modules/webcodecs/video_frame.cc
index d1f89a84..df35448 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/memory/scoped_refptr.h"
 #include "components/viz/common/gpu/raster_context_provider.h"
 #include "components/viz/common/resources/single_release_callback.h"
 #include "gpu/command_buffer/client/shared_image_interface.h"
@@ -454,20 +455,20 @@
           SkImage::MakeRasterData(info, image_pixels, bytes_per_row);
       image = UnacceleratedStaticBitmapImage::Create(std::move(skImage));
     } else {
-      if (!IsMainThread()) {
-        // TODO(crbug.com/1148849): We should either hop to the media thread and
-        // use the media context, or use the SharedGpuContext. Currently
-        // obtaining the media context requires synchronizing with the main
-        // thread, and PaintCanvasVideoRenderer is not compatible with
-        // SharedGpuContext.
+      scoped_refptr<viz::RasterContextProvider> raster_context_provider;
+      base::WeakPtr<WebGraphicsContext3DProviderWrapper> wrapper =
+          SharedGpuContext::ContextProviderWrapper();
+      if (wrapper && wrapper->ContextProvider()) {
+        raster_context_provider = base::WrapRefCounted(
+            wrapper->ContextProvider()->RasterContextProvider());
+      }
+      if (!raster_context_provider) {
         exception_state.ThrowDOMException(DOMExceptionCode::kOperationError,
                                           "Graphics context unavailable.");
         return ScriptPromise();
       }
-      scoped_refptr<viz::RasterContextProvider> raster_context_provider =
-          Platform::Current()->SharedMainThreadContextProvider();
-      auto* ri = raster_context_provider->RasterInterface();
 
+      auto* ri = raster_context_provider->RasterInterface();
       gpu::SharedImageInterface* shared_image_interface =
           raster_context_provider->SharedImageInterface();
       uint32_t usage = gpu::SHARED_IMAGE_USAGE_GLES2;
diff --git a/third_party/blink/renderer/modules/webid/web_id.cc b/third_party/blink/renderer/modules/webid/web_id.cc
index 2bf48e2..4ffc9b84 100644
--- a/third_party/blink/renderer/modules/webid/web_id.cc
+++ b/third_party/blink/renderer/modules/webid/web_id.cc
@@ -19,7 +19,15 @@
 namespace {
 
 void OnRequestIdToken(ScriptPromiseResolver* resolver,
+                      mojom::blink::RequestIdTokenStatus status,
                       const WTF::String& id_token) {
+  // TODO(kenrb): Provide better messages for different error codes.
+  if (status != mojom::blink::RequestIdTokenStatus::kSuccess) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kNetworkError,
+        "Error loading the identity provider."));
+    return;
+  }
   resolver->Resolve(id_token);
 }
 
@@ -33,8 +41,8 @@
                          ExceptionState& exception_state) {
   auto* context = GetExecutionContext();
 
-  if (!options->hasProvider()) {
-    exception_state.ThrowTypeError("Provider is required.");
+  if (!options->hasProvider() || !options->hasRequest()) {
+    exception_state.ThrowTypeError("Invalid WebIDRequestOptions");
     return ScriptPromise();
   }
 
@@ -62,7 +70,8 @@
   ScriptPromise promise = resolver->Promise();
 
   auth_request_->RequestIdToken(
-      provider, WTF::Bind(&OnRequestIdToken, WrapPersistent(resolver)));
+      provider, options->request(),
+      WTF::Bind(&OnRequestIdToken, WrapPersistent(resolver)));
 
   return promise;
 }
diff --git a/third_party/blink/renderer/modules/webid/web_id_request_options.idl b/third_party/blink/renderer/modules/webid/web_id_request_options.idl
index b84c01f..6b7bcb69 100644
--- a/third_party/blink/renderer/modules/webid/web_id_request_options.idl
+++ b/third_party/blink/renderer/modules/webid/web_id_request_options.idl
@@ -5,5 +5,8 @@
 // https://github.com/WICG/WebID
 
 dictionary WebIDRequestOptions {
+  // URL for the Identity Provider.
   USVString provider;
+  // Serialized request parameters.
+  USVString request;
 };
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h
index ae96ebec2..611c685 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h
@@ -74,6 +74,9 @@
   void CopyVideoFrame(media::PaintCanvasVideoRenderer* video_render,
                       media::VideoFrame* video_frame,
                       cc::PaintCanvas* canvas) override {}
+  viz::RasterContextProvider* RasterContextProvider() const override {
+    return nullptr;
+  }
 
  private:
   cc::StubDecodeCache image_decode_cache_;
diff --git a/third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.cc b/third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.cc
index 843840a..cb820be 100644
--- a/third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.cc
+++ b/third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.cc
@@ -49,7 +49,7 @@
                          /*can_use_lcd_text=*/false,
                          /*content_supports_distance_field_text=*/false,
                          /*max_texture_size=*/0,
-                         /*original_ctm=*/SkMatrix::I()) {
+                         /*original_ctm=*/SkM44()) {
   serialize_options_.for_identifiability_study = true;
   constexpr size_t kInitialSize = 16 * 1024;
   if (IdentifiabilityStudySettings::Get()->IsTypeAllowed(
diff --git a/third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h b/third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h
index e89c030..4e1891f 100644
--- a/third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h
+++ b/third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h
@@ -90,6 +90,9 @@
   void CopyVideoFrame(media::PaintCanvasVideoRenderer* video_render,
                       media::VideoFrame* video_frame,
                       cc::PaintCanvas* canvas) override {}
+  viz::RasterContextProvider* RasterContextProvider() const override {
+    return nullptr;
+  }
 
  private:
   cc::StubDecodeCache stub_image_decode_cache_;
diff --git a/third_party/blink/renderer/platform/graphics/test/mock_paint_canvas.h b/third_party/blink/renderer/platform/graphics/test/mock_paint_canvas.h
index 24eb3c1..8036e7c 100644
--- a/third_party/blink/renderer/platform/graphics/test/mock_paint_canvas.h
+++ b/third_party/blink/renderer/platform/graphics/test/mock_paint_canvas.h
@@ -34,6 +34,7 @@
   MOCK_METHOD1(rotate, void(SkScalar degrees));
   MOCK_METHOD1(concat, void(const SkMatrix& matrix));
   MOCK_METHOD1(setMatrix, void(const SkMatrix& matrix));
+  MOCK_METHOD1(setMatrix, void(const SkM44& matrix));
   MOCK_METHOD3(clipRect,
                void(const SkRect& rect, SkClipOp op, bool do_anti_alias));
   MOCK_METHOD3(clipRRect,
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h b/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h
index c240567..f3af418 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h
@@ -65,18 +65,6 @@
   // https://html.spec.whatwg.org/C/#concept-settings-object-referrer-policy
   virtual network::mojom::ReferrerPolicy GetReferrerPolicy() const = 0;
 
-  // |GetReferrerPolicyDisregardingMetaTagsContainingLists|
-  // returns the policy that would have been set had we been ignoring all <meta
-  // name=referrer> tags with values comma-separated lists of policies. This
-  // allows histogramming the proportion of requests that would end up with
-  // different referrers were these tags ignored, helping interpret the impact
-  // of removing support for them (which is inconsistent with the spec and other
-  // engines).
-  virtual base::Optional<network::mojom::ReferrerPolicy>
-  GetReferrerPolicyDisregardingMetaTagsContainingLists() const {
-    return base::nullopt;
-  }
-
   // "referrerURL" used in the "Determine request's Referrer" algorithm:
   // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
   virtual const String GetOutgoingReferrer() const = 0;
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index 8bae393b..8f04af0 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -295,9 +295,9 @@
 
 // This function corresponds with step 2 substep 7 of
 // https://fetch.spec.whatwg.org/#main-fetch.
-void SetReferrer(ResourceRequest& request,
-                 const FetchClientSettingsObject& fetch_client_settings_object,
-                 UseCounter& use_counter) {
+void SetReferrer(
+    ResourceRequest& request,
+    const FetchClientSettingsObject& fetch_client_settings_object) {
   String referrer_to_use = request.ReferrerString();
   network::mojom::ReferrerPolicy referrer_policy_to_use =
       request.GetReferrerPolicy();
@@ -305,42 +305,12 @@
   if (referrer_to_use == Referrer::ClientReferrerString())
     referrer_to_use = fetch_client_settings_object.GetOutgoingReferrer();
 
-  bool used_referrer_policy_from_context = false;
-  if (referrer_policy_to_use == network::mojom::ReferrerPolicy::kDefault) {
-    used_referrer_policy_from_context = true;
+  if (referrer_policy_to_use == network::mojom::ReferrerPolicy::kDefault)
     referrer_policy_to_use = fetch_client_settings_object.GetReferrerPolicy();
-  }
 
   Referrer generated_referrer = SecurityPolicy::GenerateReferrer(
       referrer_policy_to_use, request.Url(), referrer_to_use);
 
-  // The request's referrer would be different in the absence of <meta
-  // name=referrer> tags with comma-separated-list values exactly when both
-  // 1. the request falls back to its client settings object's policy; and
-  // 2. if we recompute the referrer using the value of the client settings
-  // object's policy, disregarding any policy that came from a meta tag with a
-  // comma-separated value, we obtain a different referrer.
-  if (used_referrer_policy_from_context) {
-    base::Optional<network::mojom::blink::ReferrerPolicy>
-        policy_but_for_meta_tags_with_policy_lists =
-            fetch_client_settings_object
-                .GetReferrerPolicyDisregardingMetaTagsContainingLists();
-
-    bool referrer_would_be_different_absent_meta_tags_with_policy_lists =
-        policy_but_for_meta_tags_with_policy_lists.has_value() &&
-        (generated_referrer.referrer !=
-         SecurityPolicy::GenerateReferrer(
-             *policy_but_for_meta_tags_with_policy_lists, request.Url(),
-             referrer_to_use)
-             .referrer);
-
-    if (referrer_would_be_different_absent_meta_tags_with_policy_lists) {
-      use_counter.CountUse(
-          mojom::WebFeature::
-              kHTMLMetaElementReferrerPolicyMultipleTokensAffectingRequest);
-    }
-  }
-
   request.SetReferrerString(generated_referrer.referrer);
   request.SetReferrerPolicy(generated_referrer.referrer_policy);
 }
@@ -965,8 +935,7 @@
                                              http_names::kGET &&
                                          !params.IsStaleRevalidation());
 
-  SetReferrer(resource_request, properties_->GetFetchClientSettingsObject(),
-              GetUseCounter());
+  SetReferrer(resource_request, properties_->GetFetchClientSettingsObject());
 
   resource_request.SetExternalRequestStateFromRequestorAddressSpace(
       properties_->GetFetchClientSettingsObject().GetAddressSpace());
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 95208ec..14fc7d2 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1519,6 +1519,10 @@
       name: "PaymentRequestMerchantValidationEvent",
       status: "experimental",
     },
+    // Enables the correct behavior for show(): crbug.com/825270
+    {
+      name: "PaymentRequestShowConsumesUserActivation",
+    },
     {
       name: "PaymentRetry",
       status: "stable",
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index fb658d2..39d54b4 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3476,9 +3476,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-align-justify-margin-border-padding-vertical-rl.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-align-justify-margin-border-padding.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-align-justify-overflow.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-align-justify-stretch-with-orthogonal-flows.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-align-justify-stretch.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-align.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-alignment-implies-size-change-029.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-alignment-implies-size-change-030.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-alignment-implies-size-change-031.html [ Failure ]
@@ -3587,12 +3584,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-gutters-013.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-gutters-014.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-gutters-and-alignment.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-alignment-with-orthogonal-flows-vertical-lr.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-alignment-with-orthogonal-flows-vertical-rl.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-alignment-with-orthogonal-flows.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-auto-margins-alignment-vertical-lr.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-auto-margins-alignment-vertical-rl.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-auto-margins-alignment.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-1.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-2.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-3.html [ Failure ]
@@ -3677,6 +3668,8 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-002.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-003.html [ Failure ]
+crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/replaced-alignment-with-aspect-ratio-001.tentative.html [ Pass ]
+crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/replaced-alignment-with-aspect-ratio-002.tentative.html [ Pass ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-002-b.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-002.html [ Failure ]
@@ -3857,21 +3850,15 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-order-property-auto-placement-004.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-order-property-auto-placement-005.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/percentage-size-indefinite-replaced.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/percentage-size-replaced-subitems-001.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/percentage-size-subitems-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/remove-svg-grid-item-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-layout-properties.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/column-property-should-not-apply-on-grid-container-001.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/compute-intrinsic-widths-scrollbar-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-box-sizing-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-container-ignores-first-letter-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-container-ignores-first-line-001.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-container-margin-border-padding-scrollbar-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-container-scrollbar-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-container-scrollbar-vertical-lr-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-container-scrollbar-vertical-rl-001.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-container-scrollbars-sizing-001.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-container-scrollbars-sizing-002.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-container-sizing-constraints-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-display-grid-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-first-letter-001.html [ Failure ]
@@ -3899,7 +3886,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-margins-no-collapse-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-margins-no-collapse-002.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-min-max-height-001.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-size-shrink-to-fit-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-vertical-align-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-tracks-stretched-with-different-flex-factors-sum.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/implicit-grids/grid-support-grid-auto-columns-rows-003.html [ Failure ]
@@ -3935,6 +3921,8 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/layout-algorithm/grid-minimum-contribution-baseline-shim.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/layout-algorithm/grid-percent-rows-filled-shrinkwrap-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/layout-algorithm/grid-percent-rows-spanned-shrinkwrap-001.html [ Failure ]
+crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/layout-algorithm/grid-percent-cols-filled-shrinkwrap-001.html [ Failure ]
+crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/layout-algorithm/grid-percent-cols-spanned-shrinkwrap-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/layout-algorithm/grid-stretch-respects-min-size-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/parsing/grid-columns-rows-get-set-multiple.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/parsing/grid-content-sized-columns-resolution.html [ Failure ]
@@ -3971,28 +3959,20 @@
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-auto-repeat-huge-grid.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-columns-rows-get-set.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-element-auto-repeat-get-set.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-element-border-grid-item.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-element-border-padding-grid-item.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-element-empty-row-column.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-element-min-max-width.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-element-padding-grid-item.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-element-padding-margin.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-element-repeat-get-set.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-grow-tracks-to-their-max.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-initialize-span-one-items.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-intrinsic-maximums.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-addition-auto-placement-update.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-addition-track-breadth-update.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-auto-margins-and-stretch.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-auto-sized-align-justify-margin-border-padding.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-grid-container-percentage-rows.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-margin-resolution.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-multiple-minmax-content-resolution.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-named-grid-line-resolution.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-negative-indexes.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-negative-integer-explicit-grid-resolution.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-negative-position-resolution.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-overflow-paint.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-overflow.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-positioning-with-orthogonal-flows.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-removal-auto-placement-update.html [ Failure Crash ]
@@ -4000,9 +3980,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-sizing-with-orthogonal-flows.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-spanning-and-orthogonal-flows.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-spanning-resolution.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-stretch-with-margins-borders-padding-vertical-lr.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-stretch-with-margins-borders-padding-vertical-rl.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-stretch-with-margins-borders-padding.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-text-background-not-interleaved.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-unknown-named-grid-line-resolution.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-with-border-in-fr.html [ Failure ]
@@ -4016,7 +3993,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-justify-content-vertical-rl.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-justify-content.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-only-abspos-item-computed-style-crash.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-painting-rtl.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-preferred-logical-widths.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-self-baseline-two-dimensional.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-template-columns-rows-computed-style-gaps-content-alignment.html [ Failure ]
@@ -4029,7 +4005,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/implicit-position-dynamic-change.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/implicit-rows-auto-resolution.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/implicit-tracks-before-explicit.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/justify-self-cell.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/mark-as-infinitely-growable.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/maximize-tracks-definite-indefinite-height.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/maximize-tracks-definite-indefinite-width.html [ Failure ]
@@ -4059,7 +4034,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/percent-padding-margin-resolution-grid-item.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/percent-resolution-grid-item-children.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/percent-track-breadths-regarding-container-size.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/place-cell-by-index.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/positioned-grid-container-percentage-tracks.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/preferred-width-computed-after-layout.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/relayout-indefinite-heights.html [ Failure ]
@@ -5943,3 +5917,7 @@
 # Sheriff 2020-12-03
 crbug.com/1154940 [ Win7 ] inspector-protocol/overlay/overlay-persistent-overlays-with-emulation.js [ Pass Failure ]
 crbug.com/1155125 [ Linux ] http/tests/history/replacestate-post-to-get-2.html [ Pass Timeout ]
+
+# Sheriff 2020-12-04
+# Failing on Webkit Linux Leak
+crbug.com/1155771 [ Linux ] external/wpt/dom/events/AddEventListenerOptions-signal.any.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/WebGPUExpectations b/third_party/blink/web_tests/WebGPUExpectations
index 7294a6f..a6fb559 100644
--- a/third_party/blink/web_tests/WebGPUExpectations
+++ b/third_party/blink/web_tests/WebGPUExpectations
@@ -49,21 +49,50 @@
 wpt_internal/webgpu/cts.html?q=webgpu:api,validation,buffer,mapping:mapAsync,usage,* [ Failure ]
 wpt_internal/webgpu/cts.html?q=webgpu:api,validation,buffer,mapping:mapAsync_,* [ Failure ]
 
+# Precision. Need a better way to compare expected values
 wpt_internal/webgpu/cts.html?q=webgpu:util,texture,texelData:unorm_texel_data_in_shader:format="rgba8unorm-srgb";* [ Failure ]
 wpt_internal/webgpu/cts.html?q=webgpu:util,texture,texelData:unorm_texel_data_in_shader:format="bgra8unorm-srgb";* [ Failure ]
 wpt_internal/webgpu/cts.html?q=webgpu:util,texture,texelData:ufloat_texel_data_in_shader:format="rg11b10ufloat";* [ Failure ]
 wpt_internal/webgpu/cts.html?q=webgpu:util,texture,texelData:ufloat_texel_data_in_shader:format="rgb9e5ufloat";* [ Failure ]
 
-# Many formats failing on Mac. Crash with validation layer on Linux. Also slow.
-# TODO: Split, once failures are cleaned up.
-wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";* [ Slow Failure Crash ]
+# Many formats failing on Windows
+[ Win ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";* [ Failure ]
 
-# All failing, crash with validation layer. Seems also slow.
-wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="DepthTest";* [ Slow Failure Crash ]
+# r8unorm+rg8unorm only fail on Mac Intel mipLevelCount=5;uninitializeMethod="StoreOpClear";nonPowerOfTwo=false;
+[ Mac ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="r8unorm";* [ Failure ]
+[ Mac ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rg8unorm";* [ Failure ]
+[ Mac ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rg11b10ufloat";* [ Failure ]
+[ Mac ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rgb9e5ufloat";* [ Failure ]
 
-# Failing on Mac in a few cases (same as CopyToBuffer).
+# Passing on Linux. Split by format.
+[ Linux ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="r8unorm";* [ Pass ]
+
+# Failing because crbug.com/tint/329 breaks FragDepth writes.
+# Win missing resource state D3D12_RESOURCE_STATE_DEPTH_WRITE
+[ Linux ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="DepthTest";* [ Failure ]
+[ Mac ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="DepthTest";* [ Failure ]
+[ Win ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="DepthTest";* [ Slow Failure Crash ]
+
+# Crash with validation layer. May also be slow.
+# Missing D3D12_RESOURCE_STATE_DEPTH_WRITE
+[ Win ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="StencilTest";* [ Slow Failure Crash ]
+
 # Many cases failing on Windows. May also be slow.
-wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToTexture";* [ Slow Failure ]
+[ Win ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToTexture";* [ Slow Failure ]
+[ Win ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToBuffer";format="bgra8unorm";* [ Slow Pass ]
+
+# Only on Mac Intel mipLevelCount=5; ...; uninitializeMethod=StoreOpClear
+[ Mac ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToBuffer";format="depth32float";* [ Failure ]
+[ Mac ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToTexture";format="depth32float";* [ Failure ]
+
+# Only on Mac Intel ...;aspect="all";mipLevelCount=5;sampleCount=1;uninitializeMethod="_StoreOpClear;dimension="2d";sliceCount=1;nonPowerOfTwo=false
+[ Mac ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToBuffer";format="r8unorm";* [ Failure ]
+[ Mac ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToBuffer";format="rg8unorm";* [ Failure ]
+[ Mac ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToTexture";format="r8unorm";* [ Failure ]
+[ Mac ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToTexture";format="rg8unorm";* [ Failure ]
+
+[ Linux ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToTexture";format="bgra8unorm";* [ Pass ]
+[ Linux ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToBuffer";format="bgra8unorm";* [ Pass ]
 
 # Fails or crashes on numerous combinations of backends, hardware, and validation layers
 wpt_internal/webgpu/cts.html?q=webgpu:api,operation,render_pass,resolve:* [ Failure Crash ]
@@ -127,15 +156,9 @@
 # Rendering differs slightly from ref.
 crbug.com/1083478 [ Mac ] wpt_internal/webgpu/webgpu/web_platform/reftests/canvas_complex_bgra8unorm.html [ Failure ]
 
+# Precision. Need a better way to compare expected values
 [ Mac ] wpt_internal/webgpu/cts.html?q=webgpu:util,texture,texelData:unorm_texel_data_in_shader:format="rgb10a2unorm";* [ Failure ]
 
-# Widespread failures with depth32float
-[ Mac ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToBuffer";format="depth32float";* [ Failure ]
-[ Mac ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToTexture";format="depth32float";* [ Failure ]
-
-# Only ...;aspect="all";mipLevelCount=5;sampleCount=1;uninitializeMethod="_StoreOpClear;dimension="2d";sliceCount=1;nonPowerOfTwo=false
-[ Mac ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToBuffer";format="r8unorm";* [ Failure ]
-[ Mac ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToBuffer";format="rg8unorm";* [ Failure ]
 
 #
 # Linux (Vulkan) specific
@@ -157,5 +180,3 @@
 # Very flaky on Windows/Linux, especially (but not exclusively!) with backend validation
 crbug.com/1087130 [ Win ] wpt_internal/webgpu/cts.html?q=webgpu:api,validation,createView:* [ RetryOnFailure ]
 
-# Crash with validation layer. May also be slow.
-[ Win ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="StencilTest";* [ Slow Failure Crash ]
diff --git a/third_party/blink/web_tests/accessibility/aria-owns-grid-expected.txt b/third_party/blink/web_tests/accessibility/aria-owns-grid-expected.txt
deleted file mode 100644
index 2705000b..0000000
--- a/third_party/blink/web_tests/accessibility/aria-owns-grid-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Aria-owns works on ARIA grids. assert_equals: expected 3 but got 0
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/accessibility/canvas-select-row-expected.txt b/third_party/blink/web_tests/accessibility/canvas-select-row-expected.txt
deleted file mode 100644
index 232967b..0000000
--- a/third_party/blink/web_tests/accessibility/canvas-select-row-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL An ARIA cell without a layout object does not crash assert_equals: expected "AXRole: AXCell" but got "AXRole: AXGenericContainer"
-PASS An ARIA row in a select in a canvas does not crash
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/accessibility/presentation-owned-elements-expected.txt b/third_party/blink/web_tests/accessibility/presentation-owned-elements-expected.txt
index fc0550c..6588fe1e 100644
--- a/third_party/blink/web_tests/accessibility/presentation-owned-elements-expected.txt
+++ b/third_party/blink/web_tests/accessibility/presentation-owned-elements-expected.txt
@@ -84,7 +84,7 @@
             AXRole: AXInlineTextBox "The row for "Explicit th" has a row role even if table has a presentation role because it has an explicit role."
     AXRole: AXPresentational
         AXRole: AXGenericContainer
-            AXRole: AXGenericContainer
+            AXRole: AXRow
                 AXRole: AXGenericContainer
                     AXRole: AXStaticText "Explicit th"
                         AXRole: AXInlineTextBox "Explicit th"
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 3ebebc2..0d46d96 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -146193,6 +146193,58 @@
        {}
       ]
      ],
+     "selectors-dir-selector-change-001.html": [
+      "f952389643834f4a67062bf722d5ce4422253094",
+      [
+       null,
+       [
+        [
+         "/css/selectors/selectors-dir-selector-change-001-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "selectors-dir-selector-change-002.html": [
+      "bc032b59541335d49f7ffe56919cfad9b7cd2fee",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "selectors-dir-selector-change-003.html": [
+      "f4000155a8d5004b401ce5964f535979cc4ec256",
+      [
+       null,
+       [
+        [
+         "/css/selectors/selectors-dir-selector-change-003-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "selectors-dir-selector-change-004.html": [
+      "4c76b291bad7ffde21d624a3abc03c7b3cc99ac9",
+      [
+       null,
+       [
+        [
+         "/css/selectors/selectors-dir-selector-change-004-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "selectors-dir-selector-ltr-001.html": [
       "9b90832a218debcf98a8a23bed7b0e679bd472ac",
       [
@@ -146206,6 +146258,32 @@
        {}
       ]
      ],
+     "selectors-dir-selector-ltr-002.html": [
+      "bbb3f26a780b13d434d99ca858b44f7db635047b",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "selectors-dir-selector-ltr-003.html": [
+      "821a33616fca6940618d3e27c07963eba4c0f748",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "selectors-dir-selector-rtl-001.html": [
       "9b7bfd090a3c6736c3a30329912558c3c31f32fc",
       [
@@ -146219,6 +146297,19 @@
        {}
       ]
      ],
+     "selectors-dir-selector-white-space-001.html": [
+      "d3128a03734dc9806e3136ef6657b6087d9d76fa",
+      [
+       null,
+       [
+        [
+         "/css/selectors/selectors-dir-selector-white-space-001-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "selectors-empty-001.xml": [
       "3b882debcc717c3e85de8695476eca3fd5238e8a",
       [
@@ -210511,6 +210602,22 @@
       "3b768b36a50cd46c3fae0de91c63f8231fde7811",
       []
      ],
+     "selectors-dir-selector-change-001-ref.html": [
+      "8c79c8328f0b2bf88b320956efdd689a97130550",
+      []
+     ],
+     "selectors-dir-selector-change-003-ref.html": [
+      "8c79c8328f0b2bf88b320956efdd689a97130550",
+      []
+     ],
+     "selectors-dir-selector-change-004-ref.html": [
+      "9a130cb2e5c71b9948e68022ad6ef89cc0ecda05",
+      []
+     ],
+     "selectors-dir-selector-white-space-001-ref.html": [
+      "955a2dd603d625975934e1ed6a42b4ea077093b7",
+      []
+     ],
      "selectors-empty-001-ref.xml": [
       "3f1b3f42a89399fec3ca03c4f69aa1e75527a0a3",
       []
@@ -231306,15 +231413,7 @@
       ],
       "pseudo-classes": {
        "dir-expected.txt": [
-        "8d4f29793fd8ae7579e70dbb941c6b754e003b6b",
-        []
-       ],
-       "dir-html-input-dynamic-text-expected.txt": [
-        "d26b681aec70675ecaf32b09d874c73487b81aa2",
-        []
-       ],
-       "dir01-expected.txt": [
-        "dc08da08ba5c7cd7ceb5d3585b23b4810ae24a4e",
+        "dc9646444d83d4389f765d7a10af342ed2168547",
         []
        ],
        "focus-iframe.html": [
@@ -244635,71 +244734,21 @@
      []
     ],
     "idlharness.any-expected.txt": [
-     "bf60a89d442ca7f26c61d19af6531aed1a2f5227",
+     "ac189daf9bf5a0ef428943eb5ec21e1cfa30d8d5",
      []
     ],
     "idlharness.any.serviceworker-expected.txt": [
-     "bf60a89d442ca7f26c61d19af6531aed1a2f5227",
+     "ac189daf9bf5a0ef428943eb5ec21e1cfa30d8d5",
      []
     ],
     "idlharness.any.sharedworker-expected.txt": [
-     "bf60a89d442ca7f26c61d19af6531aed1a2f5227",
+     "ac189daf9bf5a0ef428943eb5ec21e1cfa30d8d5",
      []
     ],
     "idlharness.any.worker-expected.txt": [
-     "bf60a89d442ca7f26c61d19af6531aed1a2f5227",
+     "ac189daf9bf5a0ef428943eb5ec21e1cfa30d8d5",
      []
     ],
-    "readable-byte-streams": {
-     "bad-buffers-and-views.any-expected.txt": [
-      "9a39ea475532ce53b4cb586da6ebb4efde071336",
-      []
-     ],
-     "bad-buffers-and-views.any.serviceworker-expected.txt": [
-      "9a39ea475532ce53b4cb586da6ebb4efde071336",
-      []
-     ],
-     "bad-buffers-and-views.any.sharedworker-expected.txt": [
-      "9a39ea475532ce53b4cb586da6ebb4efde071336",
-      []
-     ],
-     "bad-buffers-and-views.any.worker-expected.txt": [
-      "9a39ea475532ce53b4cb586da6ebb4efde071336",
-      []
-     ],
-     "construct-byob-request.any-expected.txt": [
-      "129e634e5436d76e0aa8e404b88375ad53962cd8",
-      []
-     ],
-     "construct-byob-request.any.serviceworker-expected.txt": [
-      "8804b86fbc5d89953981e414412cc32a7980e454",
-      []
-     ],
-     "construct-byob-request.any.sharedworker-expected.txt": [
-      "8ec519e10cc02b3a8cbb0374726eebd34a701441",
-      []
-     ],
-     "construct-byob-request.any.worker-expected.txt": [
-      "129e634e5436d76e0aa8e404b88375ad53962cd8",
-      []
-     ],
-     "general.any-expected.txt": [
-      "97c2cf18a6a681f73c4f4a100299c7c7cb271215",
-      []
-     ],
-     "general.any.serviceworker-expected.txt": [
-      "97c2cf18a6a681f73c4f4a100299c7c7cb271215",
-      []
-     ],
-     "general.any.sharedworker-expected.txt": [
-      "97c2cf18a6a681f73c4f4a100299c7c7cb271215",
-      []
-     ],
-     "general.any.worker-expected.txt": [
-      "97c2cf18a6a681f73c4f4a100299c7c7cb271215",
-      []
-     ]
-    },
     "readable-streams": {
      "async-iterator.any-expected.txt": [
       "8ae98efa8f5eb50dc91bc430108c9e048cd71c46",
@@ -259259,7 +259308,7 @@
        []
       ],
       "002.worker-expected.txt": [
-       "3e0b101b62232c58f425fe266ca066344ec41b46",
+       "f37a7e76aecde09accca0e424d745ac46b1c3279",
        []
       ],
       "003-expected.txt": [
@@ -259275,7 +259324,7 @@
        []
       ],
       "004.any.sharedworker-expected.txt": [
-       "7e4635f6554ad6f64867d14071735b1df001659e",
+       "8d98263fff7f785501cce3e0d371c928e9201dcb",
        []
       ]
      },
@@ -311008,6 +311057,20 @@
        {}
       ]
      ],
+     "selectors-dir-selector-auto.html": [
+      "d53e989f69e172ea8cb0eb0a11863c767f4f5bf2",
+      [
+       null,
+       {}
+      ]
+     ],
+     "selectors-dir-selector-querySelector.html": [
+      "a05e3fea78f378e37c1b73f7f9e2e3559db12f16",
+      [
+       null,
+       {}
+      ]
+     ],
      "user-invalid.html": [
       "05cf2b679039c61732054217cf562bfeb7c3d23a",
       [
@@ -383551,6 +383614,15 @@
       }
      ]
     ],
+    "MediaStreamTrack-clone.https.html": [
+     "c2b3e2bf8ab1b8c32cdc207181240c695e202656",
+     [
+      null,
+      {
+       "testdriver": true
+      }
+     ]
+    ],
     "MediaStreamTrack-getCapabilities-fast.html": [
      "55272d1499517a6fda0b7e06068928b4e1127b27",
      [
@@ -413695,7 +413767,7 @@
     ],
     "readable-byte-streams": {
      "bad-buffers-and-views.any.js": [
-      "0777208da44b29b822bf9345dbe20e33bb5708b9",
+      "d4ad483d9c39566b8bc5b4d09676d05eac8879e5",
       [
        null,
        {
@@ -429111,7 +429183,7 @@
        ]
       ],
       "ctor-audiobuffer.html": [
-       "9845d5eaba384cced3c63ddbf4df1400b31f4994",
+       "fbe6e42e3178f84d108bfd1c62518a4d13eb2dff",
        [
         null,
         {}
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/column-widths-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/column-widths-expected.txt
index 79fcbab..b44c757 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/column-widths-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/column-widths-expected.txt
@@ -39,5 +39,7 @@
 PASS table 29
 PASS table 30
 PASS table 31
+PASS table 32
+PASS table 33
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/column-widths.html b/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/column-widths.html
index 52cb5b6..b151c226 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/column-widths.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/column-widths.html
@@ -276,7 +276,23 @@
 <table style="width:1px" data-expected-width=234>
   <col style="width:50%">
   <td style="width:100px" data-expected-width=10><div style="width:10px">10</div></td>
-  <td><div style="width:200px">200</div>
+  <td><div style="width:200px">200</div></td>
+</table>
+
+<p class="testdesc">col fixed td fixed inside table-layout:fixed
+constrained columns take precedence in fixed layout.
+<table style="width:324px;table-layout:fixed" data-expected-width=324>
+  <col style="width:100px">
+  <td style="width:200px" data-expected-width=100>100</td>
+  <td data-expected-width=200><div style="width:400px" >200</div></td>
+</table>
+
+<p class="testdesc">col percent td fixed inside table-layout:fixed
+constrained columns take precedence in fixed layout.
+<table style="width:324px;table-layout:fixed" data-expected-width=324>
+  <col style="width:50%">
+  <td style="width:200px" data-expected-width=150>150</td>
+  <td><div style="width:400px">150</div></td>
 </table>
 </main>
 <script>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/data-driven/resolving.https-expected.txt b/third_party/blink/web_tests/external/wpt/import-maps/data-driven/resolving.https-expected.txt
deleted file mode 100644
index cdab62d..0000000
--- a/third_party/blink/web_tests/external/wpt/import-maps/data-driven/resolving.https-expected.txt
+++ /dev/null
@@ -1,175 +0,0 @@
-This is a testharness.js-based test.
-Found 171 tests; 165 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS global setup
-PASS Test helper: fetching and sanity checking test JSON: resources/scopes.json
-PASS Test helper: fetching and sanity checking test JSON: resources/empty-import-map.json
-PASS Test helper: fetching and sanity checking test JSON: resources/packages-via-trailing-slashes.json
-PASS Test helper: fetching and sanity checking test JSON: resources/tricky-specifiers.json
-PASS Test helper: fetching and sanity checking test JSON: resources/url-specifiers.json
-PASS Test helper: fetching and sanity checking test JSON: resources/data-base-url.json
-PASS Test helper: fetching and sanity checking test JSON: resources/scopes-exact-vs-prefix.json
-PASS Test helper: fetching and sanity checking test JSON: resources/overlapping-entries.json
-PASS Test helper: fetching and sanity checking test JSON: resources/resolving-null.json
-PASS global cleanup
-PASS Fallback to toplevel and between scopes: should fall back to `imports` when no scopes match: a
-PASS Fallback to toplevel and between scopes: should fall back to `imports` when no scopes match: b
-PASS Fallback to toplevel and between scopes: should fall back to `imports` when no scopes match: c
-PASS Fallback to toplevel and between scopes: should fall back to `imports` when no scopes match: d
-PASS Fallback to toplevel and between scopes: should use a direct scope override: a
-PASS Fallback to toplevel and between scopes: should use a direct scope override: b
-PASS Fallback to toplevel and between scopes: should use a direct scope override: c
-PASS Fallback to toplevel and between scopes: should use a direct scope override: d
-PASS Fallback to toplevel and between scopes: should use an indirect scope override: a
-PASS Fallback to toplevel and between scopes: should use an indirect scope override: b
-PASS Fallback to toplevel and between scopes: should use an indirect scope override: c
-PASS Fallback to toplevel and between scopes: should use an indirect scope override: d
-PASS Relative URL scope keys: An empty string scope is a scope with import map base URL: a
-PASS Relative URL scope keys: An empty string scope is a scope with import map base URL: b
-PASS Relative URL scope keys: An empty string scope is a scope with import map base URL: c
-PASS Relative URL scope keys: './' scope is a scope with import map base URL's directory: a
-PASS Relative URL scope keys: './' scope is a scope with import map base URL's directory: b
-PASS Relative URL scope keys: './' scope is a scope with import map base URL's directory: c
-PASS Relative URL scope keys: '../' scope is a scope with import map base URL's parent directory: a
-PASS Relative URL scope keys: '../' scope is a scope with import map base URL's parent directory: b
-PASS Relative URL scope keys: '../' scope is a scope with import map base URL's parent directory: c
-PASS Package-like scenarios: Base URLs inside the scope should use the scope if the scope has matching keys: lodash-dot
-PASS Package-like scenarios: Base URLs inside the scope should use the scope if the scope has matching keys: lodash-dot/foo
-PASS Package-like scenarios: Base URLs inside the scope should use the scope if the scope has matching keys: lodash-dotdot
-PASS Package-like scenarios: Base URLs inside the scope should use the scope if the scope has matching keys: lodash-dotdot/foo
-PASS Package-like scenarios: Base URLs inside the scope fallback to less specific scope: moment
-PASS Package-like scenarios: Base URLs inside the scope fallback to less specific scope: vue
-PASS Package-like scenarios: Base URLs inside the scope fallback to toplevel: moment/foo
-PASS Package-like scenarios: Base URLs outside a scope shouldn't use the scope even if the scope has matching keys: lodash-dot
-PASS Package-like scenarios: Base URLs outside a scope shouldn't use the scope even if the scope has matching keys: lodash-dotdot
-PASS Package-like scenarios: Base URLs outside a scope shouldn't use the scope even if the scope has matching keys: lodash-dot/foo
-PASS Package-like scenarios: Base URLs outside a scope shouldn't use the scope even if the scope has matching keys: lodash-dotdot/foo
-PASS Package-like scenarios: Fallback to toplevel or not, depending on trailing slash match: moment
-PASS Package-like scenarios: Fallback to toplevel or not, depending on trailing slash match: moment/foo
-PASS Package-like scenarios: should still fail for package-like specifiers that are not declared: underscore/
-PASS Package-like scenarios: should still fail for package-like specifiers that are not declared: underscore/foo
-PASS valid relative specifiers: ./foo
-PASS valid relative specifiers: ./foo/bar
-PASS valid relative specifiers: ./foo/../bar
-PASS valid relative specifiers: ./foo/../../bar
-PASS valid relative specifiers: ../foo
-PASS valid relative specifiers: ../foo/bar
-PASS valid relative specifiers: ../../../foo/bar
-PASS valid relative specifiers: /foo
-PASS valid relative specifiers: /foo/bar
-PASS valid relative specifiers: /../../foo/bar
-PASS valid relative specifiers: /../foo/../bar
-PASS HTTPS scheme absolute URLs: https://fetch-scheme.net
-PASS HTTPS scheme absolute URLs: https:fetch-scheme.org
-PASS HTTPS scheme absolute URLs: https://fetch%2Dscheme.com/
-PASS HTTPS scheme absolute URLs: https://///fetch-scheme.com///
-PASS valid relative URLs that are invalid as specifiers should fail: invalid-specifier
-PASS valid relative URLs that are invalid as specifiers should fail: \invalid-specifier
-PASS valid relative URLs that are invalid as specifiers should fail: :invalid-specifier
-PASS valid relative URLs that are invalid as specifiers should fail: @invalid-specifier
-PASS valid relative URLs that are invalid as specifiers should fail: %2E/invalid-specifier
-PASS valid relative URLs that are invalid as specifiers should fail: %2E%2E/invalid-specifier
-PASS valid relative URLs that are invalid as specifiers should fail: .%2Finvalid-specifier
-PASS invalid absolute URLs should fail: https://invalid-url.com:demo
-PASS invalid absolute URLs should fail: http://[invalid-url.com]/
-PASS Package-like scenarios: package main modules: moment
-PASS Package-like scenarios: package main modules: lodash-dot
-PASS Package-like scenarios: package main modules: lodash-dotdot
-PASS Package-like scenarios: package submodules: moment/foo
-PASS Package-like scenarios: package submodules: lodash-dot/foo
-PASS Package-like scenarios: package submodules: lodash-dotdot/foo
-PASS Package-like scenarios: package names that end in a slash should just pass through: moment/
-PASS Package-like scenarios: package modules that are not declared should fail: underscore/
-PASS Package-like scenarios: package modules that are not declared should fail: underscore/foo
-PASS Package-like scenarios: backtracking via ..: mapped/path
-PASS Package-like scenarios: backtracking via ..: mapped/path/
-FAIL Package-like scenarios: backtracking via ..: mapped/path/.. assert_unreached: Should have rejected: undefined Reached unreachable code
-FAIL Package-like scenarios: backtracking via ..: mapped/path/../backtrack assert_unreached: Should have rejected: undefined Reached unreachable code
-FAIL Package-like scenarios: backtracking via ..: mapped/path/../../backtrack assert_unreached: Should have rejected: undefined Reached unreachable code
-FAIL Package-like scenarios: backtracking via ..: mapped/path/../../../backtrack assert_unreached: Should have rejected: undefined Reached unreachable code
-FAIL Package-like scenarios: backtracking via ..: moment/../backtrack assert_unreached: Should have rejected: undefined Reached unreachable code
-FAIL Package-like scenarios: backtracking via ..: moment/.. assert_unreached: Should have rejected: undefined Reached unreachable code
-PASS Tricky specifiers: explicitly-mapped specifiers that happen to have a slash: package/withslash
-PASS Tricky specifiers: specifier with punctuation: .
-PASS Tricky specifiers: specifier with punctuation: ..
-PASS Tricky specifiers: specifier with punctuation: ..\
-PASS Tricky specifiers: specifier with punctuation: %2E
-PASS Tricky specifiers: specifier with punctuation: %2F
-PASS Tricky specifiers: submodule of something not declared with a trailing slash should fail: not-a-package/foo
-PASS Tricky specifiers: module for which only a trailing-slash version is present should fail: only-slash
-PASS URL-like specifiers: Ordinary URL-like specifiers: https://example.com/lib/foo.mjs
-PASS URL-like specifiers: Ordinary URL-like specifiers: https://///example.com/lib/foo.mjs
-PASS URL-like specifiers: Ordinary URL-like specifiers: /lib/foo.mjs
-PASS URL-like specifiers: Ordinary URL-like specifiers: https://example.com/app/dotrelative/foo.mjs
-PASS URL-like specifiers: Ordinary URL-like specifiers: ../app/dotrelative/foo.mjs
-PASS URL-like specifiers: Ordinary URL-like specifiers: https://example.com/dotdotrelative/foo.mjs
-PASS URL-like specifiers: Ordinary URL-like specifiers: ../dotdotrelative/foo.mjs
-PASS URL-like specifiers: Import map entries just composed from / and .: https://example.com/
-PASS URL-like specifiers: Import map entries just composed from / and .: /
-PASS URL-like specifiers: Import map entries just composed from / and .: ../
-PASS URL-like specifiers: Import map entries just composed from / and .: https://example.com/app/
-PASS URL-like specifiers: Import map entries just composed from / and .: /app/
-PASS URL-like specifiers: Import map entries just composed from / and .: ../app/
-PASS URL-like specifiers: prefix-matched by keys with trailing slashes: /test/foo.mjs
-PASS URL-like specifiers: prefix-matched by keys with trailing slashes: https://example.com/app/test/foo.mjs
-PASS URL-like specifiers: should use the last entry's address when URL-like specifiers parse to the same absolute URL: /test
-PASS URL-like specifiers: backtracking (relative URLs): /test/..
-PASS URL-like specifiers: backtracking (relative URLs): /test/../backtrack
-PASS URL-like specifiers: backtracking (relative URLs): /test/../../backtrack
-PASS URL-like specifiers: backtracking (relative URLs): /test/../../../backtrack
-PASS URL-like specifiers: backtracking (absolute URLs): https://example.com/test/..
-PASS URL-like specifiers: backtracking (absolute URLs): https://example.com/test/../backtrack
-PASS URL-like specifiers: backtracking (absolute URLs): https://example.com/test/../../backtrack
-PASS URL-like specifiers: backtracking (absolute URLs): https://example.com/test/../../../backtrack
-PASS data: base URL (?): should favor the most-specific key: foo/bar
-PASS Exact vs. prefix based matching: Scope without trailing slash only: Non-trailing-slash base URL (exact match): moment
-PASS Exact vs. prefix based matching: Scope without trailing slash only: Non-trailing-slash base URL (exact match): moment/foo
-PASS Exact vs. prefix based matching: Scope without trailing slash only: Trailing-slash base URL (fail): moment
-PASS Exact vs. prefix based matching: Scope without trailing slash only: Trailing-slash base URL (fail): moment/foo
-PASS Exact vs. prefix based matching: Scope without trailing slash only: Subpath base URL (fail): moment
-PASS Exact vs. prefix based matching: Scope without trailing slash only: Subpath base URL (fail): moment/foo
-PASS Exact vs. prefix based matching: Scope without trailing slash only: Non-subpath base URL (fail): moment
-PASS Exact vs. prefix based matching: Scope without trailing slash only: Non-subpath base URL (fail): moment/foo
-PASS Exact vs. prefix based matching: Scope with trailing slash only: Non-trailing-slash base URL (fail): moment
-PASS Exact vs. prefix based matching: Scope with trailing slash only: Non-trailing-slash base URL (fail): moment/foo
-PASS Exact vs. prefix based matching: Scope with trailing slash only: Trailing-slash base URL (exact match): moment
-PASS Exact vs. prefix based matching: Scope with trailing slash only: Trailing-slash base URL (exact match): moment/foo
-PASS Exact vs. prefix based matching: Scope with trailing slash only: Subpath base URL (prefix match): moment
-PASS Exact vs. prefix based matching: Scope with trailing slash only: Subpath base URL (prefix match): moment/foo
-PASS Exact vs. prefix based matching: Scope with trailing slash only: Non-subpath base URL (fail): moment
-PASS Exact vs. prefix based matching: Scope with trailing slash only: Non-subpath base URL (fail): moment/foo
-PASS Exact vs. prefix based matching: Scopes with and without trailing slash: Non-trailing-slash base URL (exact match): moment
-PASS Exact vs. prefix based matching: Scopes with and without trailing slash: Non-trailing-slash base URL (exact match): moment/foo
-PASS Exact vs. prefix based matching: Scopes with and without trailing slash: Trailing-slash base URL (exact match): moment
-PASS Exact vs. prefix based matching: Scopes with and without trailing slash: Trailing-slash base URL (exact match): moment/foo
-PASS Exact vs. prefix based matching: Scopes with and without trailing slash: Subpath base URL (prefix match): moment
-PASS Exact vs. prefix based matching: Scopes with and without trailing slash: Subpath base URL (prefix match): moment/foo
-PASS Exact vs. prefix based matching: Scopes with and without trailing slash: Non-subpath base URL (fail): moment
-PASS Exact vs. prefix based matching: Scopes with and without trailing slash: Non-subpath base URL (fail): moment/foo
-PASS should favor the most-specific key: Overlapping entries with trailing slashes: a
-PASS should favor the most-specific key: Overlapping entries with trailing slashes: a/
-PASS should favor the most-specific key: Overlapping entries with trailing slashes: a/x
-PASS should favor the most-specific key: Overlapping entries with trailing slashes: a/b
-PASS should favor the most-specific key: Overlapping entries with trailing slashes: a/b/
-PASS should favor the most-specific key: Overlapping entries with trailing slashes: a/b/c
-PASS Entries with errors shouldn't allow fallback: No fallback to less-specific prefixes: null/x
-PASS Entries with errors shouldn't allow fallback: No fallback to less-specific prefixes: null/b/x
-PASS Entries with errors shouldn't allow fallback: No fallback to less-specific prefixes: null/b/c/x
-PASS Entries with errors shouldn't allow fallback: No fallback to less-specific prefixes: invalid-url/x
-PASS Entries with errors shouldn't allow fallback: No fallback to less-specific prefixes: invalid-url/b/x
-PASS Entries with errors shouldn't allow fallback: No fallback to less-specific prefixes: invalid-url/b/c/x
-PASS Entries with errors shouldn't allow fallback: No fallback to less-specific prefixes: without-trailing-slashes/x
-PASS Entries with errors shouldn't allow fallback: No fallback to less-specific prefixes: without-trailing-slashes/b/x
-PASS Entries with errors shouldn't allow fallback: No fallback to less-specific prefixes: without-trailing-slashes/b/c/x
-PASS Entries with errors shouldn't allow fallback: No fallback to less-specific prefixes: prefix-resolution-error/x
-PASS Entries with errors shouldn't allow fallback: No fallback to less-specific prefixes: prefix-resolution-error/b/x
-PASS Entries with errors shouldn't allow fallback: No fallback to less-specific prefixes: prefix-resolution-error/b/c/x
-PASS Entries with errors shouldn't allow fallback: No fallback to less-specific scopes: null
-PASS Entries with errors shouldn't allow fallback: No fallback to less-specific scopes: invalid-url
-PASS Entries with errors shouldn't allow fallback: No fallback to less-specific scopes: without-trailing-slashes/x
-PASS Entries with errors shouldn't allow fallback: No fallback to less-specific scopes: prefix-resolution-error/x
-PASS Entries with errors shouldn't allow fallback: No fallback to absolute URL parsing: https://example.com/null
-PASS Entries with errors shouldn't allow fallback: No fallback to absolute URL parsing: https://example.com/invalid-url
-PASS Entries with errors shouldn't allow fallback: No fallback to absolute URL parsing: https://example.com/without-trailing-slashes/x
-PASS Entries with errors shouldn't allow fallback: No fallback to absolute URL parsing: https://example.com/prefix-resolution-error/x
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html
index 9845d5e..fbe6e42 100644
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html
@@ -211,17 +211,19 @@
         Promise
             .all([
               c1.startRendering().then(function(resultBuffer) {
-                return should(resultBuffer.getChannelData(0), 'c1 result')
-                    .beEqualToArray(data);
+                return resultBuffer;
               }),
               c2.startRendering().then(function(resultBuffer) {
-                return should(resultBuffer.getChannelData(0), 'c2 result')
-                    .beEqualToArray(data);
+                return resultBuffer;
               }),
             ])
-            .then(returnValues => {
+            .then(resultBuffers => {
+              let c1ResultValue = should(resultBuffers[0].getChannelData(0), 'c1 result')
+                  .beEqualToArray(data);
+              let c2ResultValue = should(resultBuffers[1].getChannelData(0), 'c2 result')
+                  .beEqualToArray(data);
               should(
-                  returnValues[0] && returnValues[1],
+                  c1ResultValue && c2ResultValue,
                   'AudioBuffer shared between two different contexts')
                   .message('correctly', 'incorrectly');
               task.done();
diff --git a/third_party/blink/web_tests/external/wpt/webmessaging/DIR_METADATA b/third_party/blink/web_tests/external/wpt/webmessaging/DIR_METADATA
new file mode 100644
index 0000000..d83a533
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webmessaging/DIR_METADATA
@@ -0,0 +1,3 @@
+monorail {
+  component: "Blink>Messaging"
+}
diff --git a/third_party/blink/web_tests/external/wpt/webmessaging/OWNERS b/third_party/blink/web_tests/external/wpt/webmessaging/OWNERS
index 9f8c4a0f..1b10b343 100644
--- a/third_party/blink/web_tests/external/wpt/webmessaging/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/webmessaging/OWNERS
@@ -1,2 +1 @@
-# COMPONENT: Blink>Messaging
 mek@chromium.org
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/DIR_METADATA b/third_party/blink/web_tests/external/wpt/webrtc/DIR_METADATA
new file mode 100644
index 0000000..d9367eac
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webrtc/DIR_METADATA
@@ -0,0 +1,6 @@
+monorail {
+  component: "Blink>WebRTC"
+}
+wpt {
+  notify: YES
+}
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/OWNERS b/third_party/blink/web_tests/external/wpt/webrtc/OWNERS
index 082fd71..e6ed2ffc 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/webrtc/OWNERS
@@ -1,5 +1,3 @@
-# COMPONENT: Blink>WebRTC
-# WPT-NOTIFY: true
 hbos@chromium.org
 hta@chromium.org
 guidou@chromium.org
diff --git a/third_party/blink/web_tests/external/wpt/websockets/DIR_METADATA b/third_party/blink/web_tests/external/wpt/websockets/DIR_METADATA
new file mode 100644
index 0000000..60fd6e9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/websockets/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Network>WebSockets"
+}
+team_email: "blink-network-dev@chromium.org"
diff --git a/third_party/blink/web_tests/external/wpt/websockets/OWNERS b/third_party/blink/web_tests/external/wpt/websockets/OWNERS
deleted file mode 100644
index 09bdfc47..0000000
--- a/third_party/blink/web_tests/external/wpt/websockets/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# TEAM: blink-network-dev@chromium.org
-# COMPONENT: Blink>Network>WebSockets
diff --git a/third_party/blink/web_tests/external/wpt/webstorage/DIR_METADATA b/third_party/blink/web_tests/external/wpt/webstorage/DIR_METADATA
new file mode 100644
index 0000000..f2b243d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webstorage/DIR_METADATA
@@ -0,0 +1,7 @@
+monorail {
+  component: "Blink>Storage>DOMStorage"
+}
+team_email: "storage-dev@chromium.org"
+wpt {
+  notify: YES
+}
diff --git a/third_party/blink/web_tests/external/wpt/webstorage/OWNERS b/third_party/blink/web_tests/external/wpt/webstorage/OWNERS
index 5f5f8d3..08758610 100644
--- a/third_party/blink/web_tests/external/wpt/webstorage/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/webstorage/OWNERS
@@ -1,5 +1,2 @@
-# TEAM: storage-dev@chromium.org
-# COMPONENT: Blink>Storage>DOMStorage
-# WPT-NOTIFY: true
 jsbell@chromium.org
 mek@chromium.org
diff --git a/third_party/blink/web_tests/external/wpt/webusb/DIR_METADATA b/third_party/blink/web_tests/external/wpt/webusb/DIR_METADATA
new file mode 100644
index 0000000..d8d7bf9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webusb/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>USB"
+}
+team_email: "webusb@chromium.org"
diff --git a/third_party/blink/web_tests/external/wpt/webusb/OWNERS b/third_party/blink/web_tests/external/wpt/webusb/OWNERS
index 8dc3166..acf1a9e 100644
--- a/third_party/blink/web_tests/external/wpt/webusb/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/webusb/OWNERS
@@ -1,4 +1 @@
 file://chrome/browser/usb/OWNERS
-
-# COMPONENT: Blink>USB
-# TEAM: webusb@chromium.org
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/DIR_METADATA b/third_party/blink/web_tests/external/wpt/webvtt/DIR_METADATA
new file mode 100644
index 0000000..cbab05cb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webvtt/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Media>Track"
+}
+team_email: "media-dev@chromium.org"
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/OWNERS b/third_party/blink/web_tests/external/wpt/webvtt/OWNERS
index 7be00e4..7e9f03e 100644
--- a/third_party/blink/web_tests/external/wpt/webvtt/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/webvtt/OWNERS
@@ -1,3 +1 @@
-# TEAM: media-dev@chromium.org
-# COMPONENT: Blink>Media>Track
 msisov@igalia.com
diff --git a/third_party/blink/web_tests/external/wpt/webxr/DIR_METADATA b/third_party/blink/web_tests/external/wpt/webxr/DIR_METADATA
new file mode 100644
index 0000000..4d83ec23
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webxr/DIR_METADATA
@@ -0,0 +1,7 @@
+monorail {
+  component: "Blink>WebXR"
+}
+team_email: "xr-dev@chromium.org"
+wpt {
+  notify: YES
+}
diff --git a/third_party/blink/web_tests/external/wpt/webxr/OWNERS b/third_party/blink/web_tests/external/wpt/webxr/OWNERS
index cb7f472..95884464e 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/webxr/OWNERS
@@ -1,5 +1 @@
 file://third_party/blink/renderer/modules/xr/OWNERS
-
-# TEAM: xr-dev@chromium.org
-# COMPONENT: Blink>WebXR
-# WPT-NOTIFY: true
diff --git a/third_party/blink/web_tests/external/wpt/workers/DIR_METADATA b/third_party/blink/web_tests/external/wpt/workers/DIR_METADATA
new file mode 100644
index 0000000..1201c65
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/workers/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Workers"
+}
+team_email: "worker-dev@chromium.org"
diff --git a/third_party/blink/web_tests/external/wpt/workers/OWNERS b/third_party/blink/web_tests/external/wpt/workers/OWNERS
deleted file mode 100644
index 51dd60fe..0000000
--- a/third_party/blink/web_tests/external/wpt/workers/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# TEAM: worker-dev@chromium.org
-# COMPONENT: Blink>Workers
diff --git a/third_party/blink/web_tests/external/wpt/worklets/DIR_METADATA b/third_party/blink/web_tests/external/wpt/worklets/DIR_METADATA
new file mode 100644
index 0000000..1201c65
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/worklets/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Workers"
+}
+team_email: "worker-dev@chromium.org"
diff --git a/third_party/blink/web_tests/external/wpt/worklets/OWNERS b/third_party/blink/web_tests/external/wpt/worklets/OWNERS
deleted file mode 100644
index 51dd60fe..0000000
--- a/third_party/blink/web_tests/external/wpt/worklets/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# TEAM: worker-dev@chromium.org
-# COMPONENT: Blink>Workers
diff --git a/third_party/blink/web_tests/external/wpt/x-frame-options/DIR_METADATA b/third_party/blink/web_tests/external/wpt/x-frame-options/DIR_METADATA
new file mode 100644
index 0000000..b0803f1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/x-frame-options/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>SecurityFeature>XFrameOptions"
+}
+team_email: "security-dev@chromium.org"
diff --git a/third_party/blink/web_tests/external/wpt/x-frame-options/OWNERS b/third_party/blink/web_tests/external/wpt/x-frame-options/OWNERS
index 243f594fd..3f84563 100644
--- a/third_party/blink/web_tests/external/wpt/x-frame-options/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/x-frame-options/OWNERS
@@ -1,3 +1 @@
-# TEAM: security-dev@chromium.org
-# COMPONENT: Blink>SecurityFeature>XFrameOptions
 mkwst@chromium.org
diff --git a/third_party/blink/web_tests/external/wpt/xhr/DIR_METADATA b/third_party/blink/web_tests/external/wpt/xhr/DIR_METADATA
new file mode 100644
index 0000000..89a667c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/xhr/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Network>XHR"
+}
+team_email: "blink-network-dev@chromium.org"
diff --git a/third_party/blink/web_tests/external/wpt/xhr/OWNERS b/third_party/blink/web_tests/external/wpt/xhr/OWNERS
deleted file mode 100644
index d9e80484..0000000
--- a/third_party/blink/web_tests/external/wpt/xhr/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# TEAM: blink-network-dev@chromium.org
-# COMPONENT: Blink>Network>XHR
diff --git a/third_party/blink/web_tests/fast/events/DIR_METADATA b/third_party/blink/web_tests/fast/events/DIR_METADATA
new file mode 100644
index 0000000..1510459
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "dom-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/events/OWNERS b/third_party/blink/web_tests/fast/events/OWNERS
index 422c227..e69de29 100644
--- a/third_party/blink/web_tests/fast/events/OWNERS
+++ b/third_party/blink/web_tests/fast/events/OWNERS
@@ -1 +0,0 @@
-# TEAM: dom-dev@chromium.org
diff --git a/third_party/blink/web_tests/fast/events/constructors/DIR_METADATA b/third_party/blink/web_tests/fast/events/constructors/DIR_METADATA
new file mode 100644
index 0000000..adc12d2
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/constructors/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "blink-reviews-bindings@chromium.org"
diff --git a/third_party/blink/web_tests/fast/events/constructors/OWNERS b/third_party/blink/web_tests/fast/events/constructors/OWNERS
index 2fccfa7..e69de29 100644
--- a/third_party/blink/web_tests/fast/events/constructors/OWNERS
+++ b/third_party/blink/web_tests/fast/events/constructors/OWNERS
@@ -1 +0,0 @@
-# TEAM: blink-reviews-bindings@chromium.org
diff --git a/third_party/blink/web_tests/fast/events/hr-timestamp/DIR_METADATA b/third_party/blink/web_tests/fast/events/hr-timestamp/DIR_METADATA
new file mode 100644
index 0000000..06c05e63
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/hr-timestamp/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Input"
+}
+team_email: "input-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/events/hr-timestamp/OWNERS b/third_party/blink/web_tests/fast/events/hr-timestamp/OWNERS
index c0dd4aa..e69de29 100644
--- a/third_party/blink/web_tests/fast/events/hr-timestamp/OWNERS
+++ b/third_party/blink/web_tests/fast/events/hr-timestamp/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: input-dev@chromium.org
-# COMPONENT: Blink>Input
diff --git a/third_party/blink/web_tests/fast/events/inputevents/DIR_METADATA b/third_party/blink/web_tests/fast/events/inputevents/DIR_METADATA
new file mode 100644
index 0000000..06c05e63
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/inputevents/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Input"
+}
+team_email: "input-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/events/inputevents/OWNERS b/third_party/blink/web_tests/fast/events/inputevents/OWNERS
index c0dd4aa..e69de29 100644
--- a/third_party/blink/web_tests/fast/events/inputevents/OWNERS
+++ b/third_party/blink/web_tests/fast/events/inputevents/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: input-dev@chromium.org
-# COMPONENT: Blink>Input
diff --git a/third_party/blink/web_tests/fast/events/pointerevents/DIR_METADATA b/third_party/blink/web_tests/fast/events/pointerevents/DIR_METADATA
new file mode 100644
index 0000000..06c05e63
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/pointerevents/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Input"
+}
+team_email: "input-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/events/pointerevents/OWNERS b/third_party/blink/web_tests/fast/events/pointerevents/OWNERS
index c0dd4aa..e69de29 100644
--- a/third_party/blink/web_tests/fast/events/pointerevents/OWNERS
+++ b/third_party/blink/web_tests/fast/events/pointerevents/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: input-dev@chromium.org
-# COMPONENT: Blink>Input
diff --git a/third_party/blink/web_tests/fast/events/touch/DIR_METADATA b/third_party/blink/web_tests/fast/events/touch/DIR_METADATA
new file mode 100644
index 0000000..06c05e63
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/touch/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Input"
+}
+team_email: "input-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/events/touch/OWNERS b/third_party/blink/web_tests/fast/events/touch/OWNERS
index c0dd4aa..e69de29 100644
--- a/third_party/blink/web_tests/fast/events/touch/OWNERS
+++ b/third_party/blink/web_tests/fast/events/touch/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: input-dev@chromium.org
-# COMPONENT: Blink>Input
diff --git a/third_party/blink/web_tests/fast/events/wheel/DIR_METADATA b/third_party/blink/web_tests/fast/events/wheel/DIR_METADATA
new file mode 100644
index 0000000..06c05e63
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/wheel/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Input"
+}
+team_email: "input-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/events/wheel/OWNERS b/third_party/blink/web_tests/fast/events/wheel/OWNERS
index c0dd4aa..e69de29 100644
--- a/third_party/blink/web_tests/fast/events/wheel/OWNERS
+++ b/third_party/blink/web_tests/fast/events/wheel/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: input-dev@chromium.org
-# COMPONENT: Blink>Input
diff --git a/third_party/blink/web_tests/fast/eventsource/DIR_METADATA b/third_party/blink/web_tests/fast/eventsource/DIR_METADATA
new file mode 100644
index 0000000..72f55a07b
--- /dev/null
+++ b/third_party/blink/web_tests/fast/eventsource/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Network"
+}
+team_email: "blink-network-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/eventsource/OWNERS b/third_party/blink/web_tests/fast/eventsource/OWNERS
index 8263c35..e69de29 100644
--- a/third_party/blink/web_tests/fast/eventsource/OWNERS
+++ b/third_party/blink/web_tests/fast/eventsource/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: blink-network-dev@chromium.org
-# COMPONENT: Blink>Network
diff --git a/third_party/blink/web_tests/fast/forms/DIR_METADATA b/third_party/blink/web_tests/fast/forms/DIR_METADATA
new file mode 100644
index 0000000..1510459
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "dom-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/forms/OWNERS b/third_party/blink/web_tests/fast/forms/OWNERS
index 422c227..e69de29 100644
--- a/third_party/blink/web_tests/fast/forms/OWNERS
+++ b/third_party/blink/web_tests/fast/forms/OWNERS
@@ -1 +0,0 @@
-# TEAM: dom-dev@chromium.org
diff --git a/third_party/blink/web_tests/fast/frames/DIR_METADATA b/third_party/blink/web_tests/fast/frames/DIR_METADATA
new file mode 100644
index 0000000..1510459
--- /dev/null
+++ b/third_party/blink/web_tests/fast/frames/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "dom-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/frames/OWNERS b/third_party/blink/web_tests/fast/frames/OWNERS
index 422c227..e69de29 100644
--- a/third_party/blink/web_tests/fast/frames/OWNERS
+++ b/third_party/blink/web_tests/fast/frames/OWNERS
@@ -1 +0,0 @@
-# TEAM: dom-dev@chromium.org
diff --git a/third_party/blink/web_tests/fast/gradients/DIR_METADATA b/third_party/blink/web_tests/fast/gradients/DIR_METADATA
new file mode 100644
index 0000000..1c9c5df
--- /dev/null
+++ b/third_party/blink/web_tests/fast/gradients/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Paint"
+}
+team_email: "paint-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/gradients/OWNERS b/third_party/blink/web_tests/fast/gradients/OWNERS
index 6904c86..e69de29 100644
--- a/third_party/blink/web_tests/fast/gradients/OWNERS
+++ b/third_party/blink/web_tests/fast/gradients/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: paint-dev@chromium.org
-# COMPONENT: Blink>Paint
diff --git a/third_party/blink/web_tests/fast/hidpi/DIR_METADATA b/third_party/blink/web_tests/fast/hidpi/DIR_METADATA
new file mode 100644
index 0000000..56b1ae24
--- /dev/null
+++ b/third_party/blink/web_tests/fast/hidpi/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Layout>Subpixel"
+}
+team_email: "layout-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/hidpi/OWNERS b/third_party/blink/web_tests/fast/hidpi/OWNERS
index 83275e8..e69de29 100644
--- a/third_party/blink/web_tests/fast/hidpi/OWNERS
+++ b/third_party/blink/web_tests/fast/hidpi/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: layout-dev@chromium.org
-# COMPONENT: Blink>Layout>Subpixel
diff --git a/third_party/blink/web_tests/fast/history/scroll-restoration/DIR_METADATA b/third_party/blink/web_tests/fast/history/scroll-restoration/DIR_METADATA
new file mode 100644
index 0000000..8d7b73e
--- /dev/null
+++ b/third_party/blink/web_tests/fast/history/scroll-restoration/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Scroll"
+}
+team_email: "input-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/history/scroll-restoration/OWNERS b/third_party/blink/web_tests/fast/history/scroll-restoration/OWNERS
index b6c7099b..e69de29 100644
--- a/third_party/blink/web_tests/fast/history/scroll-restoration/OWNERS
+++ b/third_party/blink/web_tests/fast/history/scroll-restoration/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: input-dev@chromium.org
-# COMPONENT: Blink>Scroll
diff --git a/third_party/blink/web_tests/fast/html/DIR_METADATA b/third_party/blink/web_tests/fast/html/DIR_METADATA
new file mode 100644
index 0000000..1510459
--- /dev/null
+++ b/third_party/blink/web_tests/fast/html/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "dom-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/html/OWNERS b/third_party/blink/web_tests/fast/html/OWNERS
index 422c227..e69de29 100644
--- a/third_party/blink/web_tests/fast/html/OWNERS
+++ b/third_party/blink/web_tests/fast/html/OWNERS
@@ -1 +0,0 @@
-# TEAM: dom-dev@chromium.org
diff --git a/third_party/blink/web_tests/fast/inline-block/DIR_METADATA b/third_party/blink/web_tests/fast/inline-block/DIR_METADATA
new file mode 100644
index 0000000..d21906d
--- /dev/null
+++ b/third_party/blink/web_tests/fast/inline-block/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Layout"
+}
+team_email: "layout-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/inline-block/OWNERS b/third_party/blink/web_tests/fast/inline-block/OWNERS
index 7c1ec12..e69de29 100644
--- a/third_party/blink/web_tests/fast/inline-block/OWNERS
+++ b/third_party/blink/web_tests/fast/inline-block/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: layout-dev@chromium.org
-# COMPONENT: Blink>Layout
diff --git a/third_party/blink/web_tests/fast/inline/DIR_METADATA b/third_party/blink/web_tests/fast/inline/DIR_METADATA
new file mode 100644
index 0000000..d21906d
--- /dev/null
+++ b/third_party/blink/web_tests/fast/inline/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Layout"
+}
+team_email: "layout-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/inline/OWNERS b/third_party/blink/web_tests/fast/inline/OWNERS
index 7c1ec12..e69de29 100644
--- a/third_party/blink/web_tests/fast/inline/OWNERS
+++ b/third_party/blink/web_tests/fast/inline/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: layout-dev@chromium.org
-# COMPONENT: Blink>Layout
diff --git a/third_party/blink/web_tests/fast/innerHTML/DIR_METADATA b/third_party/blink/web_tests/fast/innerHTML/DIR_METADATA
new file mode 100644
index 0000000..1510459
--- /dev/null
+++ b/third_party/blink/web_tests/fast/innerHTML/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "dom-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/innerHTML/OWNERS b/third_party/blink/web_tests/fast/innerHTML/OWNERS
index 422c227..e69de29 100644
--- a/third_party/blink/web_tests/fast/innerHTML/OWNERS
+++ b/third_party/blink/web_tests/fast/innerHTML/OWNERS
@@ -1 +0,0 @@
-# TEAM: dom-dev@chromium.org
diff --git a/third_party/blink/web_tests/fast/input/DIR_METADATA b/third_party/blink/web_tests/fast/input/DIR_METADATA
new file mode 100644
index 0000000..06c05e63
--- /dev/null
+++ b/third_party/blink/web_tests/fast/input/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Input"
+}
+team_email: "input-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/input/OWNERS b/third_party/blink/web_tests/fast/input/OWNERS
index 60eb7be..e69de29 100644
--- a/third_party/blink/web_tests/fast/input/OWNERS
+++ b/third_party/blink/web_tests/fast/input/OWNERS
@@ -1,3 +0,0 @@
-# TEAM: input-dev@chromium.org
-# COMPONENT: Blink>Input
-
diff --git a/third_party/blink/web_tests/fast/invalid/DIR_METADATA b/third_party/blink/web_tests/fast/invalid/DIR_METADATA
new file mode 100644
index 0000000..1510459
--- /dev/null
+++ b/third_party/blink/web_tests/fast/invalid/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "dom-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/invalid/OWNERS b/third_party/blink/web_tests/fast/invalid/OWNERS
index 422c227..e69de29 100644
--- a/third_party/blink/web_tests/fast/invalid/OWNERS
+++ b/third_party/blink/web_tests/fast/invalid/OWNERS
@@ -1 +0,0 @@
-# TEAM: dom-dev@chromium.org
diff --git a/third_party/blink/web_tests/fast/layout/DIR_METADATA b/third_party/blink/web_tests/fast/layout/DIR_METADATA
new file mode 100644
index 0000000..d21906d
--- /dev/null
+++ b/third_party/blink/web_tests/fast/layout/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Layout"
+}
+team_email: "layout-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/layout/OWNERS b/third_party/blink/web_tests/fast/layout/OWNERS
index 7c1ec12..e69de29 100644
--- a/third_party/blink/web_tests/fast/layout/OWNERS
+++ b/third_party/blink/web_tests/fast/layout/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: layout-dev@chromium.org
-# COMPONENT: Blink>Layout
diff --git a/third_party/blink/web_tests/fast/lists/DIR_METADATA b/third_party/blink/web_tests/fast/lists/DIR_METADATA
new file mode 100644
index 0000000..d21906d
--- /dev/null
+++ b/third_party/blink/web_tests/fast/lists/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Layout"
+}
+team_email: "layout-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/lists/OWNERS b/third_party/blink/web_tests/fast/lists/OWNERS
index 7c1ec12..e69de29 100644
--- a/third_party/blink/web_tests/fast/lists/OWNERS
+++ b/third_party/blink/web_tests/fast/lists/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: layout-dev@chromium.org
-# COMPONENT: Blink>Layout
diff --git a/third_party/blink/web_tests/fast/media/DIR_METADATA b/third_party/blink/web_tests/fast/media/DIR_METADATA
new file mode 100644
index 0000000..9ebc0ec
--- /dev/null
+++ b/third_party/blink/web_tests/fast/media/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "media-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/media/OWNERS b/third_party/blink/web_tests/fast/media/OWNERS
index d58efb68..e69de29 100644
--- a/third_party/blink/web_tests/fast/media/OWNERS
+++ b/third_party/blink/web_tests/fast/media/OWNERS
@@ -1 +0,0 @@
-# TEAM: media-dev@chromium.org
diff --git a/third_party/blink/web_tests/fast/multicol/DIR_METADATA b/third_party/blink/web_tests/fast/multicol/DIR_METADATA
new file mode 100644
index 0000000..97f655b
--- /dev/null
+++ b/third_party/blink/web_tests/fast/multicol/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Layout>MultiCol"
+}
+team_email: "layout-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/multicol/OWNERS b/third_party/blink/web_tests/fast/multicol/OWNERS
index 2edc70a..e69de29 100644
--- a/third_party/blink/web_tests/fast/multicol/OWNERS
+++ b/third_party/blink/web_tests/fast/multicol/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: layout-dev@chromium.org
-# COMPONENT: Blink>Layout>MultiCol
diff --git a/third_party/blink/web_tests/fast/overflow/DIR_METADATA b/third_party/blink/web_tests/fast/overflow/DIR_METADATA
new file mode 100644
index 0000000..d21906d
--- /dev/null
+++ b/third_party/blink/web_tests/fast/overflow/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Layout"
+}
+team_email: "layout-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/overflow/OWNERS b/third_party/blink/web_tests/fast/overflow/OWNERS
index 7c1ec12..e69de29 100644
--- a/third_party/blink/web_tests/fast/overflow/OWNERS
+++ b/third_party/blink/web_tests/fast/overflow/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: layout-dev@chromium.org
-# COMPONENT: Blink>Layout
diff --git a/third_party/blink/web_tests/fast/parser/DIR_METADATA b/third_party/blink/web_tests/fast/parser/DIR_METADATA
new file mode 100644
index 0000000..1510459
--- /dev/null
+++ b/third_party/blink/web_tests/fast/parser/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "dom-dev@chromium.org"
diff --git a/third_party/blink/web_tests/fast/parser/OWNERS b/third_party/blink/web_tests/fast/parser/OWNERS
index 422c227..e69de29 100644
--- a/third_party/blink/web_tests/fast/parser/OWNERS
+++ b/third_party/blink/web_tests/fast/parser/OWNERS
@@ -1 +0,0 @@
-# TEAM: dom-dev@chromium.org
diff --git a/third_party/blink/web_tests/html/document_metadata/referrer-comma-separated-use-counter.html b/third_party/blink/web_tests/html/document_metadata/referrer-inside-head-not-use-counted.html
similarity index 65%
rename from third_party/blink/web_tests/html/document_metadata/referrer-comma-separated-use-counter.html
rename to third_party/blink/web_tests/html/document_metadata/referrer-inside-head-not-use-counted.html
index cdcb241..81595d2 100644
--- a/third_party/blink/web_tests/html/document_metadata/referrer-comma-separated-use-counter.html
+++ b/third_party/blink/web_tests/html/document_metadata/referrer-inside-head-not-use-counted.html
@@ -10,8 +10,6 @@
 
   test(() => {
     const kHTMLMetaElementReferrerPolicyOutsideHead = 3314; // from web_feature.mojom
-    const kHTMLMetaElementReferrerPolicyMultipleTokens = 3315;
     assert_false(internals.isUseCounted(document, kHTMLMetaElementReferrerPolicyOutsideHead));
-    assert_true(internals.isUseCounted(document, kHTMLMetaElementReferrerPolicyMultipleTokens));
-  }, '<meta name=referrer> with multiple tokens is use counted');
+  }, '<meta name=referrer> inside <head> is not use counted');
 </script>
diff --git a/third_party/blink/web_tests/html/document_metadata/referrer-outside-head-use-counter.html b/third_party/blink/web_tests/html/document_metadata/referrer-outside-head-use-counter.html
index f86acf2..d0df9a0 100644
--- a/third_party/blink/web_tests/html/document_metadata/referrer-outside-head-use-counter.html
+++ b/third_party/blink/web_tests/html/document_metadata/referrer-outside-head-use-counter.html
@@ -10,8 +10,6 @@
 
     test(() => {
       const kHTMLMetaElementReferrerPolicyOutsideHead = 3314; // from web_feature.mojom
-      const kHTMLMetaElementReferrerPolicyMultipleTokens = 3315;
       assert_true(internals.isUseCounted(document, kHTMLMetaElementReferrerPolicyOutsideHead));
-      assert_false(internals.isUseCounted(document, kHTMLMetaElementReferrerPolicyMultipleTokens));
     }, '<meta name=referrer> outside <head> is use counted');
   </script>
diff --git a/third_party/blink/web_tests/http/tests/background_sync/DIR_METADATA b/third_party/blink/web_tests/http/tests/background_sync/DIR_METADATA
new file mode 100644
index 0000000..b7a1846
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/background_sync/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "platform-capabilities@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/background_sync/OWNERS b/third_party/blink/web_tests/http/tests/background_sync/OWNERS
index 1c13657..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/background_sync/OWNERS
+++ b/third_party/blink/web_tests/http/tests/background_sync/OWNERS
@@ -1 +0,0 @@
-# TEAM: platform-capabilities@chromium.org
diff --git a/third_party/blink/web_tests/http/tests/cachestorage/DIR_METADATA b/third_party/blink/web_tests/http/tests/cachestorage/DIR_METADATA
new file mode 100644
index 0000000..f478d0c
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/cachestorage/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Storage>CacheStorage"
+}
+team_email: "storage-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/cachestorage/OWNERS b/third_party/blink/web_tests/http/tests/cachestorage/OWNERS
index a42a750..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/cachestorage/OWNERS
+++ b/third_party/blink/web_tests/http/tests/cachestorage/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: storage-dev@chromium.org
-# COMPONENT: Blink>Storage>CacheStorage
diff --git a/third_party/blink/web_tests/http/tests/custom-elements/DIR_METADATA b/third_party/blink/web_tests/http/tests/custom-elements/DIR_METADATA
new file mode 100644
index 0000000..1510459
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/custom-elements/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "dom-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/custom-elements/OWNERS b/third_party/blink/web_tests/http/tests/custom-elements/OWNERS
index 422c227..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/custom-elements/OWNERS
+++ b/third_party/blink/web_tests/http/tests/custom-elements/OWNERS
@@ -1 +0,0 @@
-# TEAM: dom-dev@chromium.org
diff --git a/third_party/blink/web_tests/http/tests/devtools/DIR_METADATA b/third_party/blink/web_tests/http/tests/devtools/DIR_METADATA
new file mode 100644
index 0000000..262c6dd
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "devtools-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/devtools/OWNERS b/third_party/blink/web_tests/http/tests/devtools/OWNERS
index e08d2ece..827ae416 100644
--- a/third_party/blink/web_tests/http/tests/devtools/OWNERS
+++ b/third_party/blink/web_tests/http/tests/devtools/OWNERS
@@ -1,5 +1,4 @@
-# TEAM: devtools-dev@chromium.org
 
 # Audits owners.
 per-file audits*=cjamcl@google.com
-per-file audits*=paulirish@chromium.org
+per-file audits*=paulirish@chromium.org
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/dom/DIR_METADATA b/third_party/blink/web_tests/http/tests/dom/DIR_METADATA
new file mode 100644
index 0000000..1510459
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/dom/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "dom-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/dom/OWNERS b/third_party/blink/web_tests/http/tests/dom/OWNERS
index 422c227..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/dom/OWNERS
+++ b/third_party/blink/web_tests/http/tests/dom/OWNERS
@@ -1 +0,0 @@
-# TEAM: dom-dev@chromium.org
diff --git a/third_party/blink/web_tests/http/tests/eventsource/DIR_METADATA b/third_party/blink/web_tests/http/tests/eventsource/DIR_METADATA
new file mode 100644
index 0000000..72f55a07b
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/eventsource/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Network"
+}
+team_email: "blink-network-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/eventsource/OWNERS b/third_party/blink/web_tests/http/tests/eventsource/OWNERS
index 8263c35..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/eventsource/OWNERS
+++ b/third_party/blink/web_tests/http/tests/eventsource/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: blink-network-dev@chromium.org
-# COMPONENT: Blink>Network
diff --git a/third_party/blink/web_tests/http/tests/fetch/chromium/DIR_METADATA b/third_party/blink/web_tests/http/tests/fetch/chromium/DIR_METADATA
new file mode 100644
index 0000000..331a6d1
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/fetch/chromium/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Network>FetchAPI"
+}
+team_email: "blink-network-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/fetch/chromium/OWNERS b/third_party/blink/web_tests/http/tests/fetch/chromium/OWNERS
index e1e0a77..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/fetch/chromium/OWNERS
+++ b/third_party/blink/web_tests/http/tests/fetch/chromium/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: blink-network-dev@chromium.org
-# COMPONENT: Blink>Network>FetchAPI
diff --git a/third_party/blink/web_tests/http/tests/fetch/referrer/DIR_METADATA b/third_party/blink/web_tests/http/tests/fetch/referrer/DIR_METADATA
new file mode 100644
index 0000000..331a6d1
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/fetch/referrer/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Network>FetchAPI"
+}
+team_email: "blink-network-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/fetch/referrer/OWNERS b/third_party/blink/web_tests/http/tests/fetch/referrer/OWNERS
index e1e0a77..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/fetch/referrer/OWNERS
+++ b/third_party/blink/web_tests/http/tests/fetch/referrer/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: blink-network-dev@chromium.org
-# COMPONENT: Blink>Network>FetchAPI
diff --git a/third_party/blink/web_tests/http/tests/fetch/script-tests/thorough/DIR_METADATA b/third_party/blink/web_tests/http/tests/fetch/script-tests/thorough/DIR_METADATA
new file mode 100644
index 0000000..331a6d1
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/fetch/script-tests/thorough/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Network>FetchAPI"
+}
+team_email: "blink-network-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/fetch/script-tests/thorough/OWNERS b/third_party/blink/web_tests/http/tests/fetch/script-tests/thorough/OWNERS
index e1e0a77..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/fetch/script-tests/thorough/OWNERS
+++ b/third_party/blink/web_tests/http/tests/fetch/script-tests/thorough/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: blink-network-dev@chromium.org
-# COMPONENT: Blink>Network>FetchAPI
diff --git a/third_party/blink/web_tests/http/tests/fetch/serviceworker-proxied/thorough/DIR_METADATA b/third_party/blink/web_tests/http/tests/fetch/serviceworker-proxied/thorough/DIR_METADATA
new file mode 100644
index 0000000..331a6d1
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/fetch/serviceworker-proxied/thorough/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Network>FetchAPI"
+}
+team_email: "blink-network-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/fetch/serviceworker-proxied/thorough/OWNERS b/third_party/blink/web_tests/http/tests/fetch/serviceworker-proxied/thorough/OWNERS
index e1e0a77..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/fetch/serviceworker-proxied/thorough/OWNERS
+++ b/third_party/blink/web_tests/http/tests/fetch/serviceworker-proxied/thorough/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: blink-network-dev@chromium.org
-# COMPONENT: Blink>Network>FetchAPI
diff --git a/third_party/blink/web_tests/http/tests/fetch/serviceworker/DIR_METADATA b/third_party/blink/web_tests/http/tests/fetch/serviceworker/DIR_METADATA
new file mode 100644
index 0000000..331a6d1
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/fetch/serviceworker/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Network>FetchAPI"
+}
+team_email: "blink-network-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/fetch/serviceworker/OWNERS b/third_party/blink/web_tests/http/tests/fetch/serviceworker/OWNERS
index e1e0a77..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/fetch/serviceworker/OWNERS
+++ b/third_party/blink/web_tests/http/tests/fetch/serviceworker/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: blink-network-dev@chromium.org
-# COMPONENT: Blink>Network>FetchAPI
diff --git a/third_party/blink/web_tests/http/tests/fetch/window/DIR_METADATA b/third_party/blink/web_tests/http/tests/fetch/window/DIR_METADATA
new file mode 100644
index 0000000..331a6d1
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/fetch/window/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Network>FetchAPI"
+}
+team_email: "blink-network-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/fetch/window/OWNERS b/third_party/blink/web_tests/http/tests/fetch/window/OWNERS
index e1e0a77..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/fetch/window/OWNERS
+++ b/third_party/blink/web_tests/http/tests/fetch/window/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: blink-network-dev@chromium.org
-# COMPONENT: Blink>Network>FetchAPI
diff --git a/third_party/blink/web_tests/http/tests/fetch/workers/DIR_METADATA b/third_party/blink/web_tests/http/tests/fetch/workers/DIR_METADATA
new file mode 100644
index 0000000..331a6d1
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/fetch/workers/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Network>FetchAPI"
+}
+team_email: "blink-network-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/fetch/workers/OWNERS b/third_party/blink/web_tests/http/tests/fetch/workers/OWNERS
index e1e0a77..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/fetch/workers/OWNERS
+++ b/third_party/blink/web_tests/http/tests/fetch/workers/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: blink-network-dev@chromium.org
-# COMPONENT: Blink>Network>FetchAPI
diff --git a/third_party/blink/web_tests/http/tests/fullscreen/DIR_METADATA b/third_party/blink/web_tests/http/tests/fullscreen/DIR_METADATA
new file mode 100644
index 0000000..e8006122
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/fullscreen/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Layout>Fullscreen"
+}
+team_email: "layout-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/fullscreen/OWNERS b/third_party/blink/web_tests/http/tests/fullscreen/OWNERS
index 40194ba..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/fullscreen/OWNERS
+++ b/third_party/blink/web_tests/http/tests/fullscreen/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: layout-dev@chromium.org
-# COMPONENT: Blink>Layout>Fullscreen
diff --git a/third_party/blink/web_tests/http/tests/html/DIR_METADATA b/third_party/blink/web_tests/http/tests/html/DIR_METADATA
new file mode 100644
index 0000000..bdebb86
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/html/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>HTML"
+}
+team_email: "dom-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/html/OWNERS b/third_party/blink/web_tests/http/tests/html/OWNERS
index 02eb183..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/html/OWNERS
+++ b/third_party/blink/web_tests/http/tests/html/OWNERS
@@ -1,2 +0,0 @@
-# COMPONENT: Blink>HTML
-# TEAM: dom-dev@chromium.org
diff --git a/third_party/blink/web_tests/http/tests/html/document_metadata/referrer-comma-separated-affecting-request-use-counter.html b/third_party/blink/web_tests/http/tests/html/document_metadata/referrer-comma-separated-affecting-request-use-counter.html
deleted file mode 100644
index b5e48fc..0000000
--- a/third_party/blink/web_tests/http/tests/html/document_metadata/referrer-comma-separated-affecting-request-use-counter.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!DOCTYPE html>
-
-<!-- It's important these blocking script fetches precede the meta tag, in
-  order not to affect the use counters being measured. -->
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="/gen/third_party/blink/public/mojom/web_feature/web_feature.mojom.js"></script>
-
-<head>
-  <meta name=referrer content="origin, origin" />
-</head>
-
-<script>
-  'use strict';
-
-  promise_test(async t => {
-    try {
-      await fetch('/resources/dummy.html', {
-        referrerPolicy: 'origin'
-      });
-      assert_false(internals.isUseCounted(document, blink.mojom.WebFeature.kHTMLMetaElementReferrerPolicyMultipleTokensAffectingRequest), "Counter shouldn't be populated");
-    } finally {
-      internals.clearUseCounter(document, blink.mojom.WebFeature.kHTMLMetaElementReferrerPolicyMultipleTokensAffectingRequest);
-    }
-  }, "<meta name=referrer> with multiple tokens doesn't affect the referrers of requests with explicitly set policies.");
-
-  promise_test(async t => {
-    try {
-      await fetch('/resources/dummy.html');
-      assert_true(internals.isUseCounted(document, blink.mojom.WebFeature.kHTMLMetaElementReferrerPolicyMultipleTokensAffectingRequest), "Counter should be populated");
-    } finally {
-      internals.clearUseCounter(document, blink.mojom.WebFeature.kHTMLMetaElementReferrerPolicyMultipleTokensAffectingRequest);
-    }
-  }, "<meta name=referrer> with multiple tokens does affect the referrers of this request using its document's policy.");
-</script>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/DIR_METADATA b/third_party/blink/web_tests/http/tests/inspector-protocol/DIR_METADATA
new file mode 100644
index 0000000..262c6dd
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "devtools-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/OWNERS b/third_party/blink/web_tests/http/tests/inspector-protocol/OWNERS
index 0e56faf..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/OWNERS
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/OWNERS
@@ -1 +0,0 @@
-# TEAM: devtools-dev@chromium.org
diff --git a/third_party/blink/web_tests/http/tests/locks/DIR_METADATA b/third_party/blink/web_tests/http/tests/locks/DIR_METADATA
new file mode 100644
index 0000000..cc6fcfa
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/locks/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Storage"
+}
+team_email: "storage-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/locks/OWNERS b/third_party/blink/web_tests/http/tests/locks/OWNERS
index 5ca6cf5f..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/locks/OWNERS
+++ b/third_party/blink/web_tests/http/tests/locks/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: storage-dev@chromium.org
-# COMPONENT: Blink>Storage
diff --git a/third_party/blink/web_tests/http/tests/media/DIR_METADATA b/third_party/blink/web_tests/http/tests/media/DIR_METADATA
new file mode 100644
index 0000000..9ebc0ec
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/media/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "media-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/media/OWNERS b/third_party/blink/web_tests/http/tests/media/OWNERS
index d58efb68..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/media/OWNERS
+++ b/third_party/blink/web_tests/http/tests/media/OWNERS
@@ -1 +0,0 @@
-# TEAM: media-dev@chromium.org
diff --git a/third_party/blink/web_tests/http/tests/notifications/DIR_METADATA b/third_party/blink/web_tests/http/tests/notifications/DIR_METADATA
new file mode 100644
index 0000000..b7a1846
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/notifications/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "platform-capabilities@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/notifications/OWNERS b/third_party/blink/web_tests/http/tests/notifications/OWNERS
index 1c13657..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/notifications/OWNERS
+++ b/third_party/blink/web_tests/http/tests/notifications/OWNERS
@@ -1 +0,0 @@
-# TEAM: platform-capabilities@chromium.org
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/DIR_METADATA b/third_party/blink/web_tests/http/tests/origin_trials/DIR_METADATA
new file mode 100644
index 0000000..0032fd00
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/origin_trials/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Internals>OriginTrials"
+}
+team_email: "experimentation-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/OWNERS b/third_party/blink/web_tests/http/tests/origin_trials/OWNERS
index 534b8121..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/origin_trials/OWNERS
+++ b/third_party/blink/web_tests/http/tests/origin_trials/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: experimentation-dev@chromium.org
-# COMPONENT: Internals>OriginTrials
diff --git a/third_party/blink/web_tests/http/tests/pointer-lock/DIR_METADATA b/third_party/blink/web_tests/http/tests/pointer-lock/DIR_METADATA
new file mode 100644
index 0000000..76ff5193
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/pointer-lock/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Input>PointerLock"
+}
+team_email: "input-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/pointer-lock/OWNERS b/third_party/blink/web_tests/http/tests/pointer-lock/OWNERS
index 97350bb..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/pointer-lock/OWNERS
+++ b/third_party/blink/web_tests/http/tests/pointer-lock/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: input-dev@chromium.org
-# COMPONENT: Blink>Input>PointerLock
diff --git a/third_party/blink/web_tests/http/tests/portals/DIR_METADATA b/third_party/blink/web_tests/http/tests/portals/DIR_METADATA
new file mode 100644
index 0000000..1e4fe50
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/portals/DIR_METADATA
@@ -0,0 +1,3 @@
+monorail {
+  component: "Blink>Portals"
+}
diff --git a/third_party/blink/web_tests/http/tests/portals/OWNERS b/third_party/blink/web_tests/http/tests/portals/OWNERS
index 739ad28..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/portals/OWNERS
+++ b/third_party/blink/web_tests/http/tests/portals/OWNERS
@@ -1 +0,0 @@
-# COMPONENT: Blink>Portals
diff --git a/third_party/blink/web_tests/http/tests/push_messaging/DIR_METADATA b/third_party/blink/web_tests/http/tests/push_messaging/DIR_METADATA
new file mode 100644
index 0000000..b7a1846
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/push_messaging/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "platform-capabilities@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/push_messaging/OWNERS b/third_party/blink/web_tests/http/tests/push_messaging/OWNERS
index 1c13657..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/push_messaging/OWNERS
+++ b/third_party/blink/web_tests/http/tests/push_messaging/OWNERS
@@ -1 +0,0 @@
-# TEAM: platform-capabilities@chromium.org
diff --git a/third_party/blink/web_tests/http/tests/resources/pointer-lock/DIR_METADATA b/third_party/blink/web_tests/http/tests/resources/pointer-lock/DIR_METADATA
new file mode 100644
index 0000000..76ff5193
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/resources/pointer-lock/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Blink>Input>PointerLock"
+}
+team_email: "input-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/resources/pointer-lock/OWNERS b/third_party/blink/web_tests/http/tests/resources/pointer-lock/OWNERS
index 97350bb..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/resources/pointer-lock/OWNERS
+++ b/third_party/blink/web_tests/http/tests/resources/pointer-lock/OWNERS
@@ -1,2 +0,0 @@
-# TEAM: input-dev@chromium.org
-# COMPONENT: Blink>Input>PointerLock
diff --git a/third_party/blink/web_tests/http/tests/security/mixedContent/websocket/DIR_METADATA b/third_party/blink/web_tests/http/tests/security/mixedContent/websocket/DIR_METADATA
new file mode 100644
index 0000000..52775220
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/mixedContent/websocket/DIR_METADATA
@@ -0,0 +1 @@
+team_email: "blink-network-dev@chromium.org"
diff --git a/third_party/blink/web_tests/http/tests/security/mixedContent/websocket/OWNERS b/third_party/blink/web_tests/http/tests/security/mixedContent/websocket/OWNERS
index 46688d7..e69de29 100644
--- a/third_party/blink/web_tests/http/tests/security/mixedContent/websocket/OWNERS
+++ b/third_party/blink/web_tests/http/tests/security/mixedContent/websocket/OWNERS
@@ -1 +0,0 @@
-# TEAM: blink-network-dev@chromium.org
diff --git a/third_party/blink/web_tests/http/tests/security/referrerPolicyHeader/referrer-policy-header-then-invalid-meta.php b/third_party/blink/web_tests/http/tests/security/referrerPolicyHeader/referrer-policy-header-then-invalid-meta.php
new file mode 100644
index 0000000..9dc6ca9
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/referrerPolicyHeader/referrer-policy-header-then-invalid-meta.php
@@ -0,0 +1,20 @@
+<?php
+header("Referrer-Policy: no-referrer");
+?>
+<!DOCTYPE html>
+<head>
+  <!-- An invalid meta referrer policy should be ignored. -->
+  <meta name="referrer" content="origin,unsafe-url">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/resources/get-host-info.js"></script>
+</head>
+<body>
+</body>
+<script>
+    var policy = "no-referrer";
+    var expectedReferrer = "";
+    var navigateTo = "same-origin";
+</script>
+<script src="resources/header-test.js"></script>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/webgpu/cts.html b/third_party/blink/web_tests/wpt_internal/webgpu/cts.html
index 9c2abcd..f99b8bb 100644
--- a/third_party/blink/web_tests/wpt_internal/webgpu/cts.html
+++ b/third_party/blink/web_tests/wpt_internal/webgpu/cts.html
@@ -125,7 +125,42 @@
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToTexture";format="rgba32sint";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToTexture";format="rgba32float";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="CopyToTexture";format="depth32float";*'>
-<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="r8unorm";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="r8snorm";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="r8uint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="r8sint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="r16uint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="r16sint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="r16float";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rg8unorm";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rg8snorm";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rg8uint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rg8sint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="r32uint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="r32sint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="r32float";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rg16uint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rg16sint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rg16float";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rgba8unorm";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rgba8unorm-srgb";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rgba8snorm";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rgba8uint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rgba8sint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="bgra8unorm";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="bgra8unorm-srgb";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rgb10a2unorm";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rg11b10ufloat";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rgb9e5ufloat";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rg32uint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rg32sint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rg32float";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rgba16uint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rgba16sint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rgba16float";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rgba32uint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rgba32sint";*'>
+<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="Sample";format="rgba32float";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="DepthTest";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero_init:uninitialized_texture_is_zero:readMethod="StencilTest";*'>
 <meta name=variant content='?q=webgpu:api,validation,buffer,create:*'>
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 4bbd29344..7822cf7 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -12011,29 +12011,32 @@
 </action>
 
 <action name="ManagedUsers_Whitelist_Added">
+  <obsolete>Deprecated as of 11/2020.</obsolete>
   <owner>bauerb@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <owner>pam@chromium.org</owner>
   <description>
-    A new whitelist has been installed for a supervised user.
+    A new allowlist has been installed for a supervised user.
   </description>
 </action>
 
 <action name="ManagedUsers_Whitelist_Removed">
+  <obsolete>Deprecated as of 11/2020.</obsolete>
   <owner>bauerb@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <owner>pam@chromium.org</owner>
   <description>
-    A whitelist has been uninstalled for a supervised user.
+    An allowlist has been uninstalled for a supervised user.
   </description>
 </action>
 
 <action name="ManagedUsers_Whitelist_UncleanUninstall">
+  <obsolete>Deprecated as of 11/2020.</obsolete>
   <owner>bauerb@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <owner>pam@chromium.org</owner>
   <description>
-    Recorded at startup if an unregistered whitelist has been found. This
+    Recorded at startup if an unregistered allowlist has been found. This
     usually means that an uninstallation did not complete.
   </description>
 </action>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index bfcdcba..e0520e9 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -22773,6 +22773,7 @@
   <int value="807" label="SystemFeaturesDisableMode"/>
   <int value="808" label="IntegratedWebAuthenticationAllowed"/>
   <int value="809" label="ClearBrowsingDataOnExitList"/>
+  <int value="810" label="ProfilePickerOnStartupAvailability"/>
 </enum>
 
 <enum name="EnterprisePolicyDeviceIdValidity">
@@ -58263,6 +58264,11 @@
   <int value="3" label="Confirm Clicked"/>
 </enum>
 
+<enum name="PhoneHubMessageResult">
+  <int value="0" label="Request message was sent"/>
+  <int value="1" label="Response was sent back"/>
+</enum>
+
 <enum name="PhoneHubMessageType">
   <int value="0" label="Provide CrOS State"/>
   <int value="1" label="Phone status snapshot"/>
diff --git a/tools/metrics/histograms/histograms_xml/ash/histograms.xml b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
index 2995fa1..e62a484 100644
--- a/tools/metrics/histograms/histograms_xml/ash/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
@@ -141,6 +141,28 @@
   </summary>
 </histogram>
 
+<histogram name="Ash.AmbientMode.SelectedNumberOfAlbums" units="int"
+    expires_after="2021-05-01">
+  <owner>cowmoo@google.com</owner>
+  <owner>xiaohuic@chromium.org</owner>
+  <summary>
+    Among eligible users with ambient mode enabled, records the selected number
+    of personal photo albums. Recorded once only when users make a selection on
+    the personal photo albums on Settings page.
+  </summary>
+</histogram>
+
+<histogram name="Ash.AmbientMode.TotalNumberOfAlbums" units="int"
+    expires_after="2021-05-01">
+  <owner>cowmoo@google.com</owner>
+  <owner>xiaohuic@chromium.org</owner>
+  <summary>
+    Among eligible users with ambient mode enabled, records the total number of
+    personal photo albums. Recorded once only when users make a selection on the
+    personal photo albums on Settings page.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Ash.Assistant.AnimationSmoothness" units="%"
     expires_after="2021-07-20">
 <!-- Name completed by histogram_suffixes name="AshAssistantAnimationSmoothness" -->
@@ -528,7 +550,7 @@
 </histogram>
 
 <histogram name="Ash.Desks.NumberOfWindowsOnDesk_1" units="units"
-    expires_after="2021-04-11">
+    expires_after="2021-06-30">
   <owner>afakhry@chromium.org</owner>
   <owner>tclaiborne@chromium.org</owner>
   <summary>
@@ -538,7 +560,7 @@
 </histogram>
 
 <histogram name="Ash.Desks.NumberOfWindowsOnDesk_2" units="units"
-    expires_after="2021-04-11">
+    expires_after="2021-06-30">
   <owner>afakhry@chromium.org</owner>
   <owner>tclaiborne@chromium.org</owner>
   <summary>
@@ -548,7 +570,7 @@
 </histogram>
 
 <histogram name="Ash.Desks.NumberOfWindowsOnDesk_3" units="units"
-    expires_after="2021-04-11">
+    expires_after="2021-06-30">
   <owner>afakhry@chromium.org</owner>
   <owner>tclaiborne@chromium.org</owner>
   <summary>
@@ -558,7 +580,7 @@
 </histogram>
 
 <histogram name="Ash.Desks.NumberOfWindowsOnDesk_4" units="units"
-    expires_after="2021-04-11">
+    expires_after="2021-06-30">
   <owner>afakhry@chromium.org</owner>
   <owner>tclaiborne@chromium.org</owner>
   <summary>
@@ -567,6 +589,46 @@
   </summary>
 </histogram>
 
+<histogram name="Ash.Desks.NumberOfWindowsOnDesk_5" units="units"
+    expires_after="2021-06-30">
+  <owner>afakhry@chromium.org</owner>
+  <owner>tclaiborne@chromium.org</owner>
+  <summary>
+    The number of windows on the fifth desk. Emitted when a desk is removed, or
+    a window is moved to another desk.
+  </summary>
+</histogram>
+
+<histogram name="Ash.Desks.NumberOfWindowsOnDesk_6" units="units"
+    expires_after="2021-06-30">
+  <owner>afakhry@chromium.org</owner>
+  <owner>tclaiborne@chromium.org</owner>
+  <summary>
+    The number of windows on the sixth desk. Emitted when a desk is removed, or
+    a window is moved to another desk.
+  </summary>
+</histogram>
+
+<histogram name="Ash.Desks.NumberOfWindowsOnDesk_7" units="units"
+    expires_after="2021-06-30">
+  <owner>afakhry@chromium.org</owner>
+  <owner>tclaiborne@chromium.org</owner>
+  <summary>
+    The number of windows on the seventh desk. Emitted when a desk is removed,
+    or a window is moved to another desk.
+  </summary>
+</histogram>
+
+<histogram name="Ash.Desks.NumberOfWindowsOnDesk_8" units="units"
+    expires_after="2021-06-30">
+  <owner>afakhry@chromium.org</owner>
+  <owner>tclaiborne@chromium.org</owner>
+  <summary>
+    The number of windows on the eighth desk. Emitted when a desk is removed, or
+    a window is moved to another desk.
+  </summary>
+</histogram>
+
 <histogram name="Ash.Desks.PresentationTime.UpdateGesture" units="ms"
     expires_after="2021-09-16">
   <owner>afakhry@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index af8b247..3c139b9 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -8730,12 +8730,15 @@
   </summary>
 </histogram>
 
-<histogram name="ManagedUsers.Whitelist.Count" units="whitelists"
-    expires_after="2021-06-02">
+<histogram name="ManagedUsers.Whitelist.Count" units="allowlists"
+    expires_after="M88">
+  <obsolete>
+    Removed in M89.
+  </obsolete>
   <owner>agawronska@chromium.org</owner>
   <owner>cros-families@google.com</owner>
   <summary>
-    The number of whitelists installed for a supervised user. Recorded at every
+    The number of allowlists installed for a supervised user. Recorded at every
     profile startup.
   </summary>
 </histogram>
diff --git a/tools/metrics/histograms/histograms_xml/phonehub/histograms.xml b/tools/metrics/histograms/histograms_xml/phonehub/histograms.xml
index 32d90b4..0750d53 100644
--- a/tools/metrics/histograms/histograms_xml/phonehub/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/phonehub/histograms.xml
@@ -181,6 +181,24 @@
   </summary>
 </histogram>
 
+<histogram name="PhoneHub.TaskCompletion.{MessageType}.Result"
+    enum="PhoneHubMessageResult" expires_after="2021-12-03">
+  <owner>khorimoto@chromium.org</owner>
+  <owner>phonehub-dev@google.com</owner>
+  <summary>
+    Tracks the result of each type of message request. Logged each time a
+    request message was sent and a response was received.
+  </summary>
+  <token key="MessageType">
+    <variant name="LocatePhone"/>
+    <variant name="NotificationDismissal"/>
+    <variant name="NotificationInlineReply"/>
+    <variant name="ShowNotificationAccessSetup"/>
+    <variant name="SilencePhone"/>
+    <variant name="UpdateBatteryMode"/>
+  </token>
+</histogram>
+
 <histogram name="PhoneHub.Usage.SentMessageTypeCount"
     enum="PhoneHubMessageType" expires_after="M98">
   <owner>khorimoto@chromium.org</owner>
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index e4f8453f..af65d1db 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -363,6 +363,7 @@
  <item id="web_history_query" added_in_milestone="62" hash_code="17400350" type="1" second_id="110307337" content_hash_code="36075147" os_list="linux,windows" semantics_fields="2,3,4" policy_fields="4" file_path="components/history/core/browser/browsing_history_service.cc"/>
  <item id="web_history_service" added_in_milestone="65" hash_code="110307337" type="2" content_hash_code="16140045" os_list="linux,windows" semantics_fields="1,5" policy_fields="-1,3" file_path="components/history/core/browser/web_history_service.cc"/>
  <item id="web_push_message" added_in_milestone="77" hash_code="39886742" type="0" content_hash_code="110064650" os_list="linux,windows" file_path="chrome/browser/sharing/web_push/web_push_sender.cc"/>
+ <item id="webid" added_in_milestone="88" hash_code="113005423" type="0" content_hash_code="121729163" os_list="linux,windows" file_path="content/browser/webid/idp_network_request_manager.cc"/>
  <item id="webrtc_event_log_uploader" added_in_milestone="67" hash_code="24186190" type="0" content_hash_code="11005245" os_list="linux,windows" file_path="chrome/browser/media/webrtc/webrtc_event_log_uploader.cc"/>
  <item id="webrtc_log_upload" added_in_milestone="62" hash_code="62443804" type="0" content_hash_code="33661169" os_list="linux,windows" file_path="chrome/browser/media/webrtc/webrtc_log_uploader.cc"/>
  <item id="webrtc_peer_connection" added_in_milestone="66" hash_code="63497370" type="0" content_hash_code="60553259" os_list="linux,windows" file_path="third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc"/>
diff --git a/tools/traffic_annotation/summary/grouping.xml b/tools/traffic_annotation/summary/grouping.xml
index d0fbce4..9b01b5b 100644
--- a/tools/traffic_annotation/summary/grouping.xml
+++ b/tools/traffic_annotation/summary/grouping.xml
@@ -158,6 +158,7 @@
       <traffic_annotation unique_id="interest_feedv2_image_send"/>
       <traffic_annotation unique_id="sanitized_image_source"/>
       <traffic_annotation unique_id="kaleidoscope_service"/>
+      <traffic_annotation unique_id="webid"/>
     </sender>
   </group>
   <group name="Admin Features">
diff --git a/ui/gl/features.gni b/ui/gl/features.gni
index 817aa6f..95e6239f 100644
--- a/ui/gl/features.gni
+++ b/ui/gl/features.gni
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromecast_build.gni")
 import("//build/config/chromeos/ui_mode.gni")
 
 declare_args() {
@@ -16,7 +17,7 @@
 
   # Should Dawn support be compiled to back the WebGPU implementation?
   # Also controls linking Dawn depedencies in such as SPIRV-Tools/SPIRV-Cross.
-  use_dawn = is_mac || is_win || is_chromeos || is_linux
+  use_dawn = is_mac || is_win || is_chromeos || (is_linux && !is_chromecast)
 
   # Should Dawn test binaries (unittests, end2end_tests, perf_tests) be built?
   # Independent of use_dawn, which controls whether Dawn is used in Chromium.
diff --git a/ui/gl/gl_surface_glx.cc b/ui/gl/gl_surface_glx.cc
index db774dc..91efc6e 100644
--- a/ui/gl/gl_surface_glx.cc
+++ b/ui/gl/gl_surface_glx.cc
@@ -106,6 +106,7 @@
   bool GetSyncValues(int64_t* system_time,
                      int64_t* media_stream_counter,
                      int64_t* swap_buffer_counter) override {
+    x11::Connection::Get()->Flush();
     return glXGetSyncValuesOML(x11::Connection::Get()->GetXlibDisplay(),
                                glx_window_, system_time, media_stream_counter,
                                swap_buffer_counter);
diff --git a/ui/ozone/public/mojom/device_cursor.mojom b/ui/ozone/public/mojom/device_cursor.mojom
index be8fc284a..f165e5eb 100644
--- a/ui/ozone/public/mojom/device_cursor.mojom
+++ b/ui/ozone/public/mojom/device_cursor.mojom
@@ -15,7 +15,7 @@
   // Sets the cursor |bitmaps| on |window| at |point| with
   // |frame_delay_ms|.
   SetCursor(gfx.mojom.AcceleratedWidget window,
-            array<skia.mojom.BitmapWithArbitraryBpp> bitmaps,
+            array<skia.mojom.BitmapN32> bitmaps,
             gfx.mojom.Point point,
             int32 frame_delay_ms);
 
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenCallbackTest.java
index aeee4ea..1f5c9bc 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenCallbackTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenCallbackTest.java
@@ -7,13 +7,15 @@
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.weblayer.Browser;
+import org.chromium.weblayer.BrowserControlsOffsetCallback;
 import org.chromium.weblayer.shell.InstrumentationActivity;
 
 /**
@@ -28,8 +30,8 @@
     private InstrumentationActivity mActivity;
     private TestFullscreenCallback mDelegate;
 
-    @Before
-    public void setUp() {
+    // Launch WL and triggers html fullscreen.
+    private void enterFullscren() {
         String url = mActivityTestRule.getTestDataURL("fullscreen.html");
         mActivity = mActivityTestRule.launchShellWithUrl(url);
         Assert.assertNotNull(mActivity);
@@ -47,6 +49,7 @@
     @SmallTest
     @DisabledTest(message = "crbug.com/1133893")
     public void testFullscreen() {
+        enterFullscren();
         // Second touch exits.
         EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView());
         mDelegate.waitForExitFullscreen();
@@ -56,6 +59,7 @@
     @Test
     @SmallTest
     public void testExitFullscreenWhenDelegateCleared() {
+        enterFullscren();
         // Clearing the FullscreenCallback should exit fullscreen.
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> { mActivity.getTab().setFullscreenCallback(null); });
@@ -66,6 +70,7 @@
     @Test
     @SmallTest
     public void testExitFullscreenUsingRunnable() {
+        enterFullscren();
         // Running the runnable supplied to the delegate should exit fullscreen.
         TestThreadUtils.runOnUiThreadBlocking(mDelegate.mExitFullscreenRunnable);
         mDelegate.waitForExitFullscreen();
@@ -75,6 +80,7 @@
     @Test
     @SmallTest
     public void testExitFullscreenWhenTabDestroyed() {
+        enterFullscren();
         // Destroying the tab should exit fullscreen.
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> { mActivity.getTab().getBrowser().destroyTab(mActivity.getTab()); });
@@ -88,6 +94,57 @@
     @Test
     @SmallTest
     public void testDestroyFragmentWhileFullscreen() {
+        enterFullscren();
         TestThreadUtils.runOnUiThreadBlocking(() -> { mActivity.destroyFragment(); });
     }
+
+    // Waits for the top offset to go to -height. This means the view is completely hidden.
+    private final class BrowserControlsOffsetCallbackImpl extends BrowserControlsOffsetCallback {
+        private final CallbackHelper mCallbackHelper;
+        BrowserControlsOffsetCallbackImpl(CallbackHelper callbackHelper) {
+            mCallbackHelper = callbackHelper;
+        }
+        @Override
+        public void onTopViewOffsetChanged(int offset) {
+            int height = mActivity.getTopContentsContainer().getHeight();
+            if (height != 0 && offset == -height) {
+                mCallbackHelper.notifyCalled();
+            }
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testTopViewRemainsHiddenOnFullscreenRotation() throws Exception {
+        String url = mActivityTestRule.getTestDataURL("rotation2.html");
+        mActivity = mActivityTestRule.launchShellWithUrl(url);
+        // Ensure the fragment is not recreated as otherwise things bounce around more.
+        mActivityTestRule.setRetainInstance(true);
+        Assert.assertNotNull(mActivity);
+        mDelegate = new TestFullscreenCallback();
+        CallbackHelper callbackHelper = new CallbackHelper();
+        // The offsets may move around during rotation. Wait for reattachment before installing
+        // the BrowserControlsOffsetCallback.
+        InstrumentationActivity.registerOnCreatedCallback(
+                new InstrumentationActivity.OnCreatedCallback() {
+                    @Override
+                    public void onCreated(Browser browser) {
+                        browser.registerBrowserControlsOffsetCallback(
+                                new BrowserControlsOffsetCallbackImpl(callbackHelper));
+                    }
+                });
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { mActivity.getTab().setFullscreenCallback(mDelegate); });
+
+        EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView());
+        mDelegate.waitForFullscreen();
+        Assert.assertEquals(1, mDelegate.mEnterFullscreenCount);
+
+        // There should be a fullscreen element.
+        Assert.assertTrue(mActivityTestRule.executeScriptAndExtractBoolean(
+                "document.fullscreenElement != null"));
+
+        // Rotation should trigger the view being totally hidden.
+        callbackHelper.waitForFirst();
+    }
 }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java
index 5fa45786..9d362324 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java
@@ -376,7 +376,12 @@
         mLastHeight = height;
         if (mLastWidth > 0 && mLastHeight > 0 && mViewResourceAdapter == null) {
             createAdapterAndLayer();
-            if (mLastShownAmountWithView == DEFAULT_LAST_SHOWN_AMOUNT && mSavedState != null) {
+            if (mIsFullscreen) {
+                // This calls setControlsOffset() as onOffsetsChanged() does (mostly) nothing when
+                // fullscreen.
+                setControlsOffset(mIsTop ? -mLastHeight : mLastHeight, 0);
+            } else if (mLastShownAmountWithView == DEFAULT_LAST_SHOWN_AMOUNT
+                    && mSavedState != null) {
                 // If there wasn't a View before and we have non-empty saved state from a previous
                 // BrowserControlsContainerView instance, apply those saved offsets now. We can't
                 // rely on BrowserControlsOffsetManager to notify us of the correct location as we
diff --git a/weblayer/browser/translate_browsertest.cc b/weblayer/browser/translate_browsertest.cc
index effcfb82..570839f 100644
--- a/weblayer/browser/translate_browsertest.cc
+++ b/weblayer/browser/translate_browsertest.cc
@@ -418,7 +418,8 @@
   EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
 
   // Before browsing, set autotranslate from French to Chinese.
-  translate_client->GetTranslatePrefs()->WhitelistLanguagePair("fr", "zh-CN");
+  translate_client->GetTranslatePrefs()->AddLanguagePairToAlwaysTranslateList(
+      "fr", "zh-CN");
 
   // Navigate to a page in French.
   ResetLanguageDeterminationWaiter();
diff --git a/weblayer/test/data/rotation2.html b/weblayer/test/data/rotation2.html
new file mode 100644
index 0000000..1640b1a
--- /dev/null
+++ b/weblayer/test/data/rotation2.html
@@ -0,0 +1,16 @@
+<html>
+  <body style="height:5000px">
+    <p>A page that will rotate on touch.</p>
+  </body>
+  <script>
+    async function toggleFullscreen() {
+      if (!document.fullscreenElement) {
+        await document.documentElement.requestFullscreen();
+        await screen.orientation.lock("landscape-primary");
+      } else {
+        document.exitFullscreen();
+      }
+    }
+    document.addEventListener('touchend', function(e) { toggleFullscreen(); }, false);
+  </script>
+</html>