diff --git a/DEPS b/DEPS
index f9540d8..9455b4ad 100644
--- a/DEPS
+++ b/DEPS
@@ -239,7 +239,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': 'e17fd4fea551ea554b67af7c044b5fbc1e90ce00',
+  'skia_revision': 'dc60ca197e0280fb2fe82e42321498a0443b408f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -247,7 +247,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '8419f4fcd1ec05ffac0ac51a1ac73f343dab00f4',
+  'angle_revision': 'feb599ad894ba42e14c89a0c61f34bad8d575eeb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -270,7 +270,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling googletest
   # and whatever else without interference from each other.
-  'googletest_revision': '054a986a8513149e8374fc669a5fe40117ca6b41',
+  'googletest_revision': '97a467571a0f615a4d96e79e4399c43221ca1232',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling lighttpd
   # and whatever else without interference from each other.
@@ -306,7 +306,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'b759738dea9abafcbd0dc69553fd049a86e7b56b',
+  'catapult_revision': '58f3a92099a02ccc517ce7bce93b75da0766b451',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -314,7 +314,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': 'b47975a82916c1852f5ef7e2235b8648bbb0df38',
+  'devtools_frontend_revision': 'd692cac31ffea822377407e775750d9f6a042eba',
   # 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.
@@ -354,7 +354,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '5397f9f9d0fa2d4adb03553f60d3fc4d4db5936c',
+  'dawn_revision': '736dd07303323021e92fddc5be02a63af66c20ce',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -635,7 +635,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'fae0808b93a28b00796cdf10d3f50e16aaf2d71e',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '787b5fb459b5c95f000bd8a75c405f36292ccaea',
       'condition': 'checkout_ios',
   },
 
@@ -716,7 +716,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/linux-amd64',
-          'version': 'h4OErwvGMzWmTzk59jLdAUp9o00HkQ6vKZlwtDCstocC',
+          'version': 'pOC_fgXF3UQ176xy8dq5Ue_0mDiLot4PGslfbyWcVZsC',
         },
       ],
       'dep_type': 'cipd',
@@ -727,7 +727,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': 'ST_XL0WVEj5Xxvj-kAcMuyZsvgGw_VNlvbupF6-aFXUC',
+          'version': 'LeoA0Wv35bgH_p5lOkwPxa1y0XTsB5tlcoYRFw8ioY4C',
         },
       ],
       'dep_type': 'cipd',
@@ -799,7 +799,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': '22GLME4Ax-0a0SJo1MSxARBmZMMz-SMyTlRHX8fYhW8C',
+          'version': 'rjqESjGVnqj61OsILGz2zAx9aZu_63YrdoiwrUlJrHoC',
       },
     ],
     'condition': 'checkout_android',
@@ -1018,7 +1018,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' + '@' + '7f13e5c5ff2def048f60931a4f09bdcc0c7c3965',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '1cad40c1e7201ae3c1273066c335771590bdce2b',
       'condition': 'checkout_chromeos',
   },
 
@@ -1038,7 +1038,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '48b35e123dff26b1ede11104316cbb7f462db4e8',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'e88997a2613aaaabd83718ef25ff9a47bb70090c',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1421,7 +1421,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '22467673a50f5599e7d8b1f49982faf7c1bc30d9',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '15e3467297c3877d090e92940a04b085318ab933',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1582,7 +1582,7 @@
   },
 
   'src/third_party/tflite/src':
-    Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + 'aa79adb41c2f60c81d5d46d994c061abab686bb1',
+    Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + 'dd57f5328f37a81197b0dadd052e05c9d9461b16',
 
   'src/third_party/turbine': {
       'packages': [
@@ -1700,7 +1700,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b52ee54b2a18637068d39e2aa0f4d94588076891',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@cc4f8ef12d745384de360d967ac3f324fdeb8b67',
     'condition': 'checkout_src_internal',
   },
 
@@ -1719,7 +1719,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'RcDM8Mq_LrCGiuxfQto-gkDVFbqCRy6FE8lzPeHsNEwC',
+        'version': '27KUeowb-avScJevs9PyckybR1s34lIOg_CMEaolREQC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 0812dd74..8b298f3 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -2748,6 +2748,7 @@
     "//chromeos/components/sensors:sensors",
     "//chromeos/components/sensors:test_support",
     "//chromeos/components/sensors/mojom",
+    "//chromeos/constants",
     "//chromeos/dbus:test_support",
     "//chromeos/dbus/audio",
     "//chromeos/dbus/hammerd",
diff --git a/ash/DEPS b/ash/DEPS
index 6e2b0963..f07d3bc 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -61,6 +61,7 @@
   "+chromeos/components/multidevice",
   "+chromeos/components/quick_answers",
   "+chromeos/components/sensors",
+  "+chromeos/constants",
   # crosapi is an API to support lacros.
   "+chromeos/crosapi",
   # //ash can access all D-Bus client libraries. The mustash project previously
diff --git a/ash/accessibility/accessibility_controller_impl.cc b/ash/accessibility/accessibility_controller_impl.cc
index 1958716..d73d3b43 100644
--- a/ash/accessibility/accessibility_controller_impl.cc
+++ b/ash/accessibility/accessibility_controller_impl.cc
@@ -430,27 +430,267 @@
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
-enum class SwitchAccessCommandKeyCode {
+enum class SwitchAccessKeyCode {
   kUnknown = 0,
-  kNone = 1,
-  kSpace = 2,
-  kEnter = 3,
-  kMaxValue = kEnter,
+  kKeycode1 = 1,
+  kKeycode2 = 2,
+  kKeycode3 = 3,
+  kKeycode4 = 4,
+  kKeycode5 = 5,
+  kKeycode6 = 6,
+  kKeycode7 = 7,
+  kBackspace = 8,
+  kTab = 9,
+  kKeycode10 = 10,
+  kKeycode11 = 11,
+  kClear = 12,
+  kReturn = 13,
+  kKeycode14 = 14,
+  kKeycode15 = 15,
+  kShift = 16,
+  kControl = 17,
+  kAlt = 18,
+  kPause = 19,
+  kCapital = 20,
+  kKana = 21,
+  kKeycode22 = 22,
+  kJunja = 23,
+  kFinal = 24,
+  kHanja = 25,
+  kKeycode26 = 26,
+  kEscape = 27,
+  kConvert = 28,
+  kNonconvert = 29,
+  kAccept = 30,
+  kModechange = 31,
+  kSpace = 32,
+  kPrior = 33,
+  kNext = 34,
+  kEnd = 35,
+  kHome = 36,
+  kLeft = 37,
+  kUp = 38,
+  kRight = 39,
+  kDown = 40,
+  kSelect = 41,
+  kPrint = 42,
+  kExecute = 43,
+  kSnapshot = 44,
+  kInsert = 45,
+  kKeyDelete = 46,
+  kHelp = 47,
+  kNum0 = 48,
+  kNum1 = 49,
+  kNum2 = 50,
+  kNum3 = 51,
+  kNum4 = 52,
+  kNum5 = 53,
+  kNum6 = 54,
+  kNum7 = 55,
+  kNum8 = 56,
+  kNum9 = 57,
+  kKeycode58 = 58,
+  kKeycode59 = 59,
+  kKeycode60 = 60,
+  kKeycode61 = 61,
+  kKeycode62 = 62,
+  kKeycode63 = 63,
+  kKeycode64 = 64,
+  kA = 65,
+  kB = 66,
+  kC = 67,
+  kD = 68,
+  kE = 69,
+  kF = 70,
+  kG = 71,
+  kH = 72,
+  kI = 73,
+  kJ = 74,
+  kK = 75,
+  kL = 76,
+  kM = 77,
+  kN = 78,
+  kO = 79,
+  kP = 80,
+  kQ = 81,
+  kR = 82,
+  kS = 83,
+  kT = 84,
+  kU = 85,
+  kV = 86,
+  kW = 87,
+  kX = 88,
+  kY = 89,
+  kZ = 90,
+  kLwin = 91,
+  kRwin = 92,
+  kApps = 93,
+  kKeycode94 = 94,
+  kSleep = 95,
+  kNumpad0 = 96,
+  kNumpad1 = 97,
+  kNumpad2 = 98,
+  kNumpad3 = 99,
+  kNumpad4 = 100,
+  kNumpad5 = 101,
+  kNumpad6 = 102,
+  kNumpad7 = 103,
+  kNumpad8 = 104,
+  kNumpad9 = 105,
+  kMultiply = 106,
+  kAdd = 107,
+  kSeparator = 108,
+  kSubtract = 109,
+  kDecimal = 110,
+  kDivide = 111,
+  kF1 = 112,
+  kF2 = 113,
+  kF3 = 114,
+  kF4 = 115,
+  kF5 = 116,
+  kF6 = 117,
+  kF7 = 118,
+  kF8 = 119,
+  kF9 = 120,
+  kF10 = 121,
+  kF11 = 122,
+  kF12 = 123,
+  kF13 = 124,
+  kF14 = 125,
+  kF15 = 126,
+  kF16 = 127,
+  kF17 = 128,
+  kF18 = 129,
+  kF19 = 130,
+  kF20 = 131,
+  kF21 = 132,
+  kF22 = 133,
+  kF23 = 134,
+  kF24 = 135,
+  kKeycode136 = 136,
+  kKeycode137 = 137,
+  kKeycode138 = 138,
+  kKeycode139 = 139,
+  kKeycode140 = 140,
+  kKeycode141 = 141,
+  kKeycode142 = 142,
+  kKeycode143 = 143,
+  kNumlock = 144,
+  kScroll = 145,
+  kKeycode146 = 146,
+  kKeycode147 = 147,
+  kKeycode148 = 148,
+  kKeycode149 = 149,
+  kKeycode150 = 150,
+  kWlan = 151,
+  kPower = 152,
+  kAssistant = 153,
+  kKeycode154 = 154,
+  kKeycode155 = 155,
+  kKeycode156 = 156,
+  kKeycode157 = 157,
+  kKeycode158 = 158,
+  kKeycode159 = 159,
+  kLshift = 160,
+  kRshift = 161,
+  kLcontrol = 162,
+  kRcontrol = 163,
+  kLmenu = 164,
+  kRmenu = 165,
+  kBrowserBack = 166,
+  kBrowserForward = 167,
+  kBrowserRefresh = 168,
+  kBrowserStop = 169,
+  kBrowserSearch = 170,
+  kBrowserFavorites = 171,
+  kBrowserHome = 172,
+  kVolumeMute = 173,
+  kVolumeDown = 174,
+  kVolumeUp = 175,
+  kMediaNextTrack = 176,
+  kMediaPrevTrack = 177,
+  kMediaStop = 178,
+  kMediaPlayPause = 179,
+  kMediaLaunchMail = 180,
+  kMediaLaunchMediaSelect = 181,
+  kMediaLaunchApp1 = 182,
+  kMediaLaunchApp2 = 183,
+  kKeycode184 = 184,
+  kKeycode185 = 185,
+  kOem1 = 186,
+  kOemPlus = 187,
+  kOemComma = 188,
+  kOemMinus = 189,
+  kOemPeriod = 190,
+  kOem2 = 191,
+  kOem3 = 192,
+  kKeycode193 = 193,
+  kKeycode194 = 194,
+  kKeycode195 = 195,
+  kKeycode196 = 196,
+  kKeycode197 = 197,
+  kKeycode198 = 198,
+  kKeycode199 = 199,
+  kKeycode200 = 200,
+  kKeycode201 = 201,
+  kKeycode202 = 202,
+  kKeycode203 = 203,
+  kKeycode204 = 204,
+  kKeycode205 = 205,
+  kKeycode206 = 206,
+  kKeycode207 = 207,
+  kKeycode208 = 208,
+  kKeycode209 = 209,
+  kKeycode210 = 210,
+  kKeycode211 = 211,
+  kKeycode212 = 212,
+  kKeycode213 = 213,
+  kKeycode214 = 214,
+  kKeycode215 = 215,
+  kBrightnessDown = 216,
+  kBrightnessUp = 217,
+  kKbdBrightnessDown = 218,
+  kOem4 = 219,
+  kOem5 = 220,
+  kOem6 = 221,
+  kOem7 = 222,
+  kOem8 = 223,
+  kKeycode224 = 224,
+  kAltgr = 225,
+  kOem102 = 226,
+  kKeycode227 = 227,
+  kKeycode228 = 228,
+  kProcesskey = 229,
+  kCompose = 230,
+  kPacket = 231,
+  kKbdBrightnessUp = 232,
+  kKeycode233 = 233,
+  kKeycode234 = 234,
+  kKeycode235 = 235,
+  kKeycode236 = 236,
+  kKeycode237 = 237,
+  kKeycode238 = 238,
+  kKeycode239 = 239,
+  kKeycode240 = 240,
+  kKeycode241 = 241,
+  kKeycode242 = 242,
+  kDbeSbcschar = 243,
+  kDbeDbcschar = 244,
+  kKeycode245 = 245,
+  kAttn = 246,
+  kCrsel = 247,
+  kExsel = 248,
+  kEreof = 249,
+  kPlay = 250,
+  kZoom = 251,
+  kNoname = 252,
+  kPa1 = 253,
+  kOemClear = 254,
+  kKeycode255 = 255,
+  kNone = 256,
+  kMaxValue = kNone,
 };
 
-SwitchAccessCommandKeyCode UmaValueForKeyCode(int key_code) {
-  switch (key_code) {
-    case 0:
-      return SwitchAccessCommandKeyCode::kNone;
-    case 13:
-      return SwitchAccessCommandKeyCode::kEnter;
-    case 32:
-      return SwitchAccessCommandKeyCode::kSpace;
-    default:
-      return SwitchAccessCommandKeyCode::kUnknown;
-  }
-}
-
 void MigrateSwitchAccessKeyCodePref(PrefService* prefs,
                                     const std::string& old_pref,
                                     const std::string& new_pref) {
@@ -1835,12 +2075,11 @@
 
   std::string uma_name = UmaNameForSwitchAccessCommand(command);
   if (key_codes.size() == 0) {
-    SwitchAccessCommandKeyCode uma_value = UmaValueForKeyCode(0);
-    base::UmaHistogramEnumeration(uma_name, uma_value);
+    base::UmaHistogramEnumeration(uma_name, SwitchAccessKeyCode::kNone);
   }
   for (const auto& key_code : key_codes) {
-    SwitchAccessCommandKeyCode uma_value = UmaValueForKeyCode(key_code.first);
-    base::UmaHistogramEnumeration(uma_name, uma_value);
+    base::UmaHistogramEnumeration(
+        uma_name, static_cast<SwitchAccessKeyCode>(key_code.first));
   }
 
   accessibility_event_rewriter_->SetKeyCodesForSwitchAccessCommand(key_codes,
diff --git a/ash/app_list/views/assistant/assistant_dialog_plate_unittest.cc b/ash/app_list/views/assistant/assistant_dialog_plate_unittest.cc
index 2f2cc15..12ced5ef 100644
--- a/ash/app_list/views/assistant/assistant_dialog_plate_unittest.cc
+++ b/ash/app_list/views/assistant/assistant_dialog_plate_unittest.cc
@@ -8,13 +8,13 @@
 #include "ash/assistant/test/assistant_ash_test_base.h"
 #include "ash/assistant/ui/assistant_ui_constants.h"
 #include "ash/assistant/ui/assistant_view_ids.h"
-#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/style/color_provider.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/style/ash_color_provider.h"
 #include "base/test/scoped_feature_list.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/views/controls/textfield/textfield.h"
 
@@ -23,10 +23,11 @@
 using AssistantDialogPlateTest = AssistantAshTestBase;
 
 TEST_F(AssistantDialogPlateTest, DarkAndLightTheme) {
-  base::test::ScopedFeatureList scoped_feature_list(features::kDarkLightMode);
+  base::test::ScopedFeatureList scoped_feature_list(
+      chromeos::features::kDarkLightMode);
   AshColorProvider::Get()->OnActiveUserPrefServiceChanged(
       Shell::Get()->session_controller()->GetActivePrefService());
-  ASSERT_TRUE(features::IsDarkLightModeEnabled());
+  ASSERT_TRUE(chromeos::features::IsDarkLightModeEnabled());
   ASSERT_FALSE(ColorProvider::Get()->IsDarkModeEnabled());
 
   ShowAssistantUi();
@@ -50,7 +51,7 @@
 }
 
 TEST_F(AssistantDialogPlateTest, DarkAndLightModeFlagOff) {
-  ASSERT_FALSE(features::IsDarkLightModeEnabled());
+  ASSERT_FALSE(chromeos::features::IsDarkLightModeEnabled());
 
   ShowAssistantUi();
 
diff --git a/ash/app_list/views/assistant/assistant_main_stage_unittest.cc b/ash/app_list/views/assistant/assistant_main_stage_unittest.cc
index b4b2baa2..e02c537 100644
--- a/ash/app_list/views/assistant/assistant_main_stage_unittest.cc
+++ b/ash/app_list/views/assistant/assistant_main_stage_unittest.cc
@@ -13,6 +13,7 @@
 #include "ash/shell.h"
 #include "ash/style/ash_color_provider.h"
 #include "base/test/scoped_feature_list.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
@@ -44,7 +45,8 @@
 };
 
 TEST_F(AssistantMainStageTest, DarkAndLightTheme) {
-  base::test::ScopedFeatureList scoped_feature_list(features::kDarkLightMode);
+  base::test::ScopedFeatureList scoped_feature_list(
+      chromeos::features::kDarkLightMode);
   AshColorProvider::Get()->OnActiveUserPrefServiceChanged(
       Shell::Get()->session_controller()->GetActivePrefService());
   ASSERT_TRUE(features::IsDarkLightModeEnabled());
diff --git a/ash/app_list/views/assistant/assistant_page_view_unittest.cc b/ash/app_list/views/assistant/assistant_page_view_unittest.cc
index 419e750..c410e9f 100644
--- a/ash/app_list/views/assistant/assistant_page_view_unittest.cc
+++ b/ash/app_list/views/assistant/assistant_page_view_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "chromeos/services/assistant/public/cpp/assistant_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -932,7 +933,7 @@
 
 TEST_F(AssistantPageNonBubbleTest, ThemeDarkLightMode) {
   base::test::ScopedFeatureList scoped_feature_list_enable_dark_light_mode(
-      features::kDarkLightMode);
+      chromeos::features::kDarkLightMode);
   base::test::ScopedFeatureList scoped_feature_list_disable_blur;
   scoped_feature_list_disable_blur.InitAndDisableFeature(
       features::kEnableBackgroundBlur);
@@ -974,7 +975,8 @@
 }
 
 TEST_F(AssistantPageNonBubbleTest, ThemeDarkLightModeWithBlur) {
-  base::test::ScopedFeatureList scoped_feature_list(features::kDarkLightMode);
+  base::test::ScopedFeatureList scoped_feature_list(
+      chromeos::features::kDarkLightMode);
   AshColorProvider::Get()->OnActiveUserPrefServiceChanged(
       Shell::Get()->session_controller()->GetActivePrefService());
   ASSERT_TRUE(features::IsBackgroundBlurEnabled());
@@ -1036,7 +1038,8 @@
 TEST_F(AssistantPageBubbleTest, BackgroundColorInDarkLightMode) {
   ASSERT_TRUE(features::IsProductivityLauncherEnabled());
 
-  base::test::ScopedFeatureList scoped_feature_list(features::kDarkLightMode);
+  base::test::ScopedFeatureList scoped_feature_list(
+      chromeos::features::kDarkLightMode);
   AshColorProvider::Get()->OnActiveUserPrefServiceChanged(
       Shell::Get()->session_controller()->GetActivePrefService());
   ASSERT_FALSE(ColorProvider::Get()->IsDarkModeEnabled());
diff --git a/ash/app_list/views/privacy_container_view.cc b/ash/app_list/views/privacy_container_view.cc
index 80fd46f0..a6765658 100644
--- a/ash/app_list/views/privacy_container_view.cc
+++ b/ash/app_list/views/privacy_container_view.cc
@@ -49,6 +49,11 @@
   return nullptr;
 }
 
+int PrivacyContainerView::ScheduleResultAnimations(int preceeding_results) {
+  NOTREACHED();
+  return -1;
+}
+
 int PrivacyContainerView::DoUpdate() {
   const bool should_show_suggested_content =
       view_delegate()->ShouldShowSuggestedContentInfo();
diff --git a/ash/app_list/views/privacy_container_view.h b/ash/app_list/views/privacy_container_view.h
index ae02103..edc3f5e3 100644
--- a/ash/app_list/views/privacy_container_view.h
+++ b/ash/app_list/views/privacy_container_view.h
@@ -26,6 +26,7 @@
 
   // SearchResultContainerView:
   SearchResultBaseView* GetResultViewAt(size_t index) override;
+  int ScheduleResultAnimations(int preceeding_results) override;
 
  private:
   friend class test::PrivacyContainerViewTest;
diff --git a/ash/app_list/views/productivity_launcher_search_view.cc b/ash/app_list/views/productivity_launcher_search_view.cc
index fe7e638d..ed45fa3 100644
--- a/ash/app_list/views/productivity_launcher_search_view.cc
+++ b/ash/app_list/views/productivity_launcher_search_view.cc
@@ -15,6 +15,7 @@
 #include "ash/app_list/views/search_box_view.h"
 #include "ash/app_list/views/search_result_list_view.h"
 #include "ash/app_list/views/search_result_view.h"
+#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/app_list/app_list_color_provider.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "base/bind.h"
@@ -145,6 +146,14 @@
     result_count += view->num_results();
   }
 
+  if (features::IsProductivityLauncherAnimationEnabled()) {
+    int scheduled_result_animations = 0;
+    for (SearchResultContainerView* view : result_container_views_) {
+      scheduled_result_animations +=
+          view->ScheduleResultAnimations(scheduled_result_animations);
+    }
+  }
+
   last_search_result_count_ = result_count;
 
   ScheduleResultsChangedA11yNotification();
diff --git a/ash/app_list/views/productivity_launcher_search_view_unittest.cc b/ash/app_list/views/productivity_launcher_search_view_unittest.cc
index e07cd53..892541ee 100644
--- a/ash/app_list/views/productivity_launcher_search_view_unittest.cc
+++ b/ash/app_list/views/productivity_launcher_search_view_unittest.cc
@@ -64,6 +64,7 @@
     for (int i = 0; i < new_result_count; ++i) {
       std::unique_ptr<TestSearchResult> result =
           std::make_unique<TestSearchResult>();
+      result->set_result_id(base::NumberToString(init_id + i));
       result->set_display_type(ash::SearchResultDisplayType::kList);
       result->set_title(
           base::UTF8ToUTF16(base::StringPrintf("Result %d", init_id + i)));
@@ -82,6 +83,7 @@
                              int new_result_count) {
     std::unique_ptr<TestSearchResult> result =
         std::make_unique<TestSearchResult>();
+    result->set_result_id(base::NumberToString(init_id));
     result->set_display_type(ash::SearchResultDisplayType::kAnswerCard);
     result->set_title(base::UTF8ToUTF16(base::StringPrintf("Answer Card")));
     result->set_display_score(1000);
@@ -155,6 +157,11 @@
 
   // Press a key to start a search.
   PressAndReleaseKey(ui::VKEY_A);
+  // Populate answer card result.
+  auto* test_helper = GetAppListTestHelper();
+  SearchModel::SearchResults* results = test_helper->GetSearchResults();
+  SetUpAnswerCardResult(results, 1, 1);
+  GetProductivityLauncherSearchView()->OnSearchResultContainerResultsChanged();
 
   // Check result container visibility.
   std::vector<SearchResultContainerView*> result_containers =
@@ -539,7 +546,6 @@
   SearchModel::SearchResults* results = test_helper->GetSearchResults();
 
   // Delete all results and verify the bubble search page's A11yNodeData.
-
   AppListModelProvider::Get()->search_model()->DeleteAllResults();
   auto* search_view = GetProductivityLauncherSearchView();
   search_view->OnSearchResultContainerResultsChanged();
@@ -548,7 +554,8 @@
   std::vector<SearchResultContainerView*> result_containers =
       search_view->result_container_views_for_test();
   ASSERT_EQ(static_cast<int>(result_containers.size()), kResultContainersCount);
-  EXPECT_TRUE(result_containers[0]->GetVisible());
+  // Container view should not be shown if no result is present.
+  EXPECT_FALSE(result_containers[0]->GetVisible());
   EXPECT_TRUE(search_view->GetVisible());
 
   ui::AXNodeData data;
diff --git a/ash/app_list/views/result_selection_controller_unittest.cc b/ash/app_list/views/result_selection_controller_unittest.cc
index 9c9ea2f4..8138038 100644
--- a/ash/app_list/views/result_selection_controller_unittest.cc
+++ b/ash/app_list/views/result_selection_controller_unittest.cc
@@ -136,6 +136,10 @@
     DCHECK_LT(index, search_result_views_.size());
     return search_result_views_[index].get();
   }
+  int ScheduleResultAnimations(int preceeding_results) override {
+    NOTREACHED();
+    return -1;
+  }
 
  private:
   int DoUpdate() override { return search_result_views_.size(); }
diff --git a/ash/app_list/views/search_result_container_view.h b/ash/app_list/views/search_result_container_view.h
index 4ba8266..75d0351 100644
--- a/ash/app_list/views/search_result_container_view.h
+++ b/ash/app_list/views/search_result_container_view.h
@@ -55,6 +55,7 @@
   int num_results() const { return num_results_; }
 
   virtual SearchResultBaseView* GetResultViewAt(size_t index) = 0;
+  virtual int ScheduleResultAnimations(int preceeding_results) = 0;
 
   bool horizontally_traversable() const { return horizontally_traversable_; }
 
diff --git a/ash/app_list/views/search_result_list_view.cc b/ash/app_list/views/search_result_list_view.cc
index 67b112a6f..9af36a3 100644
--- a/ash/app_list/views/search_result_list_view.cc
+++ b/ash/app_list/views/search_result_list_view.cc
@@ -267,6 +267,43 @@
   return categorical_search_types;
 }
 
+int SearchResultListView::ScheduleResultAnimations(
+    int preceeding_result_count) {
+  DCHECK(features::IsProductivityLauncherAnimationEnabled());
+
+  if (num_results_ < 1 || !enabled_) {
+    SetVisible(false);
+    for (auto* result_view : search_result_views_) {
+      result_view->SetResult(nullptr);
+      result_view->SetVisible(false);
+    }
+    return 0;
+  }
+
+  // Tracks the number of animations scheduled so far.
+  int animated_view_count = 0;
+
+  SetVisible(true);
+  if (title_label_->GetVisible()) {
+    // TODO(crbug/1216097) Add animation for title label.
+    animated_view_count += 1;
+  }
+
+  for (size_t i = 0; i < search_result_views_.size(); ++i) {
+    SearchResultView* result_view = GetResultViewAt(i);
+    if (result_view->result() != nullptr) {
+      result_view->SizeToPreferredSize();
+      // TODO(crbug/1216097) Add animation for search result view.
+      result_view->SetVisible(true);
+      animated_view_count += 1;
+    } else {
+      result_view->SetVisible(false);
+    }
+  }
+
+  return animated_view_count;
+}
+
 int SearchResultListView::DoUpdate() {
   if (productivity_launcher_index_.has_value()) {
     std::vector<ash::AppListSearchResultCategory>* ordered_categories =
@@ -281,8 +318,8 @@
     }
   }
 
-  SetVisible(enabled_);
   if (!enabled_ || !GetWidget() || !GetWidget()->IsVisible()) {
+    SetVisible(false);
     for (auto* result_view : search_result_views_) {
       result_view->SetResult(nullptr);
       result_view->SetVisible(false);
@@ -517,19 +554,30 @@
 std::vector<SearchResult*> SearchResultListView::UpdateResultViews() {
   std::vector<SearchResult*> display_results = GetCategorizedSearchResults();
   size_t num_results = display_results.size();
+  num_results_ = num_results;
   for (size_t i = 0; i < search_result_views_.size(); ++i) {
     SearchResultView* result_view = GetResultViewAt(i);
     if (i < num_results) {
       result_view->SetResult(display_results[i]);
       result_view->SizeToPreferredSize();
-      result_view->SetVisible(true);
+      // Show animations for productivity launcher will be scheduled once all
+      // search result list views are updated.
+      if (!features::IsProductivityLauncherAnimationEnabled())
+        result_view->SetVisible(true);
     } else {
       result_view->SetResult(nullptr);
       result_view->SetVisible(false);
     }
   }
-  // the search_result_list_view should be hidden if there are no results.
-  SetVisible(num_results > 0);
+
+  if (features::IsProductivityLauncherAnimationEnabled()) {
+    // Show animations for productivity launcher will be scheduled once all
+    // search result list views are updated.
+    SetVisible(false);
+  } else {
+    // the search_result_list_view should be hidden if there are no results.
+    SetVisible(num_results > 0);
+  }
   return display_results;
 }
 
diff --git a/ash/app_list/views/search_result_list_view.h b/ash/app_list/views/search_result_list_view.h
index a1e0f15..2457ee7 100644
--- a/ash/app_list/views/search_result_list_view.h
+++ b/ash/app_list/views/search_result_list_view.h
@@ -102,6 +102,7 @@
 
   // Overridden from SearchResultContainerView:
   SearchResultView* GetResultViewAt(size_t index) override;
+  int ScheduleResultAnimations(int preceeding_result_count) override;
 
   AppListMainView* app_list_main_view() const { return main_view_; }
 
@@ -198,6 +199,9 @@
   // search result actions. Used to filter those results out from the list of
   // shown results until results in the search model get refreshed.
   std::set<std::string> removed_results_;
+
+  // The number of results shown by the list view.
+  int num_results_ = 0;
 };
 
 }  // namespace ash
diff --git a/ash/app_list/views/search_result_tile_item_list_view.cc b/ash/app_list/views/search_result_tile_item_list_view.cc
index f369559..47dd9605 100644
--- a/ash/app_list/views/search_result_tile_item_list_view.cc
+++ b/ash/app_list/views/search_result_tile_item_list_view.cc
@@ -116,6 +116,12 @@
   return tile_views_[index];
 }
 
+int SearchResultTileItemListView::ScheduleResultAnimations(
+    int preceeding_results) {
+  NOTREACHED();
+  return -1;
+}
+
 int SearchResultTileItemListView::DoUpdate() {
   if (!GetWidget() || !GetWidget()->IsVisible() || !GetWidget()->IsActive()) {
     for (size_t i = 0; i < max_search_result_tiles_; ++i) {
diff --git a/ash/app_list/views/search_result_tile_item_list_view.h b/ash/app_list/views/search_result_tile_item_list_view.h
index ec25600..fb85deb 100644
--- a/ash/app_list/views/search_result_tile_item_list_view.h
+++ b/ash/app_list/views/search_result_tile_item_list_view.h
@@ -34,6 +34,7 @@
 
   // Overridden from SearchResultContainerView:
   SearchResultTileItemView* GetResultViewAt(size_t index) override;
+  int ScheduleResultAnimations(int preceeding_results) override;
 
   // Overridden from views::View:
   const char* GetClassName() const override;
diff --git a/ash/app_list/views/suggestion_chip_container_view.cc b/ash/app_list/views/suggestion_chip_container_view.cc
index f039dea..c5274c3f 100644
--- a/ash/app_list/views/suggestion_chip_container_view.cc
+++ b/ash/app_list/views/suggestion_chip_container_view.cc
@@ -178,6 +178,12 @@
   return "SuggestionChipContainerView";
 }
 
+int SuggestionChipContainerView::ScheduleResultAnimations(
+    int preceeding_results) {
+  NOTREACHED();
+  return -1;
+}
+
 void SuggestionChipContainerView::Layout() {
   // Only show the chips that fit in this view's contents bounds.
   int total_width = 0;
diff --git a/ash/app_list/views/suggestion_chip_container_view.h b/ash/app_list/views/suggestion_chip_container_view.h
index ca72ea6..f2208ac 100644
--- a/ash/app_list/views/suggestion_chip_container_view.h
+++ b/ash/app_list/views/suggestion_chip_container_view.h
@@ -29,6 +29,7 @@
   SearchResultSuggestionChipView* GetResultViewAt(size_t index) override;
   int DoUpdate() override;
   const char* GetClassName() const override;
+  int ScheduleResultAnimations(int preceeding_results) override;
 
   // views::View:
   void Layout() override;
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 0792adb..33da35e 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -406,6 +406,9 @@
       <message name="IDS_ASH_STATUS_TRAY_CAST_CAST_DESKTOP" desc="The label used in the tray popup to tell the user we are casting the desktop.">
         Casting screen to <ph name="RECEIVER_NAME">$1<ex>Living Room</ex></ph>
       </message>
+      <message name="IDS_ASH_STATUS_TRAY_CAST_ACCESS_CAST_ADD" desc="Menu item label that, when clicked on, opens the access code casting dialog. MANAGER can be a domain or an email address.">
+        Cast to a <ph name="MANAGER">$1<ex>yourdomain.com</ex></ph> device
+      </message>
       <message name="IDS_ASH_STATUS_TRAY_DARK_THEME" desc="The label used as the header in the dark theme popup. [CHAR_LIMIT=19">
         Dark theme
       </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CAST_ACCESS_CAST_ADD.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CAST_ACCESS_CAST_ADD.png.sha1
new file mode 100644
index 0000000..9f522e5
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CAST_ACCESS_CAST_ADD.png.sha1
@@ -0,0 +1 @@
+0f8f03c66e4fc82a1a7a449861bff0b2b86eb148
\ No newline at end of file
diff --git a/ash/assistant/assistant_controller_impl_unittest.cc b/ash/assistant/assistant_controller_impl_unittest.cc
index 109bed5f..9fac96b 100644
--- a/ash/assistant/assistant_controller_impl_unittest.cc
+++ b/ash/assistant/assistant_controller_impl_unittest.cc
@@ -11,7 +11,6 @@
 #include "ash/assistant/test/assistant_ash_test_base.h"
 #include "ash/assistant/test/test_assistant_service.h"
 #include "ash/assistant/util/deep_link_util.h"
-#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/assistant/controller/assistant_controller_observer.h"
 #include "ash/public/cpp/test/test_new_window_delegate.h"
@@ -20,6 +19,7 @@
 #include "ash/style/ash_color_provider.h"
 #include "base/scoped_observation.h"
 #include "base/test/scoped_feature_list.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "chromeos/services/assistant/public/cpp/assistant_service.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -245,7 +245,7 @@
 // flag is off. SettingsController won't set options if dark mode bit is not
 // set.
 TEST_F(AssistantControllerImplTest, ColorModeIsSetWhenAssistantIsReadyFlagOff) {
-  ASSERT_FALSE(features::IsDarkLightModeEnabled());
+  ASSERT_FALSE(chromeos::features::IsDarkLightModeEnabled());
 
   controller()->SetAssistant(test_assistant_service());
 
@@ -255,9 +255,9 @@
 
 TEST_F(AssistantControllerImplTest, ColorModeIsUpdated) {
   base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(features::kDarkLightMode);
+  feature_list.InitAndEnableFeature(chromeos::features::kDarkLightMode);
 
-  ASSERT_TRUE(features::IsDarkLightModeEnabled());
+  ASSERT_TRUE(chromeos::features::IsDarkLightModeEnabled());
 
   // AshColorProvider::IsDarkModeEnabled reports it's in dark mode if active
   // pref service is not set.
diff --git a/ash/assistant/ui/DEPS b/ash/assistant/ui/DEPS
index 1bc71ee..17f1d5c 100644
--- a/ash/assistant/ui/DEPS
+++ b/ash/assistant/ui/DEPS
@@ -15,6 +15,7 @@
   "+cc/base",
   "+cc/paint",
   "+chromeos/assistant",
+  "+chromeos/constants",
   "+chromeos/services/assistant/public",
   "+chromeos/services/libassistant/public/cpp",
   "+mojo/public/cpp",
diff --git a/ash/assistant/ui/colors/assistant_colors_util_unittest.cc b/ash/assistant/ui/colors/assistant_colors_util_unittest.cc
index 7159e92..e7f4858 100644
--- a/ash/assistant/ui/colors/assistant_colors_util_unittest.cc
+++ b/ash/assistant/ui/colors/assistant_colors_util_unittest.cc
@@ -5,13 +5,13 @@
 #include "ash/assistant/ui/colors/assistant_colors_util.h"
 
 #include "ash/assistant/ui/colors/assistant_colors.h"
-#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/test/ash_test_base.h"
 #include "base/test/scoped_feature_list.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkColor.h"
 
@@ -21,7 +21,8 @@
 using AssistantColorsUtilUnittest = AshTestBase;
 
 TEST_F(AssistantColorsUtilUnittest, AssistantColor) {
-  base::test::ScopedFeatureList scoped_feature_list(features::kDarkLightMode);
+  base::test::ScopedFeatureList scoped_feature_list(
+      chromeos::features::kDarkLightMode);
   AshColorProvider::Get()->OnActiveUserPrefServiceChanged(
       Shell::Get()->session_controller()->GetActivePrefService());
 
@@ -42,7 +43,7 @@
 }
 
 TEST_F(AssistantColorsUtilUnittest, AssistantColorFlagOff) {
-  ASSERT_FALSE(features::IsDarkLightModeEnabled());
+  ASSERT_FALSE(chromeos::features::IsDarkLightModeEnabled());
 
   EXPECT_EQ(
       ResolveAssistantColor(assistant_colors::ColorName::kBgAssistantPlate),
@@ -55,7 +56,7 @@
 // ResolveAssistantColor falls back to assistant_colors::ResolveColor with dark
 // mode off if the color is not defined in the cc file map and the flag is off.
 TEST_F(AssistantColorsUtilUnittest, AssistantColorFlagOffFallback) {
-  ASSERT_FALSE(features::IsDarkLightModeEnabled());
+  ASSERT_FALSE(chromeos::features::IsDarkLightModeEnabled());
 
   EXPECT_EQ(ResolveAssistantColor(assistant_colors::ColorName::kGoogleBlue100),
             assistant_colors::ResolveColor(
diff --git a/ash/assistant/ui/main_stage/assistant_onboarding_suggestion_view_unittest.cc b/ash/assistant/ui/main_stage/assistant_onboarding_suggestion_view_unittest.cc
index 9d5fabc..a9b6579 100644
--- a/ash/assistant/ui/main_stage/assistant_onboarding_suggestion_view_unittest.cc
+++ b/ash/assistant/ui/main_stage/assistant_onboarding_suggestion_view_unittest.cc
@@ -12,6 +12,7 @@
 #include "ash/style/ash_color_provider.h"
 #include "ash/test/ash_test_base.h"
 #include "base/test/scoped_feature_list.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "chromeos/services/libassistant/public/cpp/assistant_suggestion.h"
 #include "components/prefs/pref_service.h"
 #include "ui/gfx/color_palette.h"
@@ -42,7 +43,8 @@
 using AssistantOnboardingSuggestionViewTest = AshTestBase;
 
 TEST_F(AssistantOnboardingSuggestionViewTest, DarkAndLightTheme) {
-  base::test::ScopedFeatureList scoped_feature_list(features::kDarkLightMode);
+  base::test::ScopedFeatureList scoped_feature_list(
+      chromeos::features::kDarkLightMode);
   AshColorProvider::Get()->OnActiveUserPrefServiceChanged(
       Shell::Get()->session_controller()->GetActivePrefService());
   ASSERT_TRUE(features::IsDarkLightModeEnabled());
diff --git a/ash/assistant/ui/main_stage/assistant_onboarding_view_unittest.cc b/ash/assistant/ui/main_stage/assistant_onboarding_view_unittest.cc
index f92c721..e6eee08 100644
--- a/ash/assistant/ui/main_stage/assistant_onboarding_view_unittest.cc
+++ b/ash/assistant/ui/main_stage/assistant_onboarding_view_unittest.cc
@@ -17,7 +17,6 @@
 #include "ash/assistant/ui/main_stage/assistant_onboarding_suggestion_view.h"
 #include "ash/assistant/ui/test_support/mock_assistant_view_delegate.h"
 #include "ash/assistant/util/test_support/macros.h"
-#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/assistant/controller/assistant_suggestions_controller.h"
 #include "ash/public/cpp/assistant/controller/assistant_ui_controller.h"
@@ -31,6 +30,7 @@
 #include "base/test/icu_test_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/unguessable_token.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "chromeos/services/assistant/public/cpp/assistant_service.h"
 #include "chromeos/ui/vector_icons/vector_icons.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -468,10 +468,11 @@
 }
 
 TEST_F(AssistantOnboardingViewTest, DarkAndLightTheme) {
-  base::test::ScopedFeatureList scoped_feature_list(features::kDarkLightMode);
+  base::test::ScopedFeatureList scoped_feature_list(
+      chromeos::features::kDarkLightMode);
   AshColorProvider::Get()->OnActiveUserPrefServiceChanged(
       Shell::Get()->session_controller()->GetActivePrefService());
-  ASSERT_TRUE(features::IsDarkLightModeEnabled());
+  ASSERT_TRUE(chromeos::features::IsDarkLightModeEnabled());
   ASSERT_FALSE(ColorProvider::Get()->IsDarkModeEnabled());
 
   ShowAssistantUi();
@@ -496,7 +497,7 @@
 }
 
 TEST_F(AssistantOnboardingViewTest, DarkAndLightModeFlagOff) {
-  ASSERT_FALSE(features::IsDarkLightModeEnabled());
+  ASSERT_FALSE(chromeos::features::IsDarkLightModeEnabled());
 
   ShowAssistantUi();
 
diff --git a/ash/assistant/ui/main_stage/assistant_query_view_unittest.cc b/ash/assistant/ui/main_stage/assistant_query_view_unittest.cc
index 58bb6dbf..446ba2ae 100644
--- a/ash/assistant/ui/main_stage/assistant_query_view_unittest.cc
+++ b/ash/assistant/ui/main_stage/assistant_query_view_unittest.cc
@@ -15,6 +15,7 @@
 #include "ash/style/ash_color_provider.h"
 #include "base/feature_list.h"
 #include "base/test/scoped_feature_list.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/chromeos/styles/cros_styles.h"
 #include "ui/compositor/layer.h"
@@ -28,7 +29,8 @@
 using AssistantQueryViewUnittest = AssistantAshTestBase;
 
 TEST_F(AssistantQueryViewUnittest, ThemeDarkLightMode) {
-  base::test::ScopedFeatureList scoped_feature_list(features::kDarkLightMode);
+  base::test::ScopedFeatureList scoped_feature_list(
+      chromeos::features::kDarkLightMode);
   AshColorProvider::Get()->OnActiveUserPrefServiceChanged(
       Shell::Get()->session_controller()->GetActivePrefService());
 
@@ -69,7 +71,7 @@
 }
 
 TEST_F(AssistantQueryViewUnittest, Theme) {
-  ASSERT_FALSE(features::IsDarkLightModeEnabled());
+  ASSERT_FALSE(chromeos::features::IsDarkLightModeEnabled());
 
   ShowAssistantUi();
 
diff --git a/ash/assistant/ui/main_stage/assistant_text_element_view_unittest.cc b/ash/assistant/ui/main_stage/assistant_text_element_view_unittest.cc
index def24dab..5b9a61f 100644
--- a/ash/assistant/ui/main_stage/assistant_text_element_view_unittest.cc
+++ b/ash/assistant/ui/main_stage/assistant_text_element_view_unittest.cc
@@ -5,7 +5,6 @@
 #include "ash/assistant/ui/main_stage/assistant_text_element_view.h"
 
 #include "ash/assistant/ui/assistant_ui_constants.h"
-#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/style/color_provider.h"
 #include "ash/session/session_controller_impl.h"
@@ -13,6 +12,7 @@
 #include "ash/style/ash_color_provider.h"
 #include "ash/test/ash_test_base.h"
 #include "base/test/scoped_feature_list.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/widget/widget.h"
 
@@ -25,10 +25,11 @@
 using AssistantTextElementViewTest = AshTestBase;
 
 TEST_F(AssistantTextElementViewTest, DarkAndLightTheme) {
-  base::test::ScopedFeatureList scoped_feature_list(features::kDarkLightMode);
+  base::test::ScopedFeatureList scoped_feature_list(
+      chromeos::features::kDarkLightMode);
   AshColorProvider::Get()->OnActiveUserPrefServiceChanged(
       Shell::Get()->session_controller()->GetActivePrefService());
-  ASSERT_TRUE(features::IsDarkLightModeEnabled());
+  ASSERT_TRUE(chromeos::features::IsDarkLightModeEnabled());
   ASSERT_FALSE(ColorProvider::Get()->IsDarkModeEnabled());
 
   std::unique_ptr<views::Widget> widget = CreateFramelessTestWidget();
@@ -52,7 +53,7 @@
 }
 
 TEST_F(AssistantTextElementViewTest, DarkAndLightModeFlagOff) {
-  ASSERT_FALSE(features::IsDarkLightModeEnabled());
+  ASSERT_FALSE(chromeos::features::IsDarkLightModeEnabled());
 
   std::unique_ptr<views::Widget> widget = CreateFramelessTestWidget();
   AssistantTextElementView* text_element_view = widget->SetContentsView(
diff --git a/ash/assistant/ui/main_stage/assistant_zero_state_view_unittest.cc b/ash/assistant/ui/main_stage/assistant_zero_state_view_unittest.cc
index 24a7c31..077167d2 100644
--- a/ash/assistant/ui/main_stage/assistant_zero_state_view_unittest.cc
+++ b/ash/assistant/ui/main_stage/assistant_zero_state_view_unittest.cc
@@ -8,12 +8,12 @@
 #include "ash/assistant/ui/assistant_ui_constants.h"
 #include "ash/assistant/ui/assistant_view_ids.h"
 #include "ash/assistant/ui/colors/assistant_colors.h"
-#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/style/ash_color_provider.h"
 #include "base/test/scoped_feature_list.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/chromeos/styles/cros_styles.h"
 #include "ui/views/controls/label.h"
@@ -24,7 +24,7 @@
 using AssistantZeroStateViewUnittest = AssistantAshTestBase;
 
 TEST_F(AssistantZeroStateViewUnittest, Theme) {
-  ASSERT_FALSE(features::IsDarkLightModeEnabled());
+  ASSERT_FALSE(chromeos::features::IsDarkLightModeEnabled());
 
   ShowAssistantUi();
 
@@ -36,7 +36,8 @@
 }
 
 TEST_F(AssistantZeroStateViewUnittest, ThemeDarkLightMode) {
-  base::test::ScopedFeatureList scoped_feature_list(features::kDarkLightMode);
+  base::test::ScopedFeatureList scoped_feature_list(
+      chromeos::features::kDarkLightMode);
   AshColorProvider::Get()->OnActiveUserPrefServiceChanged(
       Shell::Get()->session_controller()->GetActivePrefService());
 
diff --git a/ash/assistant/ui/main_stage/suggestion_chip_view_unittest.cc b/ash/assistant/ui/main_stage/suggestion_chip_view_unittest.cc
index a9b5480..6b6ce70f 100644
--- a/ash/assistant/ui/main_stage/suggestion_chip_view_unittest.cc
+++ b/ash/assistant/ui/main_stage/suggestion_chip_view_unittest.cc
@@ -8,7 +8,6 @@
 #include "ash/assistant/ui/assistant_view_ids.h"
 #include "ash/assistant/ui/test_support/mock_assistant_view_delegate.h"
 #include "ash/assistant/util/test_support/macros.h"
-#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/style/color_provider.h"
 #include "ash/session/session_controller_impl.h"
@@ -18,6 +17,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "cc/paint/paint_flags.h"
 #include "cc/test/pixel_comparator.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "chromeos/services/assistant/public/cpp/assistant_service.h"
 #include "chromeos/ui/vector_icons/vector_icons.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -124,10 +124,11 @@
 }
 
 TEST_F(SuggestionChipViewTest, DarkAndLightTheme) {
-  base::test::ScopedFeatureList scoped_feature_list(features::kDarkLightMode);
+  base::test::ScopedFeatureList scoped_feature_list(
+      chromeos::features::kDarkLightMode);
   AshColorProvider::Get()->OnActiveUserPrefServiceChanged(
       Shell::Get()->session_controller()->GetActivePrefService());
-  ASSERT_TRUE(features::IsDarkLightModeEnabled());
+  ASSERT_TRUE(chromeos::features::IsDarkLightModeEnabled());
   ASSERT_FALSE(ColorProvider::Get()->IsDarkModeEnabled());
 
   auto widget = CreateFramelessTestWidget();
@@ -199,7 +200,7 @@
 }
 
 TEST_F(SuggestionChipViewTest, DarkAndLightModeFlagOff) {
-  ASSERT_FALSE(features::IsDarkLightModeEnabled());
+  ASSERT_FALSE(chromeos::features::IsDarkLightModeEnabled());
 
   auto widget = CreateFramelessTestWidget();
   auto* suggestion_chip_view =
diff --git a/ash/assistant/ui/main_stage/ui_element_container_view_unittest.cc b/ash/assistant/ui/main_stage/ui_element_container_view_unittest.cc
index 55d7c4ab..ceb589b5 100644
--- a/ash/assistant/ui/main_stage/ui_element_container_view_unittest.cc
+++ b/ash/assistant/ui/main_stage/ui_element_container_view_unittest.cc
@@ -7,7 +7,6 @@
 #include "ash/assistant/assistant_interaction_controller_impl.h"
 #include "ash/assistant/test/assistant_ash_test_base.h"
 #include "ash/assistant/ui/assistant_view_ids.h"
-#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/assistant/controller/assistant_interaction_controller.h"
 #include "ash/session/session_controller_impl.h"
@@ -15,6 +14,7 @@
 #include "ash/style/ash_color_provider.h"
 #include "base/test/scoped_feature_list.h"
 #include "cc/base/math_util.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "chromeos/services/libassistant/public/cpp/assistant_interaction_metadata.h"
 #include "ui/compositor/layer.h"
 #include "ui/gfx/color_palette.h"
@@ -32,10 +32,11 @@
 using UiElementContainerViewTest = AssistantAshTestBase;
 
 TEST_F(UiElementContainerViewTest, DarkAndLightTheme) {
-  base::test::ScopedFeatureList scoped_feature_list(features::kDarkLightMode);
+  base::test::ScopedFeatureList scoped_feature_list(
+      chromeos::features::kDarkLightMode);
   AshColorProvider::Get()->OnActiveUserPrefServiceChanged(
       Shell::Get()->session_controller()->GetActivePrefService());
-  ASSERT_TRUE(features::IsDarkLightModeEnabled());
+  ASSERT_TRUE(chromeos::features::IsDarkLightModeEnabled());
   ASSERT_FALSE(AshColorProvider::Get()->IsDarkModeEnabled());
 
   ShowAssistantUi();
@@ -58,7 +59,7 @@
 }
 
 TEST_F(UiElementContainerViewTest, DarkAndLightModeFlagOff) {
-  ASSERT_FALSE(features::IsDarkLightModeEnabled());
+  ASSERT_FALSE(chromeos::features::IsDarkLightModeEnabled());
 
   ShowAssistantUi();
 
diff --git a/ash/components/arc/mojom/screen_capture.mojom b/ash/components/arc/mojom/screen_capture.mojom
index 7eca026..83901786 100644
--- a/ash/components/arc/mojom/screen_capture.mojom
+++ b/ash/components/arc/mojom/screen_capture.mojom
@@ -10,12 +10,13 @@
 // The original version of this file lives in the Chromium repository at:
 // src/components/arc/mojom/screen_capture.mojom
 
-// Next MinVersion: 2
+// Next MinVersion: 3
 
 module arc.mojom;
 
 import "ash/components/arc/mojom/gfx.mojom";
 import "ash/components/arc/mojom/video_common.mojom";
+import "ui/gfx/mojom/buffer_types.mojom";
 
 // Implemented by Chrome in order to allow requesting of permissions to perform
 // desktop capture as well as creating a session for it.
@@ -55,7 +56,22 @@
   // graphics_buffer should be a handle to the buffer which corresponds to the
   //     surface being rendered to
   // stride is the stride in pixels for each row of the buffer
-  SetOutputBuffer@0(handle graphics_buffer, uint32 stride) => ();
+  // DEPRECATED: Please use SetOutputBuffer@1 instead.
+  SetOutputBufferDeprecated@0(handle graphics_buffer, uint32 stride) => ();
+
+  // Called to set the graphics buffer that the screen capture should be
+  // rendered to. This call does not return until the rendering is complete to
+  // the passed in buffer.
+  // graphics_buffer should be a handle to the buffer which corresponds to the
+  //     surface being rendered to
+  // buffer_format gives the general format of the buffer
+  // buffer_format_modifier is an additional buffer format modifier value
+  //     (See DRM_FORMAT_MOD_* in drm_fourcc.h)
+  // stride is the stride in pixels for each row of the buffer
+  [MinVersion=2] SetOutputBuffer@1(handle graphics_buffer,
+                                   gfx.mojom.BufferFormat buffer_format,
+                                   uint64 buffer_format_modifier,
+                                   uint32 stride) => ();
 };
 
 // Implemented by Android.
diff --git a/ash/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc b/ash/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
index e0f7fbe..1e74524 100644
--- a/ash/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
+++ b/ash/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
@@ -26,6 +26,7 @@
 #include "media/gpu/chromeos/video_decoder_pipeline.h"
 #include "media/gpu/gpu_video_decode_accelerator_factory.h"
 #include "media/gpu/macros.h"
+#include "media/media_buildflags.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 
 // Make sure arc::mojom::VideoDecodeAccelerator::Result and
@@ -78,6 +79,16 @@
   }
 }
 
+media::VideoDecodeAccelerator::Config CreateVdaConfig(
+    media::VideoCodecProfile profile,
+    bool uses_vd) {
+  media::VideoDecodeAccelerator::Config vda_config(profile);
+  vda_config.output_mode =
+      media::VideoDecodeAccelerator::Config::OutputMode::IMPORT;
+  vda_config.is_deferred_initialization_allowed = uses_vd;
+  return vda_config;
+}
+
 }  // namespace
 
 namespace arc {
@@ -112,7 +123,7 @@
 GpuArcVideoDecodeAccelerator::~GpuArcVideoDecodeAccelerator() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   weak_ptr_factory_for_querying_protected_input_buffers_.InvalidateWeakPtrs();
-  weak_ptr_factory_for_querying_protected_output_buffers_.InvalidateWeakPtrs();
+  weak_ptr_factory_.InvalidateWeakPtrs();
 
   if (vda_) {
     // Destroy |vda_| now in case it needs to use *|this| during tear-down.
@@ -312,13 +323,15 @@
           mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE);
     return;
   }
-  // When pending_reset_callback_ isn't null, GAVDA is awaiting a preceding
+  // When |pending_reset_callback_| isn't null, GAVDA is awaiting a preceding
   // Reset() to be finished. Any requests are pended.
-  // There is no need to take pending_flush_callbacks into account.
+  // When |pending_init_callback_| isn't null, GAVDA is awaiting a re-init to
+  // move the decoder into secure mode.
+  // There is no need to take |pending_flush_callbacks_| into account.
   // VDA::Reset() can be called while VDA::Flush() are being executed.
   // VDA::Flush()es can be called regardless of whether or not there is a
   // preceding VDA::Flush().
-  if (pending_reset_callback_) {
+  if (pending_reset_callback_ || pending_init_callback_) {
     pending_requests_.emplace(std::move(request));
     return;
   }
@@ -358,14 +371,21 @@
         mojom::VideoDecodeAccelerator::Result::INSUFFICIENT_RESOURCES);
   }
 
-  media::VideoDecodeAccelerator::Config vda_config(config->profile);
+  profile_ = config->profile;
 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
-  vda_config.output_mode =
-      media::VideoDecodeAccelerator::Config::OutputMode::IMPORT;
-
-  if (base::FeatureList::IsEnabled(arc::kVideoDecoder)) {
+  const bool use_vd = base::FeatureList::IsEnabled(arc::kVideoDecoder);
+#if BUILDFLAG(USE_ARC_PROTECTED_MEDIA)
+  if (!use_vd) {
+    LOG(ERROR) << "Unsupported path: builds with USE_ARC_PROTECTED_MEDIA must "
+                  "use the VideoDecoder";
+    return OnInitializeDone(
+        mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE);
+  }
+#endif  // BUILDFLAG(USE_ARC_PROTECTED_MEDIA)
+  media::VideoDecodeAccelerator::Config vda_config =
+      CreateVdaConfig(profile_, use_vd);
+  if (use_vd) {
     VLOGF(2) << "Using VideoDecoder-backed VdVideoDecodeAccelerator.";
-    vda_config.is_deferred_initialization_allowed = true;
     vda_ = media::VdVideoDecodeAccelerator::Create(
         base::BindRepeating(&media::VideoDecoderPipeline::Create), this,
         vda_config, base::SequencedTaskRunnerHandle::Get());
@@ -376,7 +396,7 @@
     vda_ = vda_factory->CreateVDA(this, vda_config, gpu_workarounds_,
                                   gpu_preferences_);
   }
-#endif
+#endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
 
   if (!vda_) {
     VLOGF(1) << "Failed to create VDA.";
@@ -412,7 +432,7 @@
 
 void GpuArcVideoDecodeAccelerator::OnInitializeDone(
     mojom::VideoDecodeAccelerator::Result result) {
-  DVLOGF(4);
+  DVLOGF(4) << " result: " << result;
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   if (result != mojom::VideoDecodeAccelerator::Result::SUCCESS)
@@ -422,6 +442,16 @@
   UMA_HISTOGRAM_ENUMERATION(
       "Media.GpuArcVideoDecodeAccelerator.InitializeResult", result);
   std::move(pending_init_callback_).Run(result);
+  RunPendingRequests();
+}
+
+void GpuArcVideoDecodeAccelerator::OnReinitializeDone(
+    mojom::VideoDecodeAccelerator::Result result) {
+  DVLOGF(4) << " result: " << result;
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (result != mojom::VideoDecodeAccelerator::Result::SUCCESS)
+    client_->NotifyError(result);
 }
 
 void GpuArcVideoDecodeAccelerator::Decode(
@@ -511,6 +541,24 @@
                          weak_ptr_factory_for_querying_protected_input_buffers_
                              .GetWeakPtr()));
     }
+#if BUILDFLAG(USE_ARC_PROTECTED_MEDIA)
+    if (*secure_mode_) {
+      VLOGF(2) << "Reinitializing decoder for secure mode";
+      media::VideoDecodeAccelerator::Config vda_config =
+          CreateVdaConfig(profile_, true);
+      // TODO(jkardatzke): Properly set the encryption scheme when we
+      // implement this for Intel. For AMD it doesn't matter since it uses
+      // transcryption.
+      vda_config.encryption_scheme = media::EncryptionScheme::kCenc;
+      // Set a |pending_init_callback_| to force queueing up any incoming decode
+      // requests.
+      DCHECK(!pending_init_callback_);
+      pending_init_callback_ =
+          base::BindOnce(&GpuArcVideoDecodeAccelerator::OnReinitializeDone,
+                         weak_ptr_factory_.GetWeakPtr());
+      vda_->Initialize(vda_config, this);
+    }
+#endif  // BUILDFLAG(USE_ARC_PROTECTED_MEDIA)
   }
 
   if (!*secure_mode_) {
@@ -689,9 +737,7 @@
         std::move(handle_fd),
         base::BindOnce(
             &GpuArcVideoDecodeAccelerator::ContinueImportBufferForPicture,
-            weak_ptr_factory_for_querying_protected_output_buffers_
-                .GetWeakPtr(),
-            picture_buffer_id, pixel_format));
+            weak_ptr_factory_.GetWeakPtr(), picture_buffer_id, pixel_format));
   } else {
     std::vector<base::ScopedFD> handle_fds =
         DuplicateFD(std::move(handle_fd), planes.size());
diff --git a/ash/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h b/ash/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h
index 59cfaea..abc2de2 100644
--- a/ash/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h
+++ b/ash/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h
@@ -92,6 +92,8 @@
                 "The type of PendingCallback must match ResetCallback");
   static_assert(std::is_same<FlushCallback, PendingCallback>::value,
                 "The type of PendingCallback must match FlushCallback");
+  static_assert(std::is_same<InitializeCallback, PendingCallback>::value,
+                "The type of PendingCallback must match InitializeCallback");
   using PendingRequest =
       base::OnceCallback<void(PendingCallback, media::VideoDecodeAccelerator*)>;
 
@@ -100,6 +102,8 @@
   void InitializeTask(mojom::VideoDecodeAcceleratorConfigPtr config);
   // Called when initialization is done.
   void OnInitializeDone(mojom::VideoDecodeAccelerator::Result result);
+  // Proxy callback for re-initialize in encrypted mode in the case of error.
+  void OnReinitializeDone(mojom::VideoDecodeAccelerator::Result result);
 
   // Called after getting the input shared memory region from the
   // |protected_buffer_manager_|, if required. Otherwise, Decode() calls this
@@ -127,11 +131,12 @@
   void RunPendingRequests();
 
   // When |pending_reset_callback_| isn't null, GAVDA is awaiting a preceding
-  // Reset() to be finished, and |request| is pended by queueing
-  // in |pending_requests_|. Otherwise, the requested VDA operation is executed.
-  // In the case of Flush request, the callback is queued to
-  // |pending_flush_callbacks_|. In the case of Reset request,
-  // the callback is set |pending_reset_callback_|.
+  // Reset() to be finished. When |pending_init_callback_| isn't null, GAVDA is
+  // awaiting a re-init to move the decoder into secure mode. In both cases
+  // |request| is pended by queueing in |pending_requests_|. Otherwise, the
+  // requested VDA operation is executed. In the case of Flush request, the
+  // callback is queued to |pending_flush_callbacks_|. In the case of Reset
+  // request, the callback is set |pending_reset_callback_|.
   void ExecuteRequest(std::pair<PendingRequest, PendingCallback> request);
 
   // Requested VDA methods are executed in these functions.
@@ -172,12 +177,15 @@
   // The variables for managing callbacks.
   // VDA::Decode(), VDA::Flush() and VDA::Reset() should not be posted to VDA
   // while the previous Reset() hasn't been finished yet (i.e. before
-  // NotifyResetDone() is invoked).
-  // Those requests will be queued in |pending_requests_| in a FIFO manner,
-  // and will be executed once all the preceding Reset() have been finished.
+  // NotifyResetDone() is invoked). Same thing goes for when we are
+  // re-initializing to enter secure mode. Those requests will be queued in
+  // |pending_requests_| in a FIFO manner, and will be executed once all the
+  // preceding Reset() or Initialize() have been finished.
   // |pending_flush_callbacks_| stores all the callbacks corresponding to
   // currently executing Flush()es in VDA. |pending_reset_callback_| is a
   // callback of the currently executing Reset() in VDA.
+  // |pending_init_callback_| is a callback of the currently executing Create()
+  // or re-execution of Initialize() for the decoder.
   // If |pending_flush_callbacks_| is not empty in NotifyResetDone(),
   // as Flush()es may be cancelled by Reset() in VDA, they are called with
   // CANCELLED.
@@ -196,6 +204,7 @@
 
   gfx::Size coded_size_;
   gfx::Size pending_coded_size_;
+  media::VideoCodecProfile profile_ = media::VIDEO_CODEC_PROFILE_UNKNOWN;
 
   scoped_refptr<DecoderProtectedBufferManager> protected_buffer_manager_;
 
@@ -222,10 +231,11 @@
 
   THREAD_CHECKER(thread_checker_);
 
+  // The one for input buffers lives until ResetRequest().
   base::WeakPtrFactory<GpuArcVideoDecodeAccelerator>
       weak_ptr_factory_for_querying_protected_input_buffers_{this};
-  base::WeakPtrFactory<GpuArcVideoDecodeAccelerator>
-      weak_ptr_factory_for_querying_protected_output_buffers_{this};
+  // This one lives for the lifetime of the object.
+  base::WeakPtrFactory<GpuArcVideoDecodeAccelerator> weak_ptr_factory_{this};
 };
 
 }  // namespace arc
diff --git a/ash/components/arc/video_accelerator/protected_buffer_manager.cc b/ash/components/arc/video_accelerator/protected_buffer_manager.cc
index 61822058..b9271793 100644
--- a/ash/components/arc/video_accelerator/protected_buffer_manager.cc
+++ b/ash/components/arc/video_accelerator/protected_buffer_manager.cc
@@ -16,6 +16,7 @@
 #include "base/system/sys_info.h"
 #include "base/threading/thread_checker.h"
 #include "media/gpu/macros.h"
+#include "media/media_buildflags.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/ozone/public/surface_factory_ozone.h"
@@ -162,7 +163,11 @@
   ui::SurfaceFactoryOzone* factory = platform->GetSurfaceFactoryOzone();
   protected_pixmap->native_pixmap_ = factory->CreateNativePixmap(
       gfx::kNullAcceleratedWidget, VK_NULL_HANDLE, size, format,
+#if BUILDFLAG(USE_ARC_PROTECTED_MEDIA)
+      gfx::BufferUsage::PROTECTED_SCANOUT_VDA_WRITE);
+#else
       gfx::BufferUsage::SCANOUT_VDA_WRITE);
+#endif  // BUILDFLAG(USE_ARC_PROTECTED_MEDIA)
 
   if (!protected_pixmap->native_pixmap_) {
     VLOGF(1) << "Failed allocating a native pixmap";
diff --git a/ash/components/phonehub/camera_roll_manager_impl.cc b/ash/components/phonehub/camera_roll_manager_impl.cc
index 3bf3188..c5de006 100644
--- a/ash/components/phonehub/camera_roll_manager_impl.cc
+++ b/ash/components/phonehub/camera_roll_manager_impl.cc
@@ -159,8 +159,7 @@
     proto::PhoneStatusSnapshot phone_status_snapshot) {
   UpdateCameraRollAccessStateAndNotifyIfNeeded(
       phone_status_snapshot.properties().camera_roll_access_state());
-  if (!is_android_feature_enabled_ || !is_android_storage_granted_ ||
-      !IsCameraRollSettingEnabled()) {
+  if (!is_android_storage_granted_ || !IsCameraRollSettingEnabled()) {
     ClearCurrentItems();
     CancelPendingThumbnailRequests();
     resetViewRefreshingFlagIfNeeded();
@@ -174,8 +173,7 @@
     proto::PhoneStatusUpdate phone_status_update) {
   UpdateCameraRollAccessStateAndNotifyIfNeeded(
       phone_status_update.properties().camera_roll_access_state());
-  if (!is_android_feature_enabled_ || !is_android_storage_granted_ ||
-      !IsCameraRollSettingEnabled()) {
+  if (!is_android_storage_granted_ || !IsCameraRollSettingEnabled()) {
     ClearCurrentItems();
     CancelPendingThumbnailRequests();
     resetViewRefreshingFlagIfNeeded();
@@ -261,12 +259,8 @@
 
 void CameraRollManagerImpl::UpdateCameraRollAccessStateAndNotifyIfNeeded(
     const proto::CameraRollAccessState& access_state) {
-  bool updated_feature_enabled = access_state.feature_enabled();
   bool updated_storage_granted = access_state.storage_permission_granted();
-
-  if (is_android_feature_enabled_ != updated_feature_enabled ||
-      is_android_storage_granted_ != updated_storage_granted) {
-    is_android_feature_enabled_ = updated_feature_enabled;
+  if (is_android_storage_granted_ != updated_storage_granted) {
     is_android_storage_granted_ = updated_storage_granted;
 
     util::LogCameraRollAndroidHasStorageAccessPermission(
@@ -282,11 +276,7 @@
 }
 
 void CameraRollManagerImpl::ComputeAndUpdateUiState() {
-  if (!is_android_feature_enabled_) {
-    ui_state_ = CameraRollUiState::SHOULD_HIDE;
-    NotifyCameraRollViewUiStateUpdated();
-    return;
-  } else if (!is_android_storage_granted_) {
+  if (!is_android_storage_granted_) {
     ui_state_ = CameraRollUiState::NO_STORAGE_PERMISSION;
     NotifyCameraRollViewUiStateUpdated();
     return;
diff --git a/ash/components/phonehub/camera_roll_manager_impl_unittest.cc b/ash/components/phonehub/camera_roll_manager_impl_unittest.cc
index 8ec136d..7a52b81 100644
--- a/ash/components/phonehub/camera_roll_manager_impl_unittest.cc
+++ b/ash/components/phonehub/camera_roll_manager_impl_unittest.cc
@@ -360,7 +360,6 @@
   update.set_has_camera_roll_updates(false);
   proto::CameraRollAccessState* access_state =
       update.mutable_properties()->mutable_camera_roll_access_state();
-  access_state->set_feature_enabled(true);
   access_state->set_storage_permission_granted(true);
   fake_message_receiver_.NotifyPhoneStatusUpdateReceived(update);
 
@@ -376,7 +375,6 @@
   update.set_has_camera_roll_updates(true);
   proto::CameraRollAccessState* access_state =
       update.mutable_properties()->mutable_camera_roll_access_state();
-  access_state->set_feature_enabled(true);
   access_state->set_storage_permission_granted(true);
   fake_message_receiver_.NotifyPhoneStatusUpdateReceived(update);
 
@@ -401,7 +399,6 @@
   update.set_has_camera_roll_updates(true);
   proto::CameraRollAccessState* access_state =
       update.mutable_properties()->mutable_camera_roll_access_state();
-  access_state->set_feature_enabled(true);
   access_state->set_storage_permission_granted(true);
   fake_message_receiver_.NotifyPhoneStatusUpdateReceived(update);
 
@@ -423,28 +420,6 @@
 }
 
 TEST_F(CameraRollManagerImplTest,
-       OnPhoneStatusUpdateReceivedWithFeatureDisabled) {
-  proto::FetchCameraRollItemsResponse response;
-  PopulateItemProto(response.add_items(), "key2");
-  PopulateItemProto(response.add_items(), "key1");
-  fake_message_receiver_.NotifyFetchCameraRollItemsResponseReceived(response);
-  CompleteThumbnailDecoding(BatchDecodeResult::kSuccess);
-
-  proto::PhoneStatusUpdate update;
-  proto::CameraRollAccessState* access_state =
-      update.mutable_properties()->mutable_camera_roll_access_state();
-  access_state->set_feature_enabled(false);
-  access_state->set_storage_permission_granted(true);
-  fake_message_receiver_.NotifyPhoneStatusUpdateReceived(update);
-
-  EXPECT_EQ(0UL, GetSentFetchCameraRollItemsRequestCount());
-  EXPECT_EQ(CameraRollManager::CameraRollUiState::SHOULD_HIDE,
-            camera_roll_manager()->ui_state());
-  EXPECT_EQ(3, GetOnCameraRollViewUiStateUpdatedCallCount());
-  EXPECT_EQ(0, GetCurrentItemsCount());
-}
-
-TEST_F(CameraRollManagerImplTest,
        OnPhoneStatusUpdateReceivedWithCameraRollSettingsDisabled) {
   SetCameraRollFeatureState(FeatureState::kDisabledByUser);
   proto::FetchCameraRollItemsResponse response;
@@ -457,7 +432,6 @@
   update.set_has_camera_roll_updates(true);
   proto::CameraRollAccessState* access_state =
       update.mutable_properties()->mutable_camera_roll_access_state();
-  access_state->set_feature_enabled(true);
   access_state->set_storage_permission_granted(true);
   fake_message_receiver_.NotifyPhoneStatusUpdateReceived(update);
 
@@ -479,14 +453,13 @@
   proto::PhoneStatusUpdate update;
   proto::CameraRollAccessState* access_state =
       update.mutable_properties()->mutable_camera_roll_access_state();
-  access_state->set_feature_enabled(true);
   access_state->set_storage_permission_granted(false);
   fake_message_receiver_.NotifyPhoneStatusUpdateReceived(update);
 
   EXPECT_EQ(0UL, GetSentFetchCameraRollItemsRequestCount());
   EXPECT_EQ(CameraRollManager::CameraRollUiState::NO_STORAGE_PERMISSION,
             camera_roll_manager()->ui_state());
-  EXPECT_EQ(3, GetOnCameraRollViewUiStateUpdatedCallCount());
+  EXPECT_EQ(2, GetOnCameraRollViewUiStateUpdatedCallCount());
   EXPECT_EQ(0, GetCurrentItemsCount());
 }
 
@@ -494,7 +467,6 @@
   proto::PhoneStatusSnapshot snapshot;
   proto::CameraRollAccessState* access_state =
       snapshot.mutable_properties()->mutable_camera_roll_access_state();
-  access_state->set_feature_enabled(true);
   access_state->set_storage_permission_granted(true);
   fake_message_receiver_.NotifyPhoneStatusSnapshotReceived(snapshot);
 
@@ -505,28 +477,6 @@
 }
 
 TEST_F(CameraRollManagerImplTest,
-       OnPhoneStatusSnapshotReceivedWithFeatureDisabled) {
-  proto::FetchCameraRollItemsResponse response;
-  PopulateItemProto(response.add_items(), "key2");
-  PopulateItemProto(response.add_items(), "key1");
-  fake_message_receiver_.NotifyFetchCameraRollItemsResponseReceived(response);
-  CompleteThumbnailDecoding(BatchDecodeResult::kSuccess);
-
-  proto::PhoneStatusSnapshot snapshot;
-  proto::CameraRollAccessState* access_state =
-      snapshot.mutable_properties()->mutable_camera_roll_access_state();
-  access_state->set_feature_enabled(false);
-  access_state->set_storage_permission_granted(true);
-  fake_message_receiver_.NotifyPhoneStatusSnapshotReceived(snapshot);
-
-  EXPECT_EQ(0UL, GetSentFetchCameraRollItemsRequestCount());
-  EXPECT_EQ(CameraRollManager::CameraRollUiState::SHOULD_HIDE,
-            camera_roll_manager()->ui_state());
-  EXPECT_EQ(3, GetOnCameraRollViewUiStateUpdatedCallCount());
-  EXPECT_EQ(0, GetCurrentItemsCount());
-}
-
-TEST_F(CameraRollManagerImplTest,
        OnPhoneStatusSnapshotReceivedWithCameraRollSettingDisabled) {
   SetCameraRollFeatureState(FeatureState::kDisabledByUser);
   proto::FetchCameraRollItemsResponse response;
@@ -538,7 +488,6 @@
   proto::PhoneStatusSnapshot snapshot;
   proto::CameraRollAccessState* access_state =
       snapshot.mutable_properties()->mutable_camera_roll_access_state();
-  access_state->set_feature_enabled(true);
   access_state->set_storage_permission_granted(true);
   fake_message_receiver_.NotifyPhoneStatusSnapshotReceived(snapshot);
 
@@ -560,14 +509,13 @@
   proto::PhoneStatusSnapshot snapshot;
   proto::CameraRollAccessState* access_state =
       snapshot.mutable_properties()->mutable_camera_roll_access_state();
-  access_state->set_feature_enabled(true);
   access_state->set_storage_permission_granted(false);
   fake_message_receiver_.NotifyPhoneStatusSnapshotReceived(snapshot);
 
   EXPECT_EQ(0UL, GetSentFetchCameraRollItemsRequestCount());
   EXPECT_EQ(CameraRollManager::CameraRollUiState::NO_STORAGE_PERMISSION,
             camera_roll_manager()->ui_state());
-  EXPECT_EQ(3, GetOnCameraRollViewUiStateUpdatedCallCount());
+  EXPECT_EQ(2, GetOnCameraRollViewUiStateUpdatedCallCount());
   EXPECT_EQ(0, GetCurrentItemsCount());
 }
 
@@ -575,7 +523,6 @@
   proto::PhoneStatusSnapshot snapshot;
   proto::CameraRollAccessState* access_state =
       snapshot.mutable_properties()->mutable_camera_roll_access_state();
-  access_state->set_feature_enabled(true);
   access_state->set_storage_permission_granted(true);
   fake_message_receiver_.NotifyPhoneStatusSnapshotReceived(snapshot);
   proto::FetchCameraRollItemsResponse response;
@@ -598,7 +545,6 @@
   proto::PhoneStatusSnapshot snapshot;
   proto::CameraRollAccessState* access_state =
       snapshot.mutable_properties()->mutable_camera_roll_access_state();
-  access_state->set_feature_enabled(true);
   access_state->set_storage_permission_granted(true);
   fake_message_receiver_.NotifyPhoneStatusSnapshotReceived(snapshot);
 
diff --git a/ash/components/phonehub/proto/phonehub_api.proto b/ash/components/phonehub/proto/phonehub_api.proto
index d1c53da..a01b102 100644
--- a/ash/components/phonehub/proto/phonehub_api.proto
+++ b/ash/components/phonehub/proto/phonehub_api.proto
@@ -122,7 +122,9 @@
 // device
 message CameraRollAccessState {
   // Whether the camera roll feature is enabled
-  bool feature_enabled = 1;
+  // DEPRECATED: this is now checked during CryptAuth enrollment on the Android
+  // device and no longer checked by the Phone Hub CrOS code.
+  bool feature_enabled = 1 [deprecated = true];
   // Whether necessary storage permissions have been granted to access camera
   // roll items
   bool storage_permission_granted = 2;
diff --git a/ash/constants/BUILD.gn b/ash/constants/BUILD.gn
index ebcd13a..bbf5dab 100644
--- a/ash/constants/BUILD.gn
+++ b/ash/constants/BUILD.gn
@@ -26,6 +26,7 @@
   ]
   public_deps = [
     "//base",
+    "//chromeos/constants",
     "//skia",
     "//third_party/abseil-cpp:absl",
   ]
diff --git a/ash/constants/DEPS b/ash/constants/DEPS
index 0eda6c8..6a39252 100644
--- a/ash/constants/DEPS
+++ b/ash/constants/DEPS
@@ -5,6 +5,7 @@
   "+ash/constants",
   "+base",
   "+build",
+  "+chromeos/constants",
   "+third_party/abseil-cpp/absl/types/optional.h",
 ]
 
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 2cf5855..acf18902d 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -10,6 +10,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/system/sys_info.h"
 #include "build/build_config.h"
+#include "chromeos/constants/chromeos_features.h"
 
 namespace ash {
 namespace features {
@@ -369,10 +370,6 @@
 const base::Feature kCryptohomeRecoveryFlow{"CryptohomeRecoveryFlow",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables dark/light mode feature.
-const base::Feature kDarkLightMode{"DarkLightMode",
-                                   base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kDemoModeSWA{"DemoModeSWA",
                                  base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -1446,7 +1443,7 @@
 }
 
 bool IsDarkLightModeEnabled() {
-  return base::FeatureList::IsEnabled(kDarkLightMode);
+  return chromeos::features::IsDarkLightModeEnabled();
 }
 
 bool IsDemoModeSWAEnabled() {
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index b34e082..56d9c8b6 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -152,7 +152,6 @@
 extern const base::Feature kCryptAuthV2Enrollment;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kCryptohomeRecoveryFlow;
-COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kDarkLightMode;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kDemoModeSWA;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kDeskTemplateSync;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kDeviceActiveClient;
diff --git a/ash/constants/ash_pref_names.cc b/ash/constants/ash_pref_names.cc
index c5b4eb3..6cc5ced 100644
--- a/ash/constants/ash_pref_names.cc
+++ b/ash/constants/ash_pref_names.cc
@@ -876,18 +876,32 @@
 // A boolean pref that tracks whether the user has enabled Projector creation
 // flow during onboarding.
 const char kProjectorCreationFlowEnabled[] =
-    "ash.projector.creationFlowEnabled";
+    "ash.projector.creation_flow_enabled";
+
+// A string pref that tracks the language installed for the Projector creation
+// flow.
+const char kProjectorCreationFlowLanguage[] =
+    "ash.projector.creation_flow_language";
+
+// An integer pref counting the number of times the Onboarding flow has been
+// shown to the user inside the Projector Gallery.
+const char kProjectorGalleryOnboardingShowCount[] =
+    "ash.projector.gallery_onboarding_show_count";
+
+// An integer pref counting the number of times the Onboarding flow has been
+// shown to the user inside the Projector Viewer.
+const char kProjectorViewerOnboardingShowCount[] =
+    "ash.projector.viewer_onboarding_show_count";
+
+// A boolean pref that indicates the Projector has been enabled by admin
+// policy.
+const char kProjectorAllowByPolicy[] = "ash.projector.allow_by_policy";
 
 // A boolean pref that indicates whether the migration of Chromad devices to
 // cloud management can be started.
 const char kChromadToCloudMigrationEnabled[] =
     "ash.chromad_to_cloud_migration_enabled";
 
-// A string pref that tracks the language installed for the Projector creation
-// flow.
-const char kProjectorCreationFlowLanguage[] =
-    "ash.projector.creationFlowLanguage";
-
 // List of Drive Folder Shortcuts in the Files app. Used to sync the shortcuts
 // across devices.
 const char kFilesAppFolderShortcuts[] = "ash.filesapp.folder_shortcuts";
@@ -896,16 +910,6 @@
 // the Chrome app to System Web App.
 const char kFilesAppUIPrefsMigrated[] = "ash.filesapp.ui_prefs_migrated";
 
-// An integer pref counting the number of times the Onboarding flow has been
-// shown to the user inside the Projector Gallery.
-const char kProjectorGalleryOnboardingShowCount[] =
-    "ash.projector.galleryOnboardingShowCount";
-
-// An integer pref counting the number of times the Onboarding flow has been
-// shown to the user inside the Projector Viewer.
-const char kProjectorViewerOnboardingShowCount[] =
-    "ash.projector.viewerOnboardingShowCount";
-
 // Boolean value for the DeviceLoginScreenWebUILazyLoading device policy.
 const char kLoginScreenWebUILazyLoading[] =
     "ash.login.LoginScreenWebUILazyLoading";
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h
index bed1800c..81a82ee 100644
--- a/ash/constants/ash_pref_names.h
+++ b/ash/constants/ash_pref_names.h
@@ -402,15 +402,8 @@
 extern const char kProjectorCreationFlowEnabled[];
 
 COMPONENT_EXPORT(ASH_CONSTANTS)
-extern const char kChromadToCloudMigrationEnabled[];
-
-COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kProjectorCreationFlowLanguage[];
 
-COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kFilesAppFolderShortcuts[];
-
-COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kFilesAppUIPrefsMigrated[];
-
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kProjectorGalleryOnboardingShowCount[];
 
@@ -418,6 +411,16 @@
 extern const char kProjectorViewerOnboardingShowCount[];
 
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const char kProjectorAllowByPolicy[];
+
+COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const char kChromadToCloudMigrationEnabled[];
+
+COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kFilesAppFolderShortcuts[];
+
+COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kFilesAppUIPrefsMigrated[];
+
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kLoginScreenWebUILazyLoading[];
 
 }  // namespace prefs
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc b/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
index 5d67876e..ac63776 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
+++ b/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
@@ -25,6 +25,8 @@
 const char kPairingMethodMetric[] = "Bluetooth.ChromeOS.FastPair.PairingMethod";
 const char kRetroactivePairingResultMetric[] =
     "Bluetooth.ChromeOS.FastPair.RetroactivePairing.Result";
+const char kTotalGattConnectionTimeMetric[] =
+    "Bluetooth.ChromeOS.FastPair.TotalGattConnectionTime";
 
 }  // namespace
 
@@ -84,5 +86,10 @@
   base::UmaHistogramBoolean(kRetroactivePairingResultMetric, success);
 }
 
+void RecordTotalGattConnectionTime(base::TimeDelta total_gatt_connection_time) {
+  base::UmaHistogramTimes(kTotalGattConnectionTimeMetric,
+                          total_gatt_connection_time);
+}
+
 }  // namespace quick_pair
 }  // namespace ash
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_metrics.h b/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
index f59ab1c..36ee1b34 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
+++ b/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
@@ -73,6 +73,9 @@
 COMPONENT_EXPORT(QUICK_PAIR_COMMON)
 void RecordRetroactivePairingResult(bool success);
 
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordTotalGattConnectionTime(base::TimeDelta total_gatt_connection_time);
+
 }  // namespace quick_pair
 }  // namespace ash
 
diff --git a/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.cc b/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.cc
index 1711657f..d68674c8 100644
--- a/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.cc
+++ b/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.cc
@@ -5,6 +5,7 @@
 #include "ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.h"
 
 #include "ash/quick_pair/common/constants.h"
+#include "ash/quick_pair/common/fast_pair/fast_pair_metrics.h"
 #include "ash/quick_pair/common/logging.h"
 #include "ash/quick_pair/fast_pair_handshake/fast_pair_data_encryptor.h"
 #include "base/memory/ptr_util.h"
@@ -111,6 +112,7 @@
       base::BindOnce(&FastPairGattServiceClientImpl::OnGattConnection,
                      weak_ptr_factory_.GetWeakPtr()),
       kFastPairBluetoothUuid);
+  gatt_connection_start_time_ = base::TimeTicks::Now();
   gatt_service_discovery_timer_.Start(
       FROM_HERE, kGattOperationTimeout,
       base::BindOnce(&FastPairGattServiceClientImpl::NotifyInitializedError,
@@ -131,6 +133,8 @@
     QP_LOG(VERBOSE)
         << "Successful creation of GATT connection to device at address:["
         << device_address_ << "].";
+    RecordTotalGattConnectionTime(base::TimeTicks::Now() -
+                                  gatt_connection_start_time_);
     gatt_connection_ = std::move(gatt_connection);
   }
 }
diff --git a/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.h b/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.h
index d739a7f..52d32c7 100644
--- a/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.h
+++ b/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.h
@@ -16,6 +16,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
+#include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -175,6 +176,8 @@
   std::string device_address_;
   bool is_initialized_ = false;
 
+  base::TimeTicks gatt_connection_start_time_;
+
   device::BluetoothRemoteGattCharacteristic* key_based_characteristic_ =
       nullptr;
   device::BluetoothRemoteGattCharacteristic* passkey_characteristic_ = nullptr;
diff --git a/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_unittest.cc b/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_unittest.cc
index 46ee165..81842b0 100644
--- a/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_unittest.cc
+++ b/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
@@ -37,6 +38,9 @@
 using ErrorCallback =
     base::OnceCallback<void(device::BluetoothGattService::GattErrorCode)>;
 
+const char kTotalGattConnectionTime[] =
+    "Bluetooth.ChromeOS.FastPair.TotalGattConnectionTime";
+
 constexpr base::TimeDelta kConnectingTestTimeout = base::Seconds(5);
 
 // Below constants are used to construct MockBluetoothDevice for testing.
@@ -490,9 +494,12 @@
 
   void SetWritePasskeyTimeout() { passkey_write_timeout_ = true; }
 
+  base::HistogramTester& histogram_tester() { return histogram_tester_; }
+
  protected:
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  base::HistogramTester histogram_tester_;
 
  private:
   // We need temporary pointers to use for write/ready requests because we
@@ -529,18 +536,29 @@
 };
 
 TEST_F(FastPairGattServiceClientTest, GattServiceDiscoveryTimeout) {
+  histogram_tester().ExpectTotalCount(kTotalGattConnectionTime, 0);
   SuccessfulGattConnectionSetUp();
   FastForwardTimeByConnetingTimeout();
   NotifyGattDiscoveryCompleteForService();
   EXPECT_EQ(GetInitializedCallbackResult(),
             PairFailure::kGattServiceDiscoveryTimeout);
   EXPECT_FALSE(ServiceIsSet());
+  histogram_tester().ExpectTotalCount(kTotalGattConnectionTime, 1);
 }
 
 TEST_F(FastPairGattServiceClientTest, FailedGattConnection) {
+  histogram_tester().ExpectTotalCount(kTotalGattConnectionTime, 0);
   FailedGattConnectionSetUp();
   EXPECT_EQ(GetInitializedCallbackResult(), PairFailure::kCreateGattConnection);
   EXPECT_FALSE(ServiceIsSet());
+  histogram_tester().ExpectTotalCount(kTotalGattConnectionTime, 0);
+}
+
+TEST_F(FastPairGattServiceClientTest, GattConnectionSuccess) {
+  histogram_tester().ExpectTotalCount(kTotalGattConnectionTime, 0);
+  SuccessfulGattConnectionSetUp();
+  NotifyGattDiscoveryCompleteForService();
+  histogram_tester().ExpectTotalCount(kTotalGattConnectionTime, 1);
 }
 
 TEST_F(FastPairGattServiceClientTest, IgnoreNonFastPairServices) {
diff --git a/ash/rotator/screen_rotation_animator.h b/ash/rotator/screen_rotation_animator.h
index 57dd8d9..97a8954 100644
--- a/ash/rotator/screen_rotation_animator.h
+++ b/ash/rotator/screen_rotation_animator.h
@@ -18,7 +18,7 @@
 
 namespace aura {
 class Window;
-}  // namesapce aura
+}  // namespace aura
 
 namespace ui {
 class LayerTreeOwner;
diff --git a/ash/style/ash_color_provider_unittests.cc b/ash/style/ash_color_provider_unittests.cc
index df6e6d7..dd85bf3 100644
--- a/ash/style/ash_color_provider_unittests.cc
+++ b/ash/style/ash_color_provider_unittests.cc
@@ -4,10 +4,10 @@
 
 #include "ash/style/ash_color_provider.h"
 
-#include "ash/constants/ash_features.h"
 #include "ash/session/test_session_controller_client.h"
 #include "ash/test/ash_test_base.h"
 #include "base/test/scoped_feature_list.h"
+#include "chromeos/constants/chromeos_features.h"
 
 namespace ash {
 
@@ -21,7 +21,7 @@
   // When dark/light mode is enabled. Color mode in non-active user sessions
   // (e.g, login page) should be DARK, but LIGHT while in OOBE.
   base::test::ScopedFeatureList enable_dark_light;
-  enable_dark_light.InitAndEnableFeature(features::kDarkLightMode);
+  enable_dark_light.InitAndEnableFeature(chromeos::features::kDarkLightMode);
   client->SetSessionState(session_manager::SessionState::UNKNOWN);
   EXPECT_TRUE(color_provider->IsDarkModeEnabled());
   client->SetSessionState(session_manager::SessionState::OOBE);
@@ -30,7 +30,7 @@
   // When dark/light mode is disabled. Color mode in non-active user sessions
   // (e.g, login page) should still be DARK.
   base::test::ScopedFeatureList disable_dark_light;
-  disable_dark_light.InitAndDisableFeature(features::kDarkLightMode);
+  disable_dark_light.InitAndDisableFeature(chromeos::features::kDarkLightMode);
   client->SetSessionState(session_manager::SessionState::UNKNOWN);
   EXPECT_TRUE(color_provider->IsDarkModeEnabled());
   client->SetSessionState(session_manager::SessionState::OOBE);
diff --git a/ash/system/cast/tray_cast.cc b/ash/system/cast/tray_cast.cc
index ff03133..369feb8 100644
--- a/ash/system/cast/tray_cast.cc
+++ b/ash/system/cast/tray_cast.cc
@@ -11,12 +11,16 @@
 
 #include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/ash_view_ids.h"
+#include "ash/public/cpp/system_tray_client.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/system/model/enterprise_domain_model.h"
+#include "ash/system/model/system_tray_model.h"
 #include "ash/system/tray/hover_highlight_view.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_detailed_view.h"
+#include "base/metrics/user_metrics.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/branding_buildflags.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -120,6 +124,19 @@
     view_to_sink_map_[container] = sink.id;
   }
 
+  if (CastConfigController::Get()->AccessCodeCastingEnabled()) {
+    EnterpriseDomainModel* enterprise_domain =
+        Shell::Get()->system_tray_model()->enterprise_domain();
+    const std::string& org_name = enterprise_domain->account_domain_manager();
+    DCHECK(!org_name.empty())
+        << "account_domain_manager should not be empty when user is managed!";
+    add_access_code_device_ = AddScrollListItem(
+        // TODO(b/209720161): replace with plus button icon when UI is final.
+        SinkIconTypeToIcon(SinkIconType::kGeneric),
+        l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_CAST_ACCESS_CAST_ADD,
+                                   base::UTF8ToUTF16(org_name)));
+  }
+
   scroll_content()->SizeToPreferredSize();
   scroller()->Layout();
 }
@@ -131,6 +148,10 @@
     CastConfigController::Get()->CastToSink(it->second);
     Shell::Get()->metrics()->RecordUserMetricsAction(
         UMA_STATUS_AREA_DETAILED_CAST_VIEW_LAUNCH_CAST);
+  } else if (view == add_access_code_device_) {
+    base::RecordAction(base::UserMetricsAction(
+        "StatusArea_Cast_Detailed_Launch_AccesCastDialog"));
+    Shell::Get()->system_tray_model()->client()->ShowAccessCodeCastingDialog();
   }
 }
 
diff --git a/ash/system/cast/tray_cast.h b/ash/system/cast/tray_cast.h
index c997bda..5b895fb 100644
--- a/ash/system/cast/tray_cast.h
+++ b/ash/system/cast/tray_cast.h
@@ -45,6 +45,9 @@
   std::map<std::string, SinkAndRoute> sinks_and_routes_;
   // A mapping from the view pointer to the associated activity sink id.
   std::map<views::View*, std::string> view_to_sink_map_;
+
+  // Special list item that, if clicked, launches the access code casting dialog
+  views::View* add_access_code_device_ = nullptr;
 };
 
 }  // namespace tray
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index e8b6c17..5529a973 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -48,6 +48,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time_override.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/user_manager/fake_user_manager.h"
 #include "components/user_manager/scoped_user_manager.h"
@@ -3250,7 +3251,7 @@
   void SetUp() override {
     scoped_feature_list_.InitWithFeatures(
         {features::kWallpaperWebUI, features::kWallpaperFullScreenPreview,
-         features::kDarkLightMode},
+         chromeos::features::kDarkLightMode},
         {});
     WallpaperControllerTestBase::SetUp();
   }
diff --git a/ash/wm/desks/desk_mini_view.cc b/ash/wm/desks/desk_mini_view.cc
index 53d5d90..3b0b797 100644
--- a/ash/wm/desks/desk_mini_view.cc
+++ b/ash/wm/desks/desk_mini_view.cc
@@ -487,9 +487,16 @@
   const gfx::Size desk_name_view_size = desk_name_view_->GetPreferredSize();
   // Desk preview's width is supposed to be larger than kMinDeskNameViewWidth,
   // but it might be not the truth for tests with extreme abnormal size of
-  // display.
-  const int min_width = std::min(preview_bounds.width(), kMinDeskNameViewWidth);
-  const int max_width = std::max(preview_bounds.width(), kMinDeskNameViewWidth);
+  // display. The preview uses a border to display focus and the name view uses
+  // a focus ring (which does not inset the view), so subtract the focus ring
+  // from the size calculations so that the focus UI is aligned.
+  views::FocusRing* focus_ring = views::FocusRing::Get(desk_name_view_);
+  const int focus_ring_length =
+      focus_ring->halo_thickness() - focus_ring->halo_inset();
+  const int min_width = std::min(preview_bounds.width() - focus_ring_length,
+                                 kMinDeskNameViewWidth);
+  const int max_width = std::max(preview_bounds.width() - focus_ring_length,
+                                 kMinDeskNameViewWidth);
   const int text_width =
       base::clamp(desk_name_view_size.width(), min_width, max_width);
   const int desk_name_view_x =
@@ -497,7 +504,7 @@
   gfx::Rect desk_name_view_bounds{desk_name_view_x,
                                   preview_bounds.bottom() -
                                       GetPreviewBorderInsets().bottom() +
-                                      kLabelPreviewSpacing,
+                                      kLabelPreviewSpacing + focus_ring_length,
                                   text_width, desk_name_view_size.height()};
   desk_name_view_->SetBoundsRect(desk_name_view_bounds);
 
diff --git a/ash/wm/desks/desk_name_view.cc b/ash/wm/desks/desk_name_view.cc
index 9bdac2e..9a10759 100644
--- a/ash/wm/desks/desk_name_view.cc
+++ b/ash/wm/desks/desk_name_view.cc
@@ -20,6 +20,8 @@
 
 namespace {
 
+constexpr int kDeskNameViewHorizontalPadding = 6;
+
 bool IsDesksBarWidget(const views::Widget* widget) {
   if (!widget)
     return false;
@@ -39,7 +41,13 @@
 
 }  // namespace
 
-DeskNameView::DeskNameView(DeskMiniView* mini_view) : mini_view_(mini_view) {}
+DeskNameView::DeskNameView(DeskMiniView* mini_view) : mini_view_(mini_view) {
+  views::Builder<DeskNameView>(this)
+      .SetBorder(views::CreateEmptyBorder(
+          gfx::Insets(0, kDeskNameViewHorizontalPadding)))
+      .SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_CENTER)
+      .BuildChildren();
+}
 
 DeskNameView::~DeskNameView() = default;
 
diff --git a/ash/wm/desks/desk_name_view.h b/ash/wm/desks/desk_name_view.h
index 0073bd7..74f4912 100644
--- a/ash/wm/desks/desk_name_view.h
+++ b/ash/wm/desks/desk_name_view.h
@@ -38,6 +38,11 @@
   DeskMiniView* const mini_view_;
 };
 
+BEGIN_VIEW_BUILDER(/* no export */, DeskNameView, DesksTextfield)
+END_VIEW_BUILDER
+
 }  // namespace ash
 
+DEFINE_VIEW_BUILDER(/* no export */, ash::DeskNameView)
+
 #endif  // ASH_WM_DESKS_DESK_NAME_VIEW_H_
diff --git a/ash/wm/desks/desks_textfield.cc b/ash/wm/desks/desks_textfield.cc
index 63c158f5..21e7601 100644
--- a/ash/wm/desks/desks_textfield.cc
+++ b/ash/wm/desks/desks_textfield.cc
@@ -12,38 +12,49 @@
 #include "ui/gfx/text_elider.h"
 #include "ui/views/accessibility/accessibility_paint_checks.h"
 #include "ui/views/background.h"
+#include "ui/views/controls/focus_ring.h"
+#include "ui/views/controls/highlight_path_generator.h"
 #include "ui/views/native_cursor.h"
 
 namespace ash {
 
 namespace {
 
-constexpr int kDesksTextfieldMinHeight = 24;
-constexpr int kDesksTextfieldHorizontalPadding = 6;
+// The border radius on the text field.
+constexpr int kDesksTextfieldBorderRadius = 4;
+
+constexpr int kDesksTextfieldMinHeight = 16;
+
+// Inset for the focus ring around the textfield.
+constexpr int kFocusRingHaloInset = -2;
 
 }  // namespace
 
 DesksTextfield::DesksTextfield() {
-  auto border = std::make_unique<WmHighlightItemBorder>(
-      kDesksTextfieldBorderRadius,
-      gfx::Insets(0, kDesksTextfieldHorizontalPadding));
-  border_ptr_ = border.get();
-
   views::Builder<DesksTextfield>(this)
-      .SetBorder(std::move(border))
+      .SetBorder(nullptr)
       .SetCursorEnabled(true)
-      .SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_CENTER)
       // TODO(crbug.com/1218186): Remove this, this is in place temporarily to
       // be able to submit accessibility checks, but this focusable View needs
       // to add a name so that the screen reader knows what to announce.
       .SetProperty(views::kSkipAccessibilityPaintChecks, true)
       .BuildChildren();
+
+  views::FocusRing::Install(this);
+  views::FocusRing* focus_ring = views::FocusRing::Get(this);
+  focus_ring->SetHasFocusPredicate([](views::View* view) {
+    return static_cast<DesksTextfield*>(view)->IsViewHighlighted() ||
+           view->HasFocus();
+  });
+  focus_ring->SetHaloInset(kFocusRingHaloInset);
+  focus_ring->SetPathGenerator(
+      std::make_unique<views::RoundRectHighlightPathGenerator>(
+          gfx::Insets(), kDesksTextfieldBorderRadius));
 }
 
 DesksTextfield::~DesksTextfield() = default;
 
 // static
-constexpr size_t DesksTextfield::kDesksTextfieldBorderRadius;
 constexpr size_t DesksTextfield::kMaxLength;
 
 void DesksTextfield::SetTextAndElideIfNeeded(const std::u16string& text) {
@@ -58,7 +69,7 @@
 
 void DesksTextfield::UpdateViewAppearance() {
   background()->SetNativeControlColor(GetBackgroundColor());
-  UpdateBorderState();
+  UpdateFocusRingState();
 }
 
 gfx::Size DesksTextfield::CalculatePreferredSize() const {
@@ -74,6 +85,12 @@
   return size;
 }
 
+void DesksTextfield::SetBorder(std::unique_ptr<views::Border> b) {
+  // `views::Textfield` override of `SetBorder()` removes an installed focus
+  // ring, which we want to keep.
+  views::View::SetBorder(std::move(b));
+}
+
 bool DesksTextfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
   // The default behavior of the tab key is that it moves the focus to the next
   // available view.
@@ -108,7 +125,11 @@
   const SkColor selection_color = color_provider->GetControlsLayerColor(
       AshColorProvider::ControlsLayerType::kFocusAuraColor);
   SetSelectionBackgroundColor(selection_color);
-  UpdateBorderState();
+
+  views::FocusRing::Get(this)->SetColor(color_provider->GetControlsLayerColor(
+      AshColorProvider::ControlsLayerType::kFocusRingColor));
+
+  UpdateFocusRingState();
 }
 
 gfx::NativeCursor DesksTextfield::GetCursor(const ui::MouseEvent& event) {
@@ -128,16 +149,17 @@
 void DesksTextfield::MaybeSwapHighlightedView(bool right) {}
 
 void DesksTextfield::OnViewHighlighted() {
-  UpdateBorderState();
+  UpdateFocusRingState();
 }
 
 void DesksTextfield::OnViewUnhighlighted() {
-  UpdateBorderState();
+  UpdateFocusRingState();
 }
 
-void DesksTextfield::UpdateBorderState() {
-  border_ptr_->SetFocused(IsViewHighlighted() || HasFocus());
-  SchedulePaint();
+void DesksTextfield::UpdateFocusRingState() {
+  views::FocusRing* focus_ring = views::FocusRing::Get(this);
+  DCHECK(focus_ring);
+  focus_ring->SchedulePaint();
 }
 
 SkColor DesksTextfield::GetBackgroundColor() const {
diff --git a/ash/wm/desks/desks_textfield.h b/ash/wm/desks/desks_textfield.h
index 2eeac37..9ed8c6a 100644
--- a/ash/wm/desks/desks_textfield.h
+++ b/ash/wm/desks/desks_textfield.h
@@ -7,7 +7,6 @@
 
 #include "ash/ash_export.h"
 #include "ash/wm/overview/overview_highlightable_view.h"
-#include "ash/wm/wm_highlight_item_border.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/controls/textfield/textfield.h"
 
@@ -27,9 +26,6 @@
   DesksTextfield& operator=(const DesksTextfield&) = delete;
   ~DesksTextfield() override;
 
-  // The border radius on the text field.
-  static constexpr size_t kDesksTextfieldBorderRadius = 4;
-
   // The max number of characters (UTF-16) allowed for the textfield.
   static constexpr size_t kMaxLength = 300;
 
@@ -44,6 +40,7 @@
 
   // views::View:
   gfx::Size CalculatePreferredSize() const override;
+  void SetBorder(std::unique_ptr<views::Border> b) override;
   bool SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   void OnMouseEntered(const ui::MouseEvent& event) override;
@@ -60,15 +57,11 @@
   void OnViewUnhighlighted() override;
 
  protected:
-  // Owned by this View via `View::border_`. This is just a convenient pointer
-  // to it.
-  WmHighlightItemBorder* border_ptr_;
-
   // Full text without being elided.
   std::u16string full_text_;
 
  private:
-  void UpdateBorderState();
+  void UpdateFocusRingState();
 
   // Returns the background color for this view based on whether it has focus
   // and if the mouse is entering/exiting the view.
diff --git a/ash/wm/desks/expanded_desks_bar_button.cc b/ash/wm/desks/expanded_desks_bar_button.cc
index cdf6f72..4c6c9b20 100644
--- a/ash/wm/desks/expanded_desks_bar_button.cc
+++ b/ash/wm/desks/expanded_desks_bar_button.cc
@@ -16,6 +16,7 @@
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_highlight_controller.h"
 #include "ash/wm/overview/overview_session.h"
+#include "ash/wm/wm_highlight_item_border.h"
 #include "ui/compositor/layer.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/text_elider.h"
diff --git a/ash/wm/desks/templates/desks_templates_item_view.cc b/ash/wm/desks/templates/desks_templates_item_view.cc
index 20ab66f..d367d35e 100644
--- a/ash/wm/desks/templates/desks_templates_item_view.cc
+++ b/ash/wm/desks/templates/desks_templates_item_view.cc
@@ -409,7 +409,7 @@
   // we needed to make `this` a `ViewTargeterDelegate` for the view event
   // targeter in order to allow the `name_view_` to be specifically targeted and
   // focused.
-  if (root == this && name_view_->bounds().Contains(rect))
+  if (root == this && name_view_->GetMirroredBounds().Contains(rect))
     return name_view_;
   return views::ViewTargeterDelegate::TargetForRect(root, rect);
 }
diff --git a/ash/wm/desks/templates/desks_templates_name_view.cc b/ash/wm/desks/templates/desks_templates_name_view.cc
index cdfafb1..4066fbdc 100644
--- a/ash/wm/desks/templates/desks_templates_name_view.cc
+++ b/ash/wm/desks/templates/desks_templates_name_view.cc
@@ -42,19 +42,6 @@
 }  // namespace
 
 DesksTemplatesNameView::DesksTemplatesNameView() {
-  // TODO(richui): We need to shift the alignment of the `name_view_` in the
-  // `DesksTemplatesItemView` so that the text lines up with the other UI
-  // elements. This will be done by refactoring `WmHighlightItemBorder` to
-  // adjust the border, which we update here.
-  auto border = std::make_unique<WmHighlightItemBorder>(
-      DesksTextfield::kDesksTextfieldBorderRadius);
-  border_ptr_ = border.get();
-
-  views::Builder<DesksTemplatesNameView>(this)
-      .SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT)
-      .SetBorder(std::move(border))
-      .BuildChildren();
-
   SetFontList(GetFontList().Derive(kNameFontSizeDeltaDp, gfx::Font::NORMAL,
                                    gfx::Font::Weight::BOLD));
 }
diff --git a/ash/wm/desks/templates/desks_templates_unittest.cc b/ash/wm/desks/templates/desks_templates_unittest.cc
index e2d63e1..d2dc5d82 100644
--- a/ash/wm/desks/templates/desks_templates_unittest.cc
+++ b/ash/wm/desks/templates/desks_templates_unittest.cc
@@ -51,11 +51,13 @@
 #include "extensions/common/constants.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
+#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
 #include "ui/compositor/layer.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/window/dialog_delegate.h"
+#include "ui/wm/core/cursor_manager.h"
 
 namespace ash {
 
@@ -1814,4 +1816,34 @@
   SendKey(ui::VKEY_TAB);
 }
 
+// Tests that the hovering over the templates name shows the expected cursor.
+TEST_F(DesksTemplatesTest, TemplatesNameHitTest) {
+  auto* cursor_manager = Shell::Get()->cursor_manager();
+
+  for (bool is_rtl : {true, false}) {
+    SCOPED_TRACE(is_rtl ? "rtl" : "ltr");
+    base::i18n::SetRTLForTesting(is_rtl);
+
+    AddEntry(base::GUID::GenerateRandomV4(), "a", base::Time::Now());
+
+    OpenOverviewAndShowTemplatesGrid();
+    DesksTemplatesNameView* name_view =
+        GetItemViewFromTemplatesGrid(0)->name_view();
+    const gfx::Rect name_view_bounds = name_view->GetBoundsInScreen();
+    // Hover to a point just inside main edge. This will cover the case where
+    // the hit test logic is inverted.
+    const gfx::Point hover_point =
+        is_rtl ? name_view_bounds.right_center() + gfx::Vector2d(-2, 0)
+               : name_view_bounds.left_center() + gfx::Vector2d(2, 0);
+
+    // Tests that the hover cursor is an IBeam.
+    GetEventGenerator()->MoveMouseTo(hover_point);
+    EXPECT_EQ(ui::mojom::CursorType::kIBeam,
+              cursor_manager->GetCursor().type());
+
+    // Exit overview for the next run.
+    ToggleOverview();
+  }
+}
+
 }  // namespace ash
diff --git a/base/android/java/src/org/chromium/base/task/AsyncTask.java b/base/android/java/src/org/chromium/base/task/AsyncTask.java
index 8cfee1d..099c36d 100644
--- a/base/android/java/src/org/chromium/base/task/AsyncTask.java
+++ b/base/android/java/src/org/chromium/base/task/AsyncTask.java
@@ -157,6 +157,20 @@
     }
 
     /**
+     * Returns the current status of this task, with adjustments made to make UMA more useful.
+     * Namely, we are going to return "PENDING" until the asynctask actually starts running. Right
+     * now, as soon as you try to schedule the AsyncTask, it gets set to "RUNNING" which doesn't
+     * make sense. However, we aren't fixing this globally as this is the well-defined API
+     * AsyncTasks have, so we are just fixing this for our UMA reporting.
+     *
+     * @return The current status.
+     */
+    public final @Status int getUmaStatus() {
+        if (mStatus == Status.RUNNING && !mTaskInvoked.get()) return Status.PENDING;
+        return mStatus;
+    }
+
+    /**
      * Override this method to perform a computation on a background thread.
      *
      * @return A result, defined by the subclass of this task.
@@ -294,7 +308,7 @@
     @SuppressWarnings("NoDynamicStringsInTraceEventCheck")
     public final Result get() throws InterruptedException, ExecutionException {
         Result r;
-        int status = getStatus();
+        int status = getUmaStatus();
         if (status != Status.FINISHED && ThreadUtils.runningOnUiThread()) {
             RecordHistogram.recordEnumeratedHistogram(
                     GET_STATUS_UMA_HISTOGRAM, status, Status.NUM_ENTRIES);
@@ -332,7 +346,7 @@
     public final Result get(long timeout, TimeUnit unit)
             throws InterruptedException, ExecutionException, TimeoutException {
         Result r;
-        int status = getStatus();
+        int status = getUmaStatus();
         if (status != Status.FINISHED && ThreadUtils.runningOnUiThread()) {
             RecordHistogram.recordEnumeratedHistogram(
                     GET_STATUS_UMA_HISTOGRAM, status, Status.NUM_ENTRIES);
diff --git a/base/containers/enum_set.h b/base/containers/enum_set.h
index 204f7f07..30fbb36 100644
--- a/base/containers/enum_set.h
+++ b/base/containers/enum_set.h
@@ -48,6 +48,10 @@
       "First template parameter of EnumSet must be an enumeration type");
   using enum_underlying_type = std::underlying_type_t<E>;
 
+  static constexpr bool InRange(E value) {
+    return (value >= MinEnumValue) && (value <= MaxEnumValue);
+  }
+
   static constexpr enum_underlying_type GetUnderlyingValue(E value) {
     return static_cast<enum_underlying_type>(value);
   }
@@ -95,7 +99,7 @@
   class Iterator {
    public:
     Iterator() : enums_(nullptr), i_(kValueCount) {}
-    ~Iterator() {}
+    ~Iterator() = default;
 
     bool operator==(const Iterator& other) const { return i_ == other.i_; }
 
@@ -147,12 +151,15 @@
     size_t i_;
   };
 
-  EnumSet() {}
+  EnumSet() = default;
 
   ~EnumSet() = default;
 
   static constexpr uint64_t single_val_bitstring(E val) {
-    return 1ULL << (ToIndex(val));
+    const uint64_t bitstring = 1;
+    const size_t shift_amount = ToIndex(val);
+    CHECK_LT(shift_amount, sizeof(bitstring) * 8);
+    return bitstring << shift_amount;
   }
 
   template <class... T>
@@ -175,6 +182,7 @@
 
   // Returns an EnumSet with all the values from start to end, inclusive.
   static constexpr EnumSet FromRange(E start, E end) {
+    CHECK_LE(start, end);
     return EnumSet(EnumBitSet(
         ((single_val_bitstring(end)) - (single_val_bitstring(start))) |
         (single_val_bitstring(end))));
@@ -218,8 +226,8 @@
 
   // Adds all values in the given range to our set, inclusive.
   void PutRange(E start, E end) {
+    CHECK_LE(start, end);
     size_t endIndexInclusive = ToIndex(end);
-    DCHECK_LE(ToIndex(start), endIndexInclusive);
     for (size_t current = ToIndex(start); current <= endIndexInclusive;
          ++current) {
       enums_.set(current);
@@ -291,14 +299,11 @@
                   "Max number of enum values is 64 for constexpr ");
   }
 
-  static constexpr bool InRange(E value) {
-    return (value >= MinEnumValue) && (value <= MaxEnumValue);
-  }
-
   // Converts a value to/from an index into |enums_|.
-
   static constexpr size_t ToIndex(E value) {
-    return GetUnderlyingValue(value) - GetUnderlyingValue(MinEnumValue);
+    CHECK(InRange(value));
+    return static_cast<size_t>(GetUnderlyingValue(value)) -
+           static_cast<size_t>(GetUnderlyingValue(MinEnumValue));
   }
 
   static E FromIndex(size_t i) {
diff --git a/base/containers/enum_set_unittest.cc b/base/containers/enum_set_unittest.cc
index f025c8a3..4ecbe7d 100644
--- a/base/containers/enum_set_unittest.cc
+++ b/base/containers/enum_set_unittest.cc
@@ -6,12 +6,16 @@
 
 #include <stddef.h>
 
+#include "base/test/gtest_util.h"
+#include "testing/gtest/include/gtest/gtest-death-test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
 namespace {
 
 enum class TestEnum {
+  TEST_BELOW_MIN_NEGATIVE = -1,
+  TEST_BELOW_MIN = 0,
   TEST_1 = 1,
   TEST_MIN = TEST_1,
   TEST_2,
@@ -19,7 +23,8 @@
   TEST_4,
   TEST_5,
   TEST_MAX = TEST_5,
-  TEST_6_OUT_OF_BOUNDS
+  TEST_6_OUT_OF_BOUNDS,
+  TEST_7_OUT_OF_BOUNDS
 };
 using TestEnumSet = EnumSet<TestEnum, TestEnum::TEST_MIN, TestEnum::TEST_MAX>;
 
@@ -35,9 +40,9 @@
                                    TestEnumExtreme::TEST_MAX>;
 
 class EnumSetTest : public ::testing::Test {};
+class EnumSetDeathTest : public ::testing::Test {};
 
 TEST_F(EnumSetTest, ClassConstants) {
-  TestEnumSet enums;
   EXPECT_EQ(TestEnum::TEST_MIN, TestEnumSet::kMinValue);
   EXPECT_EQ(TestEnum::TEST_MAX, TestEnumSet::kMaxValue);
   EXPECT_EQ(static_cast<size_t>(5), TestEnumSet::kValueCount);
@@ -47,16 +52,21 @@
 // evaluatable are really that way.
 TEST_F(EnumSetTest, ConstexprsAreValid) {
   static_assert(TestEnumSet::All().Has(TestEnum::TEST_2),
-                "expected All() to be integral constant expression");
+                "Expected All() to be integral constant expression");
   static_assert(TestEnumSet::FromRange(TestEnum::TEST_2, TestEnum::TEST_4)
                     .Has(TestEnum::TEST_2),
-                "expected FromRange() to be integral constant expression");
+                "Expected FromRange() to be integral constant expression");
   static_assert(TestEnumSet(TestEnum::TEST_2).Has(TestEnum::TEST_2),
-                "expected TestEnumSet() to be integral constant expression");
+                "Expected TestEnumSet() to be integral constant expression");
   static_assert(
       TestEnumSet::FromEnumBitmask(1 << static_cast<uint64_t>(TestEnum::TEST_2))
           .Has(TestEnum::TEST_2),
       "Expected TestEnumSet() to be integral constant expression");
+  static_assert(
+      TestEnumSet::single_val_bitstring(TestEnum::TEST_1) == 1,
+      "Expected single_val_bitstring() to be integral constant expression");
+  static_assert(TestEnumSet::bitstring(TestEnum::TEST_1, TestEnum::TEST_2) == 3,
+                "Expected bitstring() to be integral constant expression");
 }
 
 TEST_F(EnumSetTest, DefaultConstructor) {
@@ -232,8 +242,7 @@
 }
 
 TEST_F(EnumSetTest, RangeBasedForLoop) {
-  const TestEnumSet enums1(TestEnum::TEST_2, TestEnum::TEST_5,
-                           TestEnum::TEST_6_OUT_OF_BOUNDS);
+  const TestEnumSet enums1(TestEnum::TEST_2, TestEnum::TEST_5);
   TestEnumSet enums2;
   for (TestEnum e : enums1) {
     enums2.Put(e);
@@ -242,8 +251,7 @@
 }
 
 TEST_F(EnumSetTest, IteratorComparisonOperators) {
-  const TestEnumSet enums(TestEnum::TEST_2, TestEnum::TEST_4,
-                          TestEnum::TEST_6_OUT_OF_BOUNDS);
+  const TestEnumSet enums(TestEnum::TEST_2, TestEnum::TEST_4);
   const auto first_it = enums.begin();
   const auto second_it = ++enums.begin();
 
@@ -264,8 +272,7 @@
 }
 
 TEST_F(EnumSetTest, IteratorIncrementOperators) {
-  const TestEnumSet enums(TestEnum::TEST_2, TestEnum::TEST_4,
-                          TestEnum::TEST_6_OUT_OF_BOUNDS);
+  const TestEnumSet enums(TestEnum::TEST_2, TestEnum::TEST_4);
   const auto begin = enums.begin();
 
   auto post_inc_it = begin;
@@ -348,5 +355,181 @@
   EXPECT_EQ(TestEnumExtremeSet::FromEnumBitmask(val1), enums1);
 }
 
+TEST_F(EnumSetTest, FromEnumBitmaskIgnoresExtraBits) {
+  const TestEnumSet kSets[] = {
+      TestEnumSet(),
+      TestEnumSet(TestEnum::TEST_MIN),
+      TestEnumSet(TestEnum::TEST_MAX),
+      TestEnumSet(TestEnum::TEST_MIN, TestEnum::TEST_MAX),
+      TestEnumSet(TestEnum::TEST_MIN, TestEnum::TEST_MAX),
+      TestEnumSet(TestEnum::TEST_2, TestEnum::TEST_4),
+  };
+  size_t i = 0;
+  for (const TestEnumSet& set : kSets) {
+    SCOPED_TRACE(i++);
+    const uint64_t val = set.ToEnumBitmask();
+
+    // Produce a bitstring for a single enum value. When `e` is in range
+    // relative to TestEnumSet, this function behaves identically to
+    // `single_val_bitstring`. When `e` is not in range, this function attempts
+    // to compute a value, while `single_val_bitstring` intentionally crashes.
+    auto single_val_bitstring = [](TestEnum e) -> uint64_t {
+      uint64_t shift_amount = static_cast<uint64_t>(e);
+      // Shifting left more than the number of bits in the lhs would be UB.
+      CHECK_LT(shift_amount, sizeof(uint64_t) * 8);
+      return 1ULL << shift_amount;
+    };
+
+    const uint64_t kJunkVals[] = {
+        // Add junk bits above TEST_MAX.
+        val | single_val_bitstring(TestEnum::TEST_6_OUT_OF_BOUNDS),
+        val | single_val_bitstring(TestEnum::TEST_7_OUT_OF_BOUNDS),
+        val | single_val_bitstring(TestEnum::TEST_6_OUT_OF_BOUNDS) |
+            single_val_bitstring(TestEnum::TEST_7_OUT_OF_BOUNDS),
+        // Add junk bits below TEST_MIN.
+        val | single_val_bitstring(TestEnum::TEST_BELOW_MIN),
+    };
+    for (uint64_t junk_val : kJunkVals) {
+      SCOPED_TRACE(junk_val);
+      ASSERT_NE(val, junk_val);
+
+      const TestEnumSet set_from_junk = TestEnumSet::FromEnumBitmask(junk_val);
+      EXPECT_EQ(set_from_junk, set);
+      EXPECT_EQ(set_from_junk.ToEnumBitmask(), set.ToEnumBitmask());
+
+      // Iterating both sets should produce the same sequence.
+      auto it1 = set.begin();
+      auto it2 = set_from_junk.begin();
+      while (it1 != set.end() && it2 != set_from_junk.end()) {
+        EXPECT_EQ(*it1, *it2);
+        ++it1;
+        ++it2;
+      }
+      EXPECT_TRUE(it1 == set.end());
+      EXPECT_TRUE(it2 == set_from_junk.end());
+    }
+  }
+}
+
+TEST_F(EnumSetDeathTest, SingleValBitstringCrashesOnOutOfRange) {
+  EXPECT_CHECK_DEATH(
+      TestEnumSet::single_val_bitstring(TestEnum::TEST_BELOW_MIN));
+  EXPECT_CHECK_DEATH(
+      TestEnumSet::single_val_bitstring(TestEnum::TEST_6_OUT_OF_BOUNDS));
+  EXPECT_CHECK_DEATH(
+      TestEnumSet::single_val_bitstring(TestEnum::TEST_7_OUT_OF_BOUNDS));
+}
+
+TEST_F(EnumSetDeathTest, SingleValBitstringEnumWithNegatives) {
+  enum class TestEnumNeg {
+    TEST_BELOW_MIN = -3,
+    TEST_A = -2,
+    TEST_MIN = TEST_A,
+    TEST_B = -1,
+    TEST_C = 0,
+    TEST_D = 1,
+    TEST_E = 2,
+    TEST_MAX = TEST_E,
+    TEST_F = 3,
+  };
+  // This EnumSet starts negative and ends positive.
+  using TestEnumWithNegSet =
+      EnumSet<TestEnumNeg, TestEnumNeg::TEST_MIN, TestEnumNeg::TEST_MAX>;
+
+  // Should crash because TEST_BELOW_MIN is not in range.
+  EXPECT_CHECK_DEATH(
+      TestEnumWithNegSet::single_val_bitstring(TestEnumNeg::TEST_BELOW_MIN));
+  // TEST_D is in range, but note that TEST_MIN is negative. This should work.
+  EXPECT_EQ(TestEnumWithNegSet::single_val_bitstring(TestEnumNeg::TEST_D),
+            1u << 3);
+  // Even though TEST_A is negative, it is in range, so this should work.
+  EXPECT_EQ(TestEnumWithNegSet::single_val_bitstring(TestEnumNeg::TEST_A),
+            1u << 0);
+}
+
+TEST_F(EnumSetDeathTest, SingleValBitstringEnumWithOnlyNegatives) {
+  enum class TestEnumNeg {
+    TEST_BELOW_MIN = -10,
+    TEST_A = -9,
+    TEST_MIN = TEST_A,
+    TEST_B = -8,
+    TEST_C = -7,
+    TEST_D = -6,
+    TEST_MAX = TEST_D,
+    TEST_F = -5,
+  };
+  // This EnumSet starts negative and ends negative.
+  using TestEnumWithNegSet =
+      EnumSet<TestEnumNeg, TestEnumNeg::TEST_MIN, TestEnumNeg::TEST_MAX>;
+
+  // Should crash because TEST_BELOW_MIN is not in range.
+  EXPECT_CHECK_DEATH(
+      TestEnumWithNegSet::single_val_bitstring(TestEnumNeg::TEST_BELOW_MIN));
+  // TEST_D is in range, but note that TEST_MIN is negative. This should work.
+  EXPECT_EQ(TestEnumWithNegSet::single_val_bitstring(TestEnumNeg::TEST_D),
+            1u << 3);
+  // Even though TEST_A is negative, it is in range, so this should work.
+  EXPECT_EQ(TestEnumWithNegSet::single_val_bitstring(TestEnumNeg::TEST_A),
+            1u << 0);
+}
+
+TEST_F(EnumSetDeathTest, VariadicConstructorCrashesOnOutOfRange) {
+  // Constructor should crash given out-of-range values.
+  EXPECT_CHECK_DEATH(TestEnumSet(TestEnum::TEST_BELOW_MIN).Empty());
+  EXPECT_CHECK_DEATH(TestEnumSet(TestEnum::TEST_BELOW_MIN_NEGATIVE).Empty());
+  EXPECT_CHECK_DEATH(TestEnumSet(TestEnum::TEST_6_OUT_OF_BOUNDS).Empty());
+}
+
+TEST_F(EnumSetDeathTest, FromRangeCrashesOnBadInputs) {
+  // FromRange crashes when the bounds are in range, but out of order.
+  EXPECT_CHECK_DEATH(
+      TestEnumSet().FromRange(TestEnum::TEST_3, TestEnum::TEST_1));
+
+  // FromRange crashes when the start value is out of range.
+  EXPECT_CHECK_DEATH(
+      TestEnumSet().FromRange(TestEnum::TEST_BELOW_MIN, TestEnum::TEST_1));
+  EXPECT_CHECK_DEATH(TestEnumSet().FromRange(TestEnum::TEST_BELOW_MIN_NEGATIVE,
+                                             TestEnum::TEST_1));
+  EXPECT_CHECK_DEATH(TestEnumSet().FromRange(TestEnum::TEST_6_OUT_OF_BOUNDS,
+                                             TestEnum::TEST_1));
+
+  // FromRange crashes when the end value is out of range.
+  EXPECT_CHECK_DEATH(
+      TestEnumSet().FromRange(TestEnum::TEST_3, TestEnum::TEST_BELOW_MIN));
+  EXPECT_CHECK_DEATH(TestEnumSet().FromRange(
+      TestEnum::TEST_3, TestEnum::TEST_BELOW_MIN_NEGATIVE));
+  EXPECT_CHECK_DEATH(TestEnumSet().FromRange(TestEnum::TEST_3,
+                                             TestEnum::TEST_6_OUT_OF_BOUNDS));
+
+  // Crashes when start and end are both out of range.
+  EXPECT_CHECK_DEATH(TestEnumSet().FromRange(TestEnum::TEST_7_OUT_OF_BOUNDS,
+                                             TestEnum::TEST_6_OUT_OF_BOUNDS));
+  EXPECT_CHECK_DEATH(TestEnumSet().FromRange(TestEnum::TEST_6_OUT_OF_BOUNDS,
+                                             TestEnum::TEST_7_OUT_OF_BOUNDS));
+}
+
+TEST_F(EnumSetDeathTest, PutCrashesOnOutOfRange) {
+  EXPECT_CHECK_DEATH(TestEnumSet().Put(TestEnum::TEST_BELOW_MIN));
+  EXPECT_CHECK_DEATH(TestEnumSet().Put(TestEnum::TEST_BELOW_MIN_NEGATIVE));
+  EXPECT_CHECK_DEATH(TestEnumSet().Put(TestEnum::TEST_6_OUT_OF_BOUNDS));
+  EXPECT_CHECK_DEATH(TestEnumSet().Put(TestEnum::TEST_7_OUT_OF_BOUNDS));
+}
+
+TEST_F(EnumSetDeathTest, PutRangeCrashesOnBadInputs) {
+  // Crashes when one input is out of range.
+  EXPECT_CHECK_DEATH(TestEnumSet().PutRange(TestEnum::TEST_BELOW_MIN_NEGATIVE,
+                                            TestEnum::TEST_BELOW_MIN));
+  EXPECT_CHECK_DEATH(
+      TestEnumSet().PutRange(TestEnum::TEST_3, TestEnum::TEST_7_OUT_OF_BOUNDS));
+
+  // Crashes when both inputs are out of range.
+  EXPECT_CHECK_DEATH(TestEnumSet().PutRange(TestEnum::TEST_6_OUT_OF_BOUNDS,
+                                            TestEnum::TEST_7_OUT_OF_BOUNDS));
+
+  // Crashes when inputs are out of order.
+  EXPECT_CHECK_DEATH(
+      TestEnumSet().PutRange(TestEnum::TEST_2, TestEnum::TEST_1));
+}
+
 }  // namespace
 }  // namespace base
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 3aaf1d2..7ab9e48 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-7.20211215.0.1
+7.20211215.1.1
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 7ab9e48..8f85665 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-7.20211215.1.1
+7.20211215.2.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 3aaf1d2..7ab9e48 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-7.20211215.0.1
+7.20211215.1.1
diff --git a/buildtools/third_party/libunwind/BUILD.gn b/buildtools/third_party/libunwind/BUILD.gn
index 7560674..36f1d9b 100644
--- a/buildtools/third_party/libunwind/BUILD.gn
+++ b/buildtools/third_party/libunwind/BUILD.gn
@@ -5,6 +5,7 @@
 import("//build/config/c++/c++.gni")
 
 config("libunwind_config") {
+  defines = [ "_LIBUNWIND_IS_NATIVE_ONLY" ]
   cflags = [
     "-fstrict-aliasing",
     "-fPIC",
diff --git a/cc/input/threaded_input_handler.cc b/cc/input/threaded_input_handler.cc
index 9cf134a8..06b79d447 100644
--- a/cc/input/threaded_input_handler.cc
+++ b/cc/input/threaded_input_handler.cc
@@ -1237,6 +1237,13 @@
   return false;
 }
 
+float ThreadedInputHandler::LineStep() const {
+  return kPixelsPerLineStep;
+}
+
+// TODO(mehdika): There is some redundancy between this function and
+// ScrollbarController::GetScrollDistanceForScrollbarPart, these two need to be
+// kept in sync.
 gfx::Vector2dF ThreadedInputHandler::ResolveScrollGranularityToPixels(
     const ScrollNode& scroll_node,
     const gfx::Vector2dF& scroll_delta,
@@ -1266,6 +1273,10 @@
         pixel_delta, scroller_size, viewport_size);
   }
 
+  if (granularity == ui::ScrollGranularity::kScrollByLine) {
+    pixel_delta.Scale(LineStep(), LineStep());
+  }
+
   return pixel_delta;
 }
 
diff --git a/cc/input/threaded_input_handler.h b/cc/input/threaded_input_handler.h
index e8c9d85..d3d9e83 100644
--- a/cc/input/threaded_input_handler.h
+++ b/cc/input/threaded_input_handler.h
@@ -138,6 +138,8 @@
                                   const gfx::Point& viewport_point,
                                   bool is_direct_manipulation);
 
+  float LineStep() const;
+
   // Resolves a delta in the given granularity for the |scroll_node| into
   // physical pixels to scroll.
   gfx::Vector2dF ResolveScrollGranularityToPixels(
diff --git a/chrome/VERSION b/chrome/VERSION
index da106233d..fc51c36 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=99
 MINOR=0
-BUILD=4768
+BUILD=4769
 PATCH=0
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantAutostartTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantAutostartTest.java
index a9e9d1b0..e9890ee3 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantAutostartTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantAutostartTest.java
@@ -28,7 +28,6 @@
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.util.Collections;
 
@@ -40,14 +39,10 @@
 public class AutofillAssistantAutostartTest {
     private final CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
     public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(new AutofillAssistantCustomTabTestRule(
-                            mTestRule, "autofill_assistant_target_website.html"));
+            RuleChain.outerRule(mTestRule).around(new AutofillAssistantCustomTabTestRule(
+                    mTestRule, "autofill_assistant_target_website.html"));
 
     /**
      * Launches autofill assistant with a single autostartable script.
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBackButtonIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBackButtonIntegrationTest.java
index 47a74ff..f298d368 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBackButtonIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBackButtonIntegrationTest.java
@@ -59,7 +59,6 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.ChromeTabUtils;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -77,13 +76,8 @@
     private final AutofillAssistantChromeTabTestRule mTabTestRule =
             new AutofillAssistantChromeTabTestRule(mTestRule, TEST_PAGE_A);
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
-    public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(mTabTestRule);
+    public final TestRule mRulesChain = RuleChain.outerRule(mTestRule).around(mTabTestRule);
 
     private String getURL(String page) {
         return mTabTestRule.getURL(page);
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBottomsheetTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBottomsheetTest.java
index 4ab121e..d5800fbd 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBottomsheetTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBottomsheetTest.java
@@ -81,7 +81,6 @@
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -95,14 +94,10 @@
 public class AutofillAssistantBottomsheetTest {
     private final CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
     public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(new AutofillAssistantCustomTabTestRule(
-                            mTestRule, "bottomsheet_behaviour_target_website.html"));
+            RuleChain.outerRule(mTestRule).around(new AutofillAssistantCustomTabTestRule(
+                    mTestRule, "bottomsheet_behaviour_target_website.html"));
 
     private AutofillAssistantTestScript makeScriptWithActionArray(
             ArrayList<ActionProto> actionsList) {
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java
index 3033855..f1e4d67 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java
@@ -66,7 +66,6 @@
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
 import org.chromium.content_public.browser.test.util.KeyUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 import org.chromium.ui.test.util.UiRestriction;
 
 import java.util.ArrayList;
@@ -85,13 +84,8 @@
     private final AutofillAssistantChromeTabTestRule mTabTestRule =
             new AutofillAssistantChromeTabTestRule(mTestRule, TEST_PAGE_A);
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
-    public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(mTabTestRule);
+    public final TestRule mRulesChain = RuleChain.outerRule(mTestRule).around(mTabTestRule);
 
     private ScrimCoordinator mScrimCoordinator;
 
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataIntegrationTest.java
index be1b831..cc593c9e 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataIntegrationTest.java
@@ -112,7 +112,6 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.content_public.browser.WebContents;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
@@ -131,14 +130,9 @@
 public class AutofillAssistantCollectUserDataIntegrationTest {
     private final CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
-    public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(new AutofillAssistantCustomTabTestRule(
-                            mTestRule, "form_target_website.html"));
+    public final TestRule mRulesChain = RuleChain.outerRule(mTestRule).around(
+            new AutofillAssistantCustomTabTestRule(mTestRule, "form_target_website.html"));
 
     private AutofillAssistantCollectUserDataTestHelper mHelper;
 
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFormActionTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFormActionTest.java
index cfae927..0b1b99da 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFormActionTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFormActionTest.java
@@ -81,7 +81,6 @@
 import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -95,14 +94,10 @@
 public class AutofillAssistantFormActionTest {
     private final CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
     public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(new AutofillAssistantCustomTabTestRule(
-                            mTestRule, "autofill_assistant_target_website.html"));
+            RuleChain.outerRule(mTestRule).around(new AutofillAssistantCustomTabTestRule(
+                    mTestRule, "autofill_assistant_target_website.html"));
 
     /**
      * Creates a close-to-real example of a form action with multiple counters and choices,
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantGenericUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantGenericUiTest.java
index cafb4df..779859a 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantGenericUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantGenericUiTest.java
@@ -164,7 +164,6 @@
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -179,14 +178,10 @@
 public class AutofillAssistantGenericUiTest {
     private final CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
     public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(new AutofillAssistantCustomTabTestRule(
-                            mTestRule, "autofill_assistant_target_website.html"));
+            RuleChain.outerRule(mTestRule).around(new AutofillAssistantCustomTabTestRule(
+                    mTestRule, "autofill_assistant_target_website.html"));
 
     private AutofillAssistantCollectUserDataTestHelper mHelper;
 
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInputActionIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInputActionIntegrationTest.java
index ed35e3e0..ea49cec 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInputActionIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInputActionIntegrationTest.java
@@ -63,7 +63,6 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -84,13 +83,9 @@
 
     private final CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
-    public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(new AutofillAssistantCustomTabTestRule(mTestRule, TEST_PAGE));
+    public final TestRule mRulesChain = RuleChain.outerRule(mTestRule).around(
+            new AutofillAssistantCustomTabTestRule(mTestRule, TEST_PAGE));
 
     @Test
     @MediumTest
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInterruptIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInterruptIntegrationTest.java
index 77a4fc8c..cf95f51 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInterruptIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInterruptIntegrationTest.java
@@ -82,7 +82,6 @@
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -95,14 +94,10 @@
 public class AutofillAssistantInterruptIntegrationTest {
     private final CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
     public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(new AutofillAssistantCustomTabTestRule(
-                            mTestRule, "autofill_assistant_target_website.html"));
+            RuleChain.outerRule(mTestRule).around(new AutofillAssistantCustomTabTestRule(
+                    mTestRule, "autofill_assistant_target_website.html"));
 
     private static final String MAIN_SCRIPT_PATH = "main_script";
     private static final String INTERRUPT_SCRIPT_PATH = "interrupt_script";
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantKeyboardIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantKeyboardIntegrationTest.java
index 32b1f4d..2d1895a 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantKeyboardIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantKeyboardIntegrationTest.java
@@ -54,7 +54,6 @@
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -69,13 +68,9 @@
 
     private final CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
-    public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(new AutofillAssistantCustomTabTestRule(mTestRule, TEST_PAGE));
+    public final TestRule mRulesChain = RuleChain.outerRule(mTestRule).around(
+            new AutofillAssistantCustomTabTestRule(mTestRule, TEST_PAGE));
 
     private void runAutofillAssistant(AutofillAssistantTestScript script) {
         AutofillAssistantTestService testService =
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantNavigationIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantNavigationIntegrationTest.java
index 3f8a5ed..d6a7ae47 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantNavigationIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantNavigationIntegrationTest.java
@@ -47,7 +47,6 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.ChromeTabUtils;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -65,13 +64,8 @@
     private final AutofillAssistantChromeTabTestRule mTabTestRule =
             new AutofillAssistantChromeTabTestRule(mTestRule, TEST_PAGE_A);
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
-    public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(mTabTestRule);
+    public final TestRule mRulesChain = RuleChain.outerRule(mTestRule).around(mTabTestRule);
 
     private String getURL(String page) {
         return mTabTestRule.getURL(page);
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayIntegrationTest.java
index f572966..06df15e 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayIntegrationTest.java
@@ -64,7 +64,6 @@
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -77,14 +76,10 @@
 public class AutofillAssistantOverlayIntegrationTest {
     private final CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
     public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(new AutofillAssistantCustomTabTestRule(
-                            mTestRule, "autofill_assistant_target_website.html"));
+            RuleChain.outerRule(mTestRule).around(new AutofillAssistantCustomTabTestRule(
+                    mTestRule, "autofill_assistant_target_website.html"));
 
     /**
      * Tests that clicking on a document element works with a showcast.
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPasswordManagerIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPasswordManagerIntegrationTest.java
index 2c7f7ed..68cca04 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPasswordManagerIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPasswordManagerIntegrationTest.java
@@ -41,7 +41,6 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -54,14 +53,9 @@
 public class AutofillAssistantPasswordManagerIntegrationTest {
     private final CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
-    public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(new AutofillAssistantCustomTabTestRule(
-                            mTestRule, "form_target_website.html"));
+    public final TestRule mRulesChain = RuleChain.outerRule(mTestRule).around(
+            new AutofillAssistantCustomTabTestRule(mTestRule, "form_target_website.html"));
 
     private WebContents getWebContents() {
         return mTestRule.getWebContents();
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPersonalDataManagerTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPersonalDataManagerTest.java
index ecc2f1f2..b23d7287 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPersonalDataManagerTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPersonalDataManagerTest.java
@@ -84,7 +84,6 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.util.ArrayList;
 import java.util.Calendar;
@@ -98,14 +97,9 @@
 public class AutofillAssistantPersonalDataManagerTest {
     private final CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
-    public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(new AutofillAssistantCustomTabTestRule(
-                            mTestRule, "form_target_website.html"));
+    public final TestRule mRulesChain = RuleChain.outerRule(mTestRule).around(
+            new AutofillAssistantCustomTabTestRule(mTestRule, "form_target_website.html"));
 
     private AutofillAssistantCollectUserDataTestHelper mHelper;
     private AutofillTestHelper mAutofillHelper;
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPromptNavigationIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPromptNavigationIntegrationTest.java
index 86e2229d..a08a573fc 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPromptNavigationIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPromptNavigationIntegrationTest.java
@@ -33,7 +33,6 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -48,13 +47,9 @@
 
     private final CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
-    public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(new AutofillAssistantCustomTabTestRule(mTestRule, TEST_PAGE));
+    public final TestRule mRulesChain = RuleChain.outerRule(mTestRule).around(
+            new AutofillAssistantCustomTabTestRule(mTestRule, TEST_PAGE));
 
     /**
      * Integration test for ending a prompt action when a render side navigation occurs.
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTriggerScriptIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTriggerScriptIntegrationTest.java
index 503c2e1..7cfb380 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTriggerScriptIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTriggerScriptIntegrationTest.java
@@ -73,7 +73,6 @@
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TouchCommon;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -91,13 +90,8 @@
     private final AutofillAssistantChromeTabTestRule mTabTestRule =
             new AutofillAssistantChromeTabTestRule(mTestRule, TEST_PAGE_A);
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
-    public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(mTabTestRule);
+    public final TestRule mRulesChain = RuleChain.outerRule(mTestRule).around(mTabTestRule);
 
     private String getURL(String page) {
         return mTabTestRule.getURL(page);
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTtsIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTtsIntegrationTest.java
index f8f786e0..d54436d8 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTtsIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTtsIntegrationTest.java
@@ -58,7 +58,6 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.content.browser.accessibility.BrowserAccessibilityState;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -74,13 +73,9 @@
 
     private final CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
 
-    // TODO(crbug/1272997): Find out why the DisableAnimationsTestRule is necessary and remove it
-    //  again.
     @Rule
-    public final TestRule mRulesChain =
-            RuleChain.outerRule(mTestRule)
-                    .around(new DisableAnimationsTestRule(/* enableAnimation= */ true))
-                    .around(new AutofillAssistantCustomTabTestRule(mTestRule, TEST_PAGE));
+    public final TestRule mRulesChain = RuleChain.outerRule(mTestRule).around(
+            new AutofillAssistantCustomTabTestRule(mTestRule, TEST_PAGE));
 
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardViewBinderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardViewBinderTest.java
index 70027f6..89fadd5 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardViewBinderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardViewBinderTest.java
@@ -24,6 +24,7 @@
 import org.chromium.base.test.UiThreadTest;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
@@ -180,8 +181,7 @@
                                         mItemView.getContext(), R.color.default_text_color_list)
                                 .getDefaultColor()));
         assertThat(actionButton.getCurrentTextColor(),
-                equalTo(ApiCompatibilityUtils.getColor(
-                        mItemView.getResources(), R.color.default_text_color_link)));
+                equalTo(SemanticColorUtils.getDefaultTextColorLink(mItemView.getContext())));
         assertThat(closeButton.getImageTintList(),
                 equalTo(AppCompatResources.getColorStateList(
                         getActivity(), R.color.default_icon_color_tint_list)));
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageCardTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageCardTest.java
index 3c18d77..1a3a136 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageCardTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageCardTest.java
@@ -182,6 +182,7 @@
     @Test
     @MediumTest
     @CommandLineFlags.Add({BASE_PARAMS + ENABLE_CLOSE_SUGGESTION_PARAM})
+    @DisableIf.Build(sdk_is_less_than = VERSION_CODES.N, message = "https://crbug.com/1280257")
     public void closeTabSuggestionReviewedAndDismissed() {
         CriteriaHelper.pollUiThread(TabSuggestionMessageService::isSuggestionAvailableForTesting);
 
@@ -195,6 +196,7 @@
     @Test
     @MediumTest
     @CommandLineFlags.Add({BASE_PARAMS + ENABLE_GROUP_SUGGESTION_PARAM})
+    @DisableIf.Build(sdk_is_less_than = VERSION_CODES.N, message = "https://crbug.com/1273142")
     public void groupTabSuggestionReviewedAndAccepted() {
         CriteriaHelper.pollUiThread(TabSuggestionMessageService::isSuggestionAvailableForTesting);
 
diff --git a/chrome/android/java/res/values/styles.xml b/chrome/android/java/res/values/styles.xml
index 48045abb..e5e6508 100644
--- a/chrome/android/java/res/values/styles.xml
+++ b/chrome/android/java/res/values/styles.xml
@@ -122,7 +122,7 @@
         parent="Theme.BrowserUI.DialogWhenLarge">
         <item name="android:windowBackground">@drawable/bg_white_dialog</item>
         <item name="android:textAppearance">@style/TextAppearance.TextMedium.Primary</item>
-        <item name="android:textColorLink">@color/default_text_color_link</item>
+        <item name="android:textColorLink">@macro/default_text_color_link</item>
         <item name="colorPrimaryDark">@android:color/black</item>
         <item name="colorAccent">@macro/default_control_color_active</item>
         <item name="colorControlHighlight">@color/control_highlight_color</item>
@@ -345,7 +345,7 @@
 
     <!-- Misc text appearance styles -->
     <style name="TextAppearance.SearchEngineRecentTitle" parent="TextAppearance.RobotoMediumStyle">
-        <item name="android:textColor">@color/default_text_color_link</item>
+        <item name="android:textColor">@macro/default_text_color_link</item>
     </style>
     <!-- TODO(crbug.com/1081933): Replace with a standard text appearance. -->
     <style name="TextAppearance.ClearBrowsingDataText">
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java
index facf66afc..d0f770a8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java
@@ -20,6 +20,8 @@
 import androidx.annotation.StyleRes;
 import androidx.appcompat.app.AppCompatActivity;
 
+import com.google.android.material.color.DynamicColors;
+
 import org.chromium.base.BundleUtils;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.supplier.ObservableSupplier;
@@ -34,7 +36,6 @@
 import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
 import org.chromium.chrome.browser.night_mode.NightModeUtils;
 import org.chromium.chrome.browser.theme.ThemeUtils;
-import org.chromium.chrome.browser.ui.theme.ColorDelegateImpl;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modaldialog.ModalDialogManagerHolder;
 
@@ -211,7 +212,7 @@
         setTheme(R.style.ColorOverlay_ChromiumAndroid);
 
         if (supportsDynamicColors()) {
-            new ColorDelegateImpl().applyDynamicColorsIfAvailable(this);
+            DynamicColors.applyIfAvailable(this);
         }
 
         // Try to enable browser overscroll when content overscroll is enabled for consistency. This
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/CardEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/CardEditor.java
index c7d25377..73cc513 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/CardEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/CardEditor.java
@@ -32,6 +32,7 @@
 import org.chromium.chrome.browser.payments.AutofillAddress;
 import org.chromium.chrome.browser.payments.AutofillPaymentInstrument;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.payments.BasicCardUtils;
 import org.chromium.components.payments.MethodStrings;
 import org.chromium.content_public.browser.WebContents;
@@ -674,8 +675,8 @@
                 builder.append(editMessage);
                 int endIndex = builder.length();
 
-                Object foregroundSpanner = new ForegroundColorSpan(ApiCompatibilityUtils.getColor(
-                        mContext.getResources(), R.color.default_text_color_link));
+                Object foregroundSpanner = new ForegroundColorSpan(
+                        SemanticColorUtils.getDefaultTextColorLink(mContext));
                 builder.setSpan(foregroundSpanner, startIndex, endIndex, 0);
 
                 // The text size in the dropdown is 14dp.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/datareduction/settings/DataReductionSiteBreakdownView.java b/chrome/android/java/src/org/chromium/chrome/browser/datareduction/settings/DataReductionSiteBreakdownView.java
index 4fb66ee..935f3b9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/datareduction/settings/DataReductionSiteBreakdownView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/datareduction/settings/DataReductionSiteBreakdownView.java
@@ -15,9 +15,9 @@
 import android.widget.TableRow;
 import android.widget.TextView;
 
-import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.datareduction.DataReductionProxyUma;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 
 import java.io.Serializable;
 import java.util.Collections;
@@ -302,8 +302,7 @@
             dataSavedView.setText(
                     Formatter.formatFileSize(getContext(), everythingElseDataSavings));
 
-            int textColorLink = ApiCompatibilityUtils.getColor(
-                    getContext().getResources(), R.color.default_text_color_link);
+            int textColorLink = SemanticColorUtils.getDefaultTextColorAccent1(getContext());
 
             hostnameView.setTextColor(textColorLink);
             dataUsedView.setTextColor(textColorLink);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java
index 8a3a2a6d..d913605 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java
@@ -25,7 +25,6 @@
 import androidx.preference.PreferenceFragmentCompat;
 import androidx.preference.PreferenceGroup;
 
-import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.StrictModeContext;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherImpl;
@@ -46,6 +45,7 @@
 import org.chromium.components.browser_ui.settings.SearchUtils;
 import org.chromium.components.browser_ui.settings.SettingsLauncher;
 import org.chromium.components.browser_ui.settings.TextMessagePreference;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.prefs.PrefService;
 import org.chromium.components.signin.base.CoreAccountInfo;
 import org.chromium.components.user_prefs.UserPrefs;
@@ -588,8 +588,8 @@
             getPreferenceScreen().addPreference(mLinkPref);
             return;
         }
-        ForegroundColorSpan colorSpan = new ForegroundColorSpan(
-                ApiCompatibilityUtils.getColor(getResources(), R.color.default_text_color_link));
+        ForegroundColorSpan colorSpan =
+                new ForegroundColorSpan(SemanticColorUtils.getDefaultTextColorLink(getContext()));
         SpannableString title = SpanApplier.applySpans(getString(R.string.manage_passwords_text),
                 new SpanApplier.SpanInfo("<link>", "</link>", colorSpan));
         mLinkPref = new ChromeBasePreference(getStyledContext());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
index bde9e8fa..0e686495 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
@@ -29,6 +29,7 @@
 import org.chromium.chrome.browser.autofill.prefeditor.EditorModel;
 import org.chromium.chrome.browser.autofill.settings.AutofillProfileBridge.DropdownKeyValue;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.payments.BasicCardUtils;
 import org.chromium.components.payments.MethodStrings;
 import org.chromium.components.payments.PaymentFeatureList;
@@ -651,8 +652,8 @@
                 builder.append(editMessage);
                 int endIndex = builder.length();
 
-                Object foregroundSpanner = new ForegroundColorSpan(ApiCompatibilityUtils.getColor(
-                        mContext.getResources(), R.color.default_text_color_link));
+                Object foregroundSpanner = new ForegroundColorSpan(
+                        SemanticColorUtils.getDefaultTextColorLink(mContext));
                 builder.setSpan(foregroundSpanner, startIndex, endIndex, 0);
 
                 // The text size in the dropdown is 14dp.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
index 88f3ee2..13990f0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
@@ -37,6 +37,7 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.components.autofill.EditableOption;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.browser_ui.widget.DualControlLayout;
 import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.components.browser_ui.widget.animation.Interpolators;
@@ -1454,8 +1455,8 @@
                 if (builder.length() > 0) builder.append(labelSeparator);
                 String editMessage = item.getEditMessage();
                 builder.append(editMessage);
-                Object foregroundSpanner = new ForegroundColorSpan(ApiCompatibilityUtils.getColor(
-                        getContext().getResources(), R.color.default_text_color_link));
+                Object foregroundSpanner = new ForegroundColorSpan(
+                        SemanticColorUtils.getDefaultTextColorLink(getContext()));
                 Object sizeSpanner = new AbsoluteSizeSpan(14, true);
                 int startIndex = builder.length() - editMessage.length();
                 builder.setSpan(foregroundSpanner, startIndex, builder.length(), 0);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrarTest.java
index 9e7e3ddd3..8a18cbc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrarTest.java
@@ -16,6 +16,7 @@
 
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.FlakyTest;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
@@ -63,6 +64,7 @@
      */
     @Test
     @MediumTest
+    @FlakyTest(message = "crbug.com/1269017")
     public void testObserveActiveTab() throws Throwable {
         EmbeddedTestServer testServer = mCustomTabActivityTestRule.getTestServer();
         final String windowOpenUrl =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java
index 74374c6..00acf05b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java
@@ -428,6 +428,7 @@
     @Test
     @LargeTest
     @Features.DisableFeatures({ChromeFeatureList.OFFLINE_INDICATOR})
+    @DisabledTest(message = "Disabled while investigating. crbug.com/1280192")
     public void testHidingBrowserControlsPreservesScrollOffset() throws TimeoutException {
         FullscreenManagerTestUtils.disableBrowserOverrides();
         mActivityTestRule.startMainActivityWithURL(SCROLL_OFFSET_TEST_PAGE);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
index af53dac..6052c7d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
@@ -101,6 +101,7 @@
     Bitmap mBitmap;
     OneshotSupplierImpl<TemplateUrlService> mTemplateUrlServiceSupplier;
     WindowAndroid mWindowAndroid;
+    LibraryLoader mOriginalLibraryLoader;
 
     @Before
     public void setUp() {
@@ -114,6 +115,7 @@
         mModel = TestThreadUtils.runOnUiThreadBlockingNoException(
                 () -> new PropertyModel(StatusProperties.ALL_KEYS));
 
+        mOriginalLibraryLoader = LibraryLoader.getInstance();
         doReturn(true).when(mLibraryLoader).isInitialized();
         LibraryLoader.setLibraryLoaderForTesting(mLibraryLoader);
 
@@ -136,6 +138,7 @@
 
     @After
     public void tearDown() {
+        LibraryLoader.setLibraryLoaderForTesting(mOriginalLibraryLoader);
         TestThreadUtils.runOnUiThreadBlocking(() -> { mWindowAndroid.destroy(); });
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java
index 8de75a6..565cac067 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java
@@ -35,7 +35,6 @@
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.RequiresRestart;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.tab.Tab;
@@ -123,8 +122,6 @@
     @Test
     @SmallTest
     @Feature("TabPersistentStore")
-    // TODO(https://crbug.com/1278039): remove @RequiresRestart workaround when culprit is found
-    @RequiresRestart("https://crbug.com/1278039")
     public void testNtpSaveBehavior() {
         when(mNormalTabModel.index()).thenReturn(TabList.INVALID_TAB_INDEX);
         when(mIncognitoTabModel.index()).thenReturn(TabList.INVALID_TAB_INDEX);
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 9848090..4d25d7a 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -4347,6 +4347,9 @@
         <message name="IDS_EXTENSION_PROMPT_WARNING_SOCKET_SPECIFIC_HOSTS" desc="Permission string for access to multiple specific devices on the local network or internet.">
           Exchange data with the devices named: <ph name="HOSTNAMES">$1<ex>foo.example.com bar.example.com</ex></ph>
         </message>
+        <message name="IDS_EXTENSION_PROMPT_WARNING_SPEECH_RECOGNITION" desc="Permission string for speech recognition.">
+          Access your microphone and analyze your speech
+        </message>
         <message name="IDS_EXTENSION_PROMPT_WARNING_START_PAGE_SETTING_OVERRIDE" desc="Permission string for start page override.">
           Change your start page to: <ph name="START_PAGE">$1<ex>start.page.com/start.html</ex></ph>
         </message>
diff --git a/chrome/app/theme/default_100_percent/common/tailored_security_unconsented.png b/chrome/app/theme/default_100_percent/common/tailored_security_unconsented.png
new file mode 100644
index 0000000..6e41efb4
--- /dev/null
+++ b/chrome/app/theme/default_100_percent/common/tailored_security_unconsented.png
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/tailored_security_unconsented.png b/chrome/app/theme/default_200_percent/common/tailored_security_unconsented.png
new file mode 100644
index 0000000..b859de6
--- /dev/null
+++ b/chrome/app/theme/default_200_percent/common/tailored_security_unconsented.png
Binary files differ
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index 92d6a90..7f47399 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -367,6 +367,9 @@
           <structure type="chrome_scaled_image" name="IDR_TRANSLATE_BUTTON_WORDMARK" file="google_chrome/translate_button_wordmark.png" />
         </if>
       </if>
+      <if expr="not is_android">
+        <structure type="chrome_scaled_image" name="IDR_TAILORED_SECURITY_UNCONSENTED" file="common/tailored_security_unconsented.png" />
+      </if>
     </structures>
   </release>
 </grit>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 4109bff..e2b5687 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3005,9 +3005,6 @@
      flag_descriptions::kUseHDRTransferFunctionName,
      flag_descriptions::kUseHDRTransferFunctionDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(display::features::kUseHDRTransferFunction)},
-    {"dark-light-mode", flag_descriptions::kDarkLightTestName,
-     flag_descriptions::kDarkLightTestDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kDarkLightMode)},
     {"vertical-snap", flag_descriptions::kVerticalSnapName,
      flag_descriptions::kVerticalSnapDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::wm::features::kVerticalSnap)},
@@ -3229,6 +3226,9 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if defined(OS_CHROMEOS)
+    {"dark-light-mode", flag_descriptions::kDarkLightTestName,
+     flag_descriptions::kDarkLightTestDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::features::kDarkLightMode)},
     // TODO(b/180051795): remove kOsLinux when lacros-chrome switches to
     // kOsCrOS.
     {"deprecate-low-usage-codecs",
diff --git a/chrome/browser/apps/digital_goods/util.cc b/chrome/browser/apps/digital_goods/util.cc
index 36ae84a..fe9abaa 100644
--- a/chrome/browser/apps/digital_goods/util.cc
+++ b/chrome/browser/apps/digital_goods/util.cc
@@ -20,50 +20,48 @@
   auto* web_contents =
       content::WebContents::FromRenderFrameHost(render_frame_host);
   if (!web_contents)
-    return "";
+    return std::string();
 
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
   if (!web_app::AppBrowserController::IsWebApp(browser)) {
-    return "";
+    return std::string();
   }
 
   auto* profile =
       Profile::FromBrowserContext(render_frame_host->GetBrowserContext());
   if (profile->IsIncognitoProfile()) {
-    return "";
+    return std::string();
   }
 
   auto* apk_web_app_service = ash::ApkWebAppService::Get(profile);
   if (!apk_web_app_service) {
-    return "";
+    return std::string();
   }
 
   absl::optional<std::string> twa_package_name =
       apk_web_app_service->GetPackageNameForWebApp(
-          content::WebContents::FromRenderFrameHost(render_frame_host)
-              ->GetLastCommittedURL());
+          render_frame_host->GetMainFrame()->GetLastCommittedURL());
 
-  return twa_package_name.value_or("");
+  return twa_package_name.value_or(std::string());
 }
 
 std::string GetScope(content::RenderFrameHost* render_frame_host) {
   web_app::WebAppProvider* provider = web_app::WebAppProvider::GetForWebApps(
       Profile::FromBrowserContext(render_frame_host->GetBrowserContext()));
   if (!provider) {
-    return "";
+    return std::string();
   }
 
   const web_app::WebAppRegistrar& registrar = provider->registrar();
   absl::optional<web_app::AppId> app_id = registrar.FindAppWithUrlInScope(
-      content::WebContents::FromRenderFrameHost(render_frame_host)
-          ->GetLastCommittedURL());
+      render_frame_host->GetMainFrame()->GetLastCommittedURL());
   if (!app_id) {
-    return "";
+    return std::string();
   }
 
   GURL scope = registrar.GetAppScope(app_id.value());
   if (!scope.is_valid()) {
-    return "";
+    return std::string();
   }
 
   return scope.spec();
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index aab0ac3c..40183a9 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -1397,14 +1397,15 @@
   TestHelper("testDisplayNoneWebviewLoad", "web_view/shim", NO_TEST_SERVER);
 }
 
-#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS) || \
+    defined(OS_MAC)
 #define MAYBE_Shim_TestDisplayNoneWebviewRemoveChild \
   DISABLED_Shim_TestDisplayNoneWebviewRemoveChild
 #else
 #define MAYBE_Shim_TestDisplayNoneWebviewRemoveChild \
   Shim_TestDisplayNoneWebviewRemoveChild
 #endif
-// Flaky on Windows & Linux: https://crbug.com/1115106.
+// Flaky on most desktop platforms: https://crbug.com/1115106.
 IN_PROC_BROWSER_TEST_F(WebViewTest,
                        MAYBE_Shim_TestDisplayNoneWebviewRemoveChild) {
   TestHelper("testDisplayNoneWebviewRemoveChild",
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
index 4f76032..dda0ad4 100644
--- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
+++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
@@ -98,7 +98,7 @@
   DCHECK(overlay_widget);
   if (!overlay_widget)
     return;
-  overlay_widget->GetContentsView()->RemoveChildView(input_mapping_view_);
+  overlay_widget->GetContentsView()->RemoveChildViewT(input_mapping_view_);
   input_mapping_view_ = nullptr;
 }
 
diff --git a/chrome/browser/ash/arc/screen_capture/arc_screen_capture_session.cc b/chrome/browser/ash/arc/screen_capture/arc_screen_capture_session.cc
index 2d3bef0..7a9cf3b 100644
--- a/chrome/browser/ash/arc/screen_capture/arc_screen_capture_session.cc
+++ b/chrome/browser/ash/arc/screen_capture/arc_screen_capture_session.cc
@@ -184,8 +184,26 @@
   Close();
 }
 
+void ArcScreenCaptureSession::SetOutputBufferDeprecated(
+    mojo::ScopedHandle graphics_buffer,
+    uint32_t stride,
+    SetOutputBufferDeprecatedCallback callback) {
+  // Defined locally to avoid having to add a dependency on drm_fourcc.h
+  constexpr uint64_t DRM_FORMAT_MOD_LINEAR = 0;
+
+  SetOutputBuffer(std::move(graphics_buffer), gfx::BufferFormat::RGBX_8888,
+                  DRM_FORMAT_MOD_LINEAR, stride,
+                  base::BindOnce(
+                      [](base::OnceCallback<void()> callback) {
+                        std::move(callback).Run();
+                      },
+                      std::move(callback)));
+}
+
 void ArcScreenCaptureSession::SetOutputBuffer(
     mojo::ScopedHandle graphics_buffer,
+    gfx::BufferFormat buffer_format,
+    uint64_t buffer_format_modifier,
     uint32_t stride,
     SetOutputBufferCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -203,8 +221,7 @@
 
   gfx::GpuMemoryBufferHandle handle;
   handle.type = gfx::NATIVE_PIXMAP;
-  // Dummy modifier.
-  handle.native_pixmap_handle.modifier = 0;
+  handle.native_pixmap_handle.modifier = buffer_format_modifier;
   base::ScopedPlatformFile platform_file;
   MojoResult mojo_result =
       mojo::UnwrapPlatformFile(std::move(graphics_buffer), &platform_file);
@@ -219,7 +236,7 @@
   std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
       gpu::GpuMemoryBufferImplNativePixmap::CreateFromHandle(
           client_native_pixmap_factory_.get(), std::move(handle), size_,
-          gfx::BufferFormat::RGBX_8888, gfx::BufferUsage::SCANOUT,
+          buffer_format, gfx::BufferUsage::SCANOUT,
           gpu::GpuMemoryBufferImpl::DestructionCallback());
   if (!gpu_memory_buffer) {
     LOG(ERROR) << "Failed creating GpuMemoryBuffer";
diff --git a/chrome/browser/ash/arc/screen_capture/arc_screen_capture_session.h b/chrome/browser/ash/arc/screen_capture/arc_screen_capture_session.h
index 1b60668..ed95c023 100644
--- a/chrome/browser/ash/arc/screen_capture/arc_screen_capture_session.h
+++ b/chrome/browser/ash/arc/screen_capture/arc_screen_capture_session.h
@@ -56,7 +56,13 @@
   ArcScreenCaptureSession& operator=(const ArcScreenCaptureSession&) = delete;
 
   // Implements mojo::ScreenCaptureSession interface.
+  void SetOutputBufferDeprecated(
+      mojo::ScopedHandle graphics_buffer,
+      uint32_t stride,
+      SetOutputBufferDeprecatedCallback callback) override;
   void SetOutputBuffer(mojo::ScopedHandle graphics_buffer,
+                       gfx::BufferFormat buffer_format,
+                       uint64_t buffer_format_modifier,
                        uint32_t stride,
                        SetOutputBufferCallback callback) override;
 
diff --git a/chrome/browser/ash/borealis/borealis_window_manager.cc b/chrome/browser/ash/borealis/borealis_window_manager.cc
index c780d44..c45fcce 100644
--- a/chrome/browser/ash/borealis/borealis_window_manager.cc
+++ b/chrome/browser/ash/borealis/borealis_window_manager.cc
@@ -54,7 +54,7 @@
            ->GetRegisteredApps(guest_os::GuestOsRegistryService::VmType::
                                    ApplicationList_VmType_BOREALIS)) {
     absl::optional<int> app_id = GetBorealisAppId(item.second.Exec());
-    if (app_id && app_id.value() == borealis_id) {
+    if (app_id && app_id.value() == static_cast<int>(borealis_id)) {
       return item.first;
     }
   }
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator.cc b/chrome/browser/ash/crosapi/browser_data_migrator.cc
index f2a01cf3..425ed13 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator.cc
+++ b/chrome/browser/ash/crosapi/browser_data_migrator.cc
@@ -51,6 +51,7 @@
     kTmpDir,
     "Cache",
     "Code Cache",
+    "crash",
     "blob_storage",
     "GCache",
     "data_reduction_proxy_leveldb",
diff --git a/chrome/browser/ash/input_method/native_input_method_engine.cc b/chrome/browser/ash/input_method/native_input_method_engine.cc
index 3aec826..d611e73 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine.cc
+++ b/chrome/browser/ash/input_method/native_input_method_engine.cc
@@ -49,7 +49,6 @@
   return ui::IMEBridge::Get()->GetInputContextHandler();
 }
 
-// TODO(https://crbug/1166082): Use a separate InputMethodEngine for rule-based.
 bool ShouldRouteToRuleBasedEngine(const std::string& engine_id) {
   return base::StartsWith(engine_id, "vkd_", base::CompareCase::SENSITIVE);
 }
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
index 0d7d7c8c..d899fe9 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -114,10 +114,8 @@
       storage_partition_(nullptr),
       omnibox_triggered_feature_service_(
           std::make_unique<OmniboxTriggeredFeatureService>()) {
-#if !defined(OS_ANDROID)
   pedal_provider_ = std::make_unique<OmniboxPedalProvider>(
       *this, GetPedalImplementations(IsOffTheRecord(), false));
-#endif
 }
 
 ChromeAutocompleteProviderClient::~ChromeAutocompleteProviderClient() = default;
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index dfd710b8..90760c852 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -1932,11 +1932,6 @@
 bool ChromeContentBrowserClient::ShouldTryToUseExistingProcessHost(
     content::BrowserContext* browser_context,
     const GURL& url) {
-  // Only proceed for valid URLs.
-  // TODO(creis): This may no longer be needed.
-  if (!url.is_valid())
-    return false;
-
   // Top Chrome WebUI should try to share a RenderProcessHost with other
   // existing Top Chrome WebUI.
   if (IsTopChromeWebUIURL(url))
diff --git a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_apitest.cc b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_apitest.cc
index 1b51305f7..edf6ca1e 100644
--- a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_apitest.cc
@@ -39,8 +39,7 @@
     ::testing::Values(speech::SpeechRecognitionType::kOnDevice));
 
 IN_PROC_BROWSER_TEST_P(SpeechRecognitionPrivateApiTest, Simple) {
-  ASSERT_TRUE(RunExtensionTest("speech/speech_recognition_private/simple"))
-      << message_;
+  ASSERT_TRUE(RunSpeechRecognitionPrivateTest("simple")) << message_;
 }
 
 // An end-to-end test that starts speech recognition and ensures that the
@@ -49,8 +48,7 @@
 IN_PROC_BROWSER_TEST_P(SpeechRecognitionPrivateApiTest, OnStart) {
   SetCustomArg(api::speech_recognition_private::ToString(
       speech::SpeechRecognitionTypeToApiType(GetParam())));
-  ASSERT_TRUE(RunExtensionTest("speech/speech_recognition_private/on_start"))
-      << message_;
+  ASSERT_TRUE(RunSpeechRecognitionPrivateTest("on_start")) << message_;
 }
 
 // An end-to-end test that starts speech recognition, waits for results, then
@@ -66,8 +64,7 @@
 
   // Load the extension and wait for speech recognition to start.
   ResultCatcher result_catcher;
-  const Extension* extension = LoadExtension(test_data_dir_.AppendASCII(
-      "speech/speech_recognition_private/start_result_stop"));
+  const Extension* extension = LoadExtensionAsComponent("start_result_stop");
   ASSERT_TRUE(extension);
   ASSERT_TRUE(start_listener.WaitUntilSatisfied());
 
@@ -93,8 +90,7 @@
   ExtensionTestMessageListener start_listener("Started", false);
 
   ResultCatcher result_catcher;
-  const Extension* extension = LoadExtension(test_data_dir_.AppendASCII(
-      "speech/speech_recognition_private/start_error_stop"));
+  const Extension* extension = LoadExtensionAsComponent("start_error_stop");
   ASSERT_TRUE(extension);
   ASSERT_TRUE(start_listener.WaitUntilSatisfied());
 
diff --git a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.cc b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.cc
index 9d52e29..ef43600 100644
--- a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.cc
+++ b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.cc
@@ -4,13 +4,16 @@
 
 #include "chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.h"
 
-#include "components/version_info/channel.h"
 #include "content/public/test/fake_speech_recognition_manager.h"
 
+namespace {
+const char kBasePath[] = "speech/speech_recognition_private/";
+}  // namespace
+
 namespace extensions {
 
 SpeechRecognitionPrivateBaseTest::SpeechRecognitionPrivateBaseTest()
-    : test_helper_(GetParam()), current_channel_(version_info::Channel::DEV) {}
+    : test_helper_(GetParam()) {}
 
 SpeechRecognitionPrivateBaseTest::~SpeechRecognitionPrivateBaseTest() = default;
 
@@ -37,6 +40,19 @@
   ExtensionApiTest::TearDownOnMainThread();
 }
 
+bool SpeechRecognitionPrivateBaseTest::RunSpeechRecognitionPrivateTest(
+    const std::string& dir_name) {
+  const std::string path = kBasePath + dir_name;
+  return RunExtensionTest(path.c_str(), {}, {.load_as_component = true});
+}
+
+const Extension* SpeechRecognitionPrivateBaseTest::LoadExtensionAsComponent(
+    const std::string& dir_name) {
+  const std::string path = kBasePath + dir_name;
+  return LoadExtension(test_data_dir_.AppendASCII(path.c_str()),
+                       {.load_as_component = true});
+}
+
 void SpeechRecognitionPrivateBaseTest::WaitForRecognitionStarted() {
   test_helper_.WaitForRecognitionStarted();
 }
diff --git a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.h b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.h
index 0e3952b..14ecde9 100644
--- a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.h
+++ b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.h
@@ -12,7 +12,6 @@
 #include "chrome/browser/speech/speech_recognition_constants.h"
 #include "chrome/browser/speech/speech_recognition_test_helper.h"
 #include "content/public/test/browser_test.h"
-#include "extensions/common/features/feature_channel.h"
 
 namespace extensions {
 
@@ -42,6 +41,13 @@
   void SetUpOnMainThread() override;
   void TearDownOnMainThread() override;
 
+  // Runs a speech recognition private test.
+  bool RunSpeechRecognitionPrivateTest(const std::string& dir_name);
+  // Loads a test extension from the speech recognition private directory as a
+  // component extension, which is required to use the speech recognition
+  // private API.
+  const Extension* LoadExtensionAsComponent(const std::string& dir_name);
+
   // Routers to SpeechRecognitionTestHelper methods.
   void WaitForRecognitionStarted();
   void WaitForRecognitionStopped();
@@ -52,10 +58,6 @@
 
  private:
   SpeechRecognitionTestHelper test_helper_;
-  // Needed because the speechRecognitionPrivate API is restricted to the dev
-  // channel during development.
-  // TODO(crbug.com/1220107): Remove this when it's launched to stable.
-  extensions::ScopedCurrentChannel current_channel_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
diff --git a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager_browsertest.cc b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager_browsertest.cc
index 8e6450d..d15756e 100644
--- a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager_browsertest.cc
+++ b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager_browsertest.cc
@@ -165,9 +165,7 @@
 // and received and processed in an extension.
 IN_PROC_BROWSER_TEST_P(SpeechRecognitionPrivateManagerTest,
                        DispatchOnStopEvent) {
-  ASSERT_TRUE(
-      RunExtensionTest("speech/speech_recognition_private/onstop_event"))
-      << message_;
+  ASSERT_TRUE(RunSpeechRecognitionPrivateTest("onstop_event")) << message_;
 
   const char* kExtensionIdAndIncorrectClientId =
       "egfdjlfmgnehecnclamagfafdccgfndp.0";
@@ -196,9 +194,7 @@
 // and received and processed in an extension.
 IN_PROC_BROWSER_TEST_P(SpeechRecognitionPrivateManagerTest,
                        DispatchOnResultEvent) {
-  ASSERT_TRUE(
-      RunExtensionTest("speech/speech_recognition_private/onresult_event"))
-      << message_;
+  ASSERT_TRUE(RunSpeechRecognitionPrivateTest("onresult_event")) << message_;
 
   const char* kFirstClient = "egfdjlfmgnehecnclamagfafdccgfndp.1";
   const char* kSecondClient = "egfdjlfmgnehecnclamagfafdccgfndp.2";
@@ -237,8 +233,7 @@
   ResultCatcher result_catcher;
   ExtensionTestMessageListener listener("Proceed", false);
 
-  const Extension* extension = LoadExtension(test_data_dir_.AppendASCII(
-      "speech/speech_recognition_private/onerror_event"));
+  const Extension* extension = LoadExtensionAsComponent("onerror_event");
   ASSERT_TRUE(extension);
   ASSERT_TRUE(listener.WaitUntilSatisfied());
 
diff --git a/chrome/browser/client_hints/client_hints_browsertest.cc b/chrome/browser/client_hints/client_hints_browsertest.cc
index 7f31752..54ab2b4 100644
--- a/chrome/browser/client_hints/client_hints_browsertest.cc
+++ b/chrome/browser/client_hints/client_hints_browsertest.cc
@@ -4321,6 +4321,27 @@
     ASSERT_TRUE(ua_ch_result.find(i) == std::string::npos);
   }
 }
+IN_PROC_BROWSER_TEST_F(GreaseEnterprisePolicyTest,
+                       GreaseEnterprisePolicyDynamicRefreshTest) {
+  const GURL gurl = accept_ch_url();
+  // Reset the policy that was already set to false in the setup, then see if
+  // the change is reflected in the sec-ch-ua header without requiring a browser
+  // restart.
+  policy::PolicyMap policies;
+  SetPolicy(&policies, policy::key::kUserAgentClientHintsGREASEUpdateEnabled,
+            absl::optional<base::Value>(true));
+  provider_.UpdateChromePolicy(policies);
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), gurl));
+  std::string ua_ch_result = main_frame_ua_observed();
+  bool seen_updated = false;
+  std::vector<char> updated_grease_chars = {'(', ':', '-', '.',
+                                            '/', ')', '?', '_'};
+  for (auto c : updated_grease_chars) {
+    seen_updated = seen_updated || (ua_ch_result.find(c) != std::string::npos);
+  }
+  ASSERT_TRUE(seen_updated);
+}
+
 // Tests that the Sec-CH-UA-Reduced client hint gets cleared on a redirect if
 // the response doesn't contain the hint in the Accept-CH header.
 class RedirectUaReducedOriginTrialBrowserTest : public InProcessBrowserTest {
diff --git a/chrome/browser/client_hints/client_hints_factory.cc b/chrome/browser/client_hints/client_hints_factory.cc
index c9133e3..b551dac6 100644
--- a/chrome/browser/client_hints/client_hints_factory.cc
+++ b/chrome/browser/client_hints/client_hints_factory.cc
@@ -50,7 +50,7 @@
       HostContentSettingsMapFactory::GetForProfile(context),
       CookieSettingsFactory::GetForProfile(
           Profile::FromBrowserContext(context)),
-      embedder_support::GetUserAgentMetadata(g_browser_process->local_state()));
+      g_browser_process->local_state());
 }
 
 content::BrowserContext* ClientHintsFactory::GetBrowserContextToUse(
diff --git a/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/scene/ReactionLayout.java b/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/scene/ReactionLayout.java
index b861361..a3da980 100644
--- a/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/scene/ReactionLayout.java
+++ b/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/scene/ReactionLayout.java
@@ -215,6 +215,8 @@
         mCopyButton.setOnClickListener(view -> {
             if (mSceneEditorDelegate.canAddReaction()) {
                 mSceneEditorDelegate.duplicateReaction(ReactionLayout.this);
+            } else {
+                mSceneEditorDelegate.showMaxReactionsReachedToast();
             }
         });
     }
diff --git a/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/scene/SceneCoordinator.java b/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/scene/SceneCoordinator.java
index 42bf8bd..38dbd9e 100644
--- a/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/scene/SceneCoordinator.java
+++ b/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/scene/SceneCoordinator.java
@@ -23,6 +23,7 @@
 import org.chromium.components.content_creation.reactions.ReactionMetadata;
 import org.chromium.ui.LayoutInflaterUtils;
 import org.chromium.ui.base.ViewUtils;
+import org.chromium.ui.widget.Toast;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -51,6 +52,7 @@
     private ReactionLayout mActiveReaction;
     private RelativeLayout mSceneBackground;
     private ImageView mScreenshotView;
+    private Toast mToast;
 
     private int mNbReactionsAdded;
     private int mNbTypeChange;
@@ -306,6 +308,18 @@
     }
 
     @Override
+    public void showMaxReactionsReachedToast() {
+        if (mToast != null) {
+            mToast.cancel();
+        }
+        mToast = Toast.makeText(mActivity,
+                mActivity.getString(R.string.lightweight_reactions_error_max_reactions_reached,
+                        MAX_REACTION_COUNT),
+                Toast.LENGTH_SHORT);
+        mToast.show();
+    }
+
+    @Override
     public void removeReaction(ReactionLayout reactionLayout) {
         ++mNbDelete;
         markActiveStatus(reactionLayout, false);
@@ -346,7 +360,11 @@
         if (mActiveReaction != null) {
             replaceActiveReaction(reaction);
         } else {
-            addReactionInDefaultLocation(reaction);
+            if (canAddReaction()) {
+                addReactionInDefaultLocation(reaction);
+            } else {
+                showMaxReactionsReachedToast();
+            }
         }
     }
 
diff --git a/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/scene/SceneEditorDelegate.java b/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/scene/SceneEditorDelegate.java
index 94ba4ae..7b7fdec1 100644
--- a/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/scene/SceneEditorDelegate.java
+++ b/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/scene/SceneEditorDelegate.java
@@ -19,6 +19,12 @@
     void duplicateReaction(ReactionLayout reactionLayout);
 
     /**
+     * Shows the {@link org.chromium.ui.widget.Toast} that indicates the max number of reactions on
+     * the scene has been reached.
+     */
+    void showMaxReactionsReachedToast();
+
+    /**
      * Removes the given {@link ReactionLayout} from the scene.
      */
     void removeReaction(ReactionLayout reactionLayout);
diff --git a/chrome/browser/devtools/devtools_browsertest.cc b/chrome/browser/devtools/devtools_browsertest.cc
index fcf104d..36cfae0 100644
--- a/chrome/browser/devtools/devtools_browsertest.cc
+++ b/chrome/browser/devtools/devtools_browsertest.cc
@@ -2736,6 +2736,30 @@
   CloseDevToolsWindow();
 }
 
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, IsDeveloperModeTrueHistogram) {
+  browser()->profile()->GetPrefs()->SetBoolean(
+      prefs::kExtensionsUIDeveloperMode, true);
+  base::HistogramTester histograms;
+  const char* histogram_name = "Extensions.DevTools.UserIsInDeveloperMode";
+
+  LoadExtension("devtools_extension");
+  RunTest("waitForTestResultsInConsole", kArbitraryPage);
+
+  histograms.ExpectBucketCount(histogram_name, true, 2);
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, IsDeveloperModeFalseHistogram) {
+  browser()->profile()->GetPrefs()->SetBoolean(
+      prefs::kExtensionsUIDeveloperMode, false);
+  base::HistogramTester histograms;
+  const char* histogram_name = "Extensions.DevTools.UserIsInDeveloperMode";
+
+  LoadExtension("devtools_extension");
+  RunTest("waitForTestResultsInConsole", kArbitraryPage);
+
+  histograms.ExpectBucketCount(histogram_name, false, 2);
+}
+
 namespace {
 
 class DevToolsLocalizationTest : public DevToolsTest {
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc
index 8042c95..7c61157 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.cc
+++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -1509,6 +1509,7 @@
     return;
 
   base::ListValue results;
+  bool have_user_installed_devtools_extensions = false;
   for (const scoped_refptr<const extensions::Extension>& extension :
        registry->enabled_extensions()) {
     if (extensions::chrome_manifest_urls::GetDevToolsPage(extension.get())
@@ -1537,6 +1538,18 @@
         extension->permissions_data()->HasAPIPermission(
             extensions::mojom::APIPermissionID::kExperimental));
     results.Append(std::move(extension_info));
+
+    if (!(extensions::Manifest::IsPolicyLocation(extension->location()) ||
+          extensions::Manifest::IsComponentLocation(extension->location()))) {
+      have_user_installed_devtools_extensions = true;
+    }
+  }
+
+  if (have_user_installed_devtools_extensions) {
+    bool is_developer_mode =
+        profile_->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode);
+    base::UmaHistogramBoolean("Extensions.DevTools.UserIsInDeveloperMode",
+                              is_developer_mode);
   }
 
   CallClientMethod("DevToolsAPI", "addExtensions", std::move(results));
diff --git a/chrome/browser/enterprise/signals/context_info_fetcher.cc b/chrome/browser/enterprise/signals/context_info_fetcher.cc
index 88d4a01..13a6e22 100644
--- a/chrome/browser/enterprise/signals/context_info_fetcher.cc
+++ b/chrome/browser/enterprise/signals/context_info_fetcher.cc
@@ -27,8 +27,9 @@
 #include "content/public/browser/site_isolation_policy.h"
 #include "device_management_backend.pb.h"
 
-#if defined(OS_LINUX)
+#if defined(OS_POSIX)
 #include "net/dns/public/resolv_reader.h"
+#include "net/dns/public/scoped_res_state.h"
 #endif
 
 #if defined(OS_MAC)
@@ -298,22 +299,19 @@
 
 std::vector<std::string> ContextInfoFetcher::GetDnsServers() {
   std::vector<std::string> dns_addresses;
-#if defined(OS_LINUX)
-  std::vector<net::IPEndPoint> nameservers;
-  std::unique_ptr<net::ResolvReader> resolv_reader =
-      std::make_unique<net::ResolvReader>();
-
-  std::unique_ptr<struct __res_state> res = resolv_reader->GetResState();
+#if defined(OS_POSIX)
+  std::unique_ptr<net::ScopedResState> res = net::ResolvReader().GetResState();
   if (res) {
-    if (net::GetNameservers(*res.get()).has_value())
-      nameservers = net::GetNameservers(*res.get()).value();
-    resolv_reader->CloseResState(res.get());
-    // If any name server is 0.0.0.0, assume the configuration is invalid.
-    for (const net::IPEndPoint& nameserver : nameservers) {
-      if (nameserver.address().IsZero())
-        return std::vector<std::string>();
-      else
-        dns_addresses.push_back(nameserver.ToString());
+    absl::optional<std::vector<net::IPEndPoint>> nameservers =
+        net::GetNameservers(res->state());
+    if (nameservers) {
+      // If any name server is 0.0.0.0, assume the configuration is invalid.
+      for (const net::IPEndPoint& nameserver : nameservers.value()) {
+        if (nameserver.address().IsZero())
+          return std::vector<std::string>();
+        else
+          dns_addresses.push_back(nameserver.ToString());
+      }
     }
   }
 #elif defined(OS_WIN)
diff --git a/chrome/browser/extensions/content_verifier_hash_fetch_behavior_browsertest.cc b/chrome/browser/extensions/content_verifier_hash_fetch_behavior_browsertest.cc
index b1ff991..8803ce4 100644
--- a/chrome/browser/extensions/content_verifier_hash_fetch_behavior_browsertest.cc
+++ b/chrome/browser/extensions/content_verifier_hash_fetch_behavior_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/test/bind.h"
+#include "build/build_config.h"
 #include "chrome/browser/extensions/browsertest_util.h"
 #include "chrome/browser/extensions/chrome_content_verifier_delegate.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
@@ -481,8 +482,16 @@
 
 // Tests that tampering a resource that will be requested by the extension and
 // tampering computed_hashes.json will always disable the extension.
+// TODO(crbug.com/1278994): Flaky.
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#define MAYBE_TamperRequestedResourceTamperComputedHashes \
+  DISABLED_TamperRequestedResourceTamperComputedHashes
+#else
+#define MAYBE_TamperRequestedResourceTamperComputedHashes \
+  TamperRequestedResourceTamperComputedHashes
+#endif
 IN_PROC_BROWSER_TEST_P(ContentVerifierHashTest,
-                       TamperRequestedResourceTamperComputedHashes) {
+                       MAYBE_TamperRequestedResourceTamperComputedHashes) {
   ASSERT_TRUE(InstallDefaultResourceExtension());
 
   DisableExtension();
@@ -784,9 +793,17 @@
 // Tests the behavior of loading a default resource extension with tampering
 // an extension resource that is not requested by default and tampering
 // computed_hashes.json.
+// TODO(crbug.com/1279323): Flaky.
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#define MAYBE_DefaultRequestExtensionTamperNotRequestedResourceTamperComputedHashes \
+  DISABLED_DefaultRequestExtensionTamperNotRequestedResourceTamperComputedHashes
+#else
+#define MAYBE_DefaultRequestExtensionTamperNotRequestedResourceTamperComputedHashes \
+  DefaultRequestExtensionTamperNotRequestedResourceTamperComputedHashes
+#endif
 IN_PROC_BROWSER_TEST_P(
     ContentVerifierHashTest,
-    DefaultRequestExtensionTamperNotRequestedResourceTamperComputedHashes) {
+    MAYBE_DefaultRequestExtensionTamperNotRequestedResourceTamperComputedHashes) {
   ASSERT_TRUE(InstallDefaultResourceExtension());
 
   DisableExtension();
diff --git a/chrome/browser/incognito/BUILD.gn b/chrome/browser/incognito/BUILD.gn
index 60a2a9d..a3a0e18 100644
--- a/chrome/browser/incognito/BUILD.gn
+++ b/chrome/browser/incognito/BUILD.gn
@@ -27,6 +27,7 @@
     "//chrome/browser/ui/android/strings:ui_strings_grd",
     "//chrome/browser/util:java",
     "//components/browser_ui/settings/android:java",
+    "//components/browser_ui/styles/android:java",
     "//components/device_reauth:device_reauth_java_enums",
     "//third_party/android_deps:dagger_java",
     "//third_party/android_deps:javax_inject_javax_inject_java",
diff --git a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthSettingUtils.java b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthSettingUtils.java
index fe1d8c9..4c2550bd 100644
--- a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthSettingUtils.java
+++ b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthSettingUtils.java
@@ -16,9 +16,9 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.browser.incognito.R;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.ui.text.SpanApplier;
 
 /**
@@ -77,8 +77,7 @@
     // click behaviour is dependent on {@link IncognitoReauthSettingSwitchPreference} so
     // need to refactor that as well.
     private static SpannableString buildLinkToAndroidScreenLockSettings(Activity activity) {
-        int color = ApiCompatibilityUtils.getColor(
-                activity.getResources(), R.color.default_text_color_link);
+        int color = SemanticColorUtils.getDefaultTextColorLink(activity);
         ForegroundColorSpan linkSpan = new ForegroundColorSpan(color);
 
         return SpanApplier.applySpans(
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager.cc b/chrome/browser/optimization_guide/prediction/prediction_manager.cc
index 9c80c0c7..d53fb10 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_manager.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_manager.cc
@@ -417,14 +417,18 @@
   std::vector<proto::ModelInfo> models_info = std::vector<proto::ModelInfo>();
 
   proto::ModelInfo base_model_info;
-  base_model_info.add_supported_model_types(proto::MODEL_TYPE_DECISION_TREE);
   if (features::IsModelDownloadingEnabled()) {
     // TODO(crbug/1204614): Remove v2.3* and 2.4 when server supports 2.7.
-    base_model_info.add_supported_model_types(proto::MODEL_TYPE_TFLITE_2_3_0);
-    base_model_info.add_supported_model_types(proto::MODEL_TYPE_TFLITE_2_3_0_1);
-    base_model_info.add_supported_model_types(proto::MODEL_TYPE_TFLITE_2_4);
-    base_model_info.add_supported_model_types(proto::MODEL_TYPE_TFLITE_2_7);
-    base_model_info.add_supported_model_types(proto::MODEL_TYPE_TFLITE_2_8);
+    base_model_info.add_supported_model_engine_versions(
+        proto::MODEL_ENGINE_VERSION_TFLITE_2_3_0);
+    base_model_info.add_supported_model_engine_versions(
+        proto::MODEL_ENGINE_VERSION_TFLITE_2_3_0_1);
+    base_model_info.add_supported_model_engine_versions(
+        proto::MODEL_ENGINE_VERSION_TFLITE_2_4);
+    base_model_info.add_supported_model_engine_versions(
+        proto::MODEL_ENGINE_VERSION_TFLITE_2_7);
+    base_model_info.add_supported_model_engine_versions(
+        proto::MODEL_ENGINE_VERSION_TFLITE_2_8);
   }
 
   std::string debug_msg;
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
index 83c5ea7..bfa5f38 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
@@ -113,8 +113,9 @@
   model_info->add_supported_host_model_features("agg1");
   model_info->set_optimization_target(
       optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-  model_info->add_supported_model_types(
-      optimization_guide::proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      optimization_guide::proto::ModelEngineVersion::
+          MODEL_ENGINE_VERSION_DECISION_TREE);
   return prediction_model;
 }
 
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc b/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc
index ad8ea1db..28d2348 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc
@@ -69,8 +69,8 @@
   model_info->add_supported_host_model_features("host_feat1");
   model_info->set_optimization_target(
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   if (output_model_as_download_url) {
     prediction_model.mutable_model()->set_download_url(
         "https://example.com/model");
@@ -255,8 +255,9 @@
   bool ValidateModelsInfoForFetch(
       const std::vector<proto::ModelInfo>& models_request_info) {
     for (const auto& model_info : models_request_info) {
-      if (model_info.supported_model_types_size() == 0 ||
-          !proto::ModelType_IsValid(model_info.supported_model_types(0))) {
+      if (model_info.supported_model_engine_versions_size() == 0 ||
+          !proto::ModelEngineVersion_IsValid(
+              model_info.supported_model_engine_versions(0))) {
         return false;
       }
       if (!model_info.has_optimization_target() ||
diff --git a/chrome/browser/password_manager/android/password_store_android_backend.cc b/chrome/browser/password_manager/android/password_store_android_backend.cc
index 5e3221a6..3f78cd3 100644
--- a/chrome/browser/password_manager/android/password_store_android_backend.cc
+++ b/chrome/browser/password_manager/android/password_store_android_backend.cc
@@ -129,30 +129,23 @@
 
 }  // namespace
 
-PasswordStoreAndroidBackend::JobReturnHandler::JobReturnHandler() = default;
+PasswordStoreAndroidBackend::MetricsRecorder::MetricsRecorder() = default;
 
-PasswordStoreAndroidBackend::JobReturnHandler::JobReturnHandler(
-    LoginsOrErrorReply callback,
+PasswordStoreAndroidBackend::MetricsRecorder::MetricsRecorder(
     MetricInfix metric_infix)
-    : success_callback_(std::move(callback)),
-      metric_infix_(std::move(metric_infix)) {}
+    : metric_infix_(std::move(metric_infix)) {}
 
-PasswordStoreAndroidBackend::JobReturnHandler::JobReturnHandler(
-    PasswordStoreChangeListReply callback,
-    MetricInfix metric_infix)
-    : success_callback_(std::move(callback)),
-      metric_infix_(std::move(metric_infix)) {}
+PasswordStoreAndroidBackend::MetricsRecorder::MetricsRecorder(
+    MetricsRecorder&&) = default;
 
-PasswordStoreAndroidBackend::JobReturnHandler::JobReturnHandler(
-    JobReturnHandler&&) = default;
-PasswordStoreAndroidBackend::JobReturnHandler&
+PasswordStoreAndroidBackend::MetricsRecorder&
+PasswordStoreAndroidBackend::MetricsRecorder::MetricsRecorder::operator=(
+    MetricsRecorder&&) = default;
 
-PasswordStoreAndroidBackend::JobReturnHandler::JobReturnHandler::operator=(
-    JobReturnHandler&&) = default;
+PasswordStoreAndroidBackend::MetricsRecorder::~MetricsRecorder() = default;
 
-PasswordStoreAndroidBackend::JobReturnHandler::~JobReturnHandler() = default;
-
-void PasswordStoreAndroidBackend::JobReturnHandler::RecordMetrics(
+void PasswordStoreAndroidBackend::MetricsRecorder::RecordMetrics(
+    bool success,
     absl::optional<AndroidBackendError> error) const {
   auto BuildMetricName = [this](base::StringPiece suffix) {
     return base::StrCat({"PasswordManager.PasswordStoreAndroidBackend.",
@@ -160,11 +153,12 @@
   };
   base::TimeDelta duration = base::Time::Now() - start_;
   base::UmaHistogramMediumTimes(BuildMetricName("Latency"), duration);
-  base::UmaHistogramBoolean(BuildMetricName("Success"), !error.has_value());
+  base::UmaHistogramBoolean(BuildMetricName("Success"), success);
   if (!error.has_value())
     return;
 
-  // In case of error, we report additional metrics.
+  DCHECK(!success);
+  // In case of AndroidBackend error, we report additional metrics.
   base::UmaHistogramEnumeration(
       "PasswordManager.PasswordStoreAndroidBackend.ErrorCode",
       error.value().type);
@@ -183,6 +177,34 @@
   }
 }
 
+PasswordStoreAndroidBackend::JobReturnHandler::JobReturnHandler() = default;
+
+PasswordStoreAndroidBackend::JobReturnHandler::JobReturnHandler(
+    LoginsOrErrorReply callback,
+    MetricsRecorder metrics_recorder)
+    : success_callback_(std::move(callback)),
+      metrics_recorder_(std::move(metrics_recorder)) {}
+
+PasswordStoreAndroidBackend::JobReturnHandler::JobReturnHandler(
+    PasswordStoreChangeListReply callback,
+    MetricsRecorder metrics_recorder)
+    : success_callback_(std::move(callback)),
+      metrics_recorder_(std::move(metrics_recorder)) {}
+
+PasswordStoreAndroidBackend::JobReturnHandler::JobReturnHandler(
+    JobReturnHandler&&) = default;
+PasswordStoreAndroidBackend::JobReturnHandler&
+
+PasswordStoreAndroidBackend::JobReturnHandler::JobReturnHandler::operator=(
+    JobReturnHandler&&) = default;
+
+PasswordStoreAndroidBackend::JobReturnHandler::~JobReturnHandler() = default;
+
+void PasswordStoreAndroidBackend::JobReturnHandler::RecordMetrics(
+    absl::optional<AndroidBackendError> error) const {
+  metrics_recorder_.RecordMetrics(!error.has_value(), std::move(error));
+}
+
 PasswordStoreAndroidBackend::SyncModelTypeControllerDelegate::
     SyncModelTypeControllerDelegate(PasswordStoreAndroidBackendBridge* bridge)
     : bridge_(bridge) {
@@ -290,18 +312,18 @@
   JobId job_id = bridge_->GetAllLogins();
   QueueNewJob(job_id, JobReturnHandler(
                           std::move(callback),
-                          JobReturnHandler::MetricInfix("GetAllLoginsAsync")));
+                          MetricsRecorder(MetricInfix("GetAllLoginsAsync"))));
 }
 
 void PasswordStoreAndroidBackend::GetAutofillableLoginsAsync(
     LoginsOrErrorReply callback) {
   JobId job_id = bridge_->GetAutofillableLogins();
-  QueueNewJob(job_id, JobReturnHandler(std::move(callback),
-                                       JobReturnHandler::MetricInfix(
-                                           "GetAutofillableLoginsAsync")));
+  QueueNewJob(job_id,
+              JobReturnHandler(
+                  std::move(callback),
+                  MetricsRecorder(MetricInfix("GetAutofillableLoginsAsync"))));
 }
 
-// TODO(https://crbug.com/1229655): Replace LoginsReply with LoginsOrErrorReply.
 void PasswordStoreAndroidBackend::FillMatchingLoginsAsync(
     LoginsReply callback,
     bool include_psl,
@@ -311,34 +333,20 @@
     return;
   }
 
-  LoginsOrErrorReply invoke_noerror_callback = base::BindOnce(
-      [](LoginsReply no_error_callback, LoginsResultOrError result) {
-        // `result` is the LoginsResult returned by `JoinRetrievedLogins`.
-        DCHECK(absl::holds_alternative<LoginsResult>(result));
-        std::move(no_error_callback)
-            .Run(std::move(absl::get<LoginsResult>(result)));
-      },
-      std::move(callback));
+  // Record FillMatchingLoginsAsync metrics prior to invoking |callback|.
+  LoginsOrErrorReply record_metrics_and_reply =
+      ReportMetricsAndInvokeCallbackForLoginsRetrieval(
+          MetricInfix("FillMatchingLoginsAsync"), std::move(callback));
 
-  // TODO(https://crbug.com/1229655): Don't use the JobHandler just because it
-  // contains a metrics recorder. Generalize the metrics recording instead!
-  LoginsReply record_metrics_and_reply = base::BindOnce(
-      [](JobReturnHandler handler, LoginsResult logins) {
-        DCHECK(handler.Holds<LoginsOrErrorReply>());
-        handler.RecordMetrics(/*error=*/absl::nullopt);
-        std::move(handler).Get<LoginsOrErrorReply>().Run(std::move(logins));
-      },
-      JobReturnHandler(
-          std::move(invoke_noerror_callback),
-          JobReturnHandler::MetricInfix("FillMatchingLoginsAsync")));
-
+  // Create a barrier callback that aggregates results of a multiple
+  // calls to GetLoginsAsync.
   auto barrier_callback = base::BarrierCallback<LoginsResult>(
       forms.size(), base::BindOnce(&JoinRetrievedLogins)
                         .Then(std::move(record_metrics_and_reply)));
 
-  // Create and run a callbacks chain that retrieves the logins.
+  // Create and run a callbacks chain that retrieves logins and invokes
+  // |barrier_callback| afterwards.
   base::RepeatingClosure callbacks_chain = base::DoNothing();
-
   for (const PasswordFormDigest& form : forms) {
     callbacks_chain = base::BindRepeating(
         &PasswordStoreAndroidBackend::GetLoginsAsync,
@@ -354,7 +362,7 @@
   JobId job_id = bridge_->AddLogin(form);
   QueueNewJob(job_id,
               JobReturnHandler(std::move(callback),
-                               JobReturnHandler::MetricInfix("AddLoginAsync")));
+                               MetricsRecorder(MetricInfix("AddLoginAsync"))));
 }
 
 void PasswordStoreAndroidBackend::UpdateLoginAsync(
@@ -363,7 +371,7 @@
   JobId job_id = bridge_->UpdateLogin(form);
   QueueNewJob(job_id, JobReturnHandler(
                           std::move(callback),
-                          JobReturnHandler::MetricInfix("UpdateLoginAsync")));
+                          MetricsRecorder(MetricInfix("UpdateLoginAsync"))));
 }
 
 void PasswordStoreAndroidBackend::RemoveLoginAsync(
@@ -372,7 +380,7 @@
   JobId job_id = bridge_->RemoveLogin(form);
   QueueNewJob(job_id, JobReturnHandler(
                           std::move(callback),
-                          JobReturnHandler::MetricInfix("RemoveLoginAsync")));
+                          MetricsRecorder(MetricInfix("RemoveLoginAsync"))));
 }
 
 void PasswordStoreAndroidBackend::FilterAndRemoveLogins(
@@ -395,20 +403,20 @@
     }
   }
 
+  // Create a barrier callback that aggregates results of a multiple
+  // calls to RemoveLoginAsync.
   auto barrier_callback = base::BarrierCallback<PasswordStoreChangeList>(
       logins_to_remove.size(),
       base::BindOnce(&JoinPasswordStoreChanges).Then(std::move(reply)));
 
   // Create and run the callback chain that removes the logins.
   base::RepeatingClosure callbacks_chain = base::DoNothing();
-
   for (const auto& login : logins_to_remove) {
     callbacks_chain =
         base::BindRepeating(&PasswordStoreAndroidBackend::RemoveLoginAsync,
                             weak_ptr_factory_.GetWeakPtr(), std::move(login),
                             barrier_callback.Then(std::move(callbacks_chain)));
   }
-
   std::move(callbacks_chain).Run();
 }
 
@@ -425,7 +433,7 @@
           base::BindOnce(&PasswordStoreAndroidBackend::FilterAndRemoveLogins,
                          weak_ptr_factory_.GetWeakPtr(), std::move(url_filter),
                          delete_begin, delete_end, std::move(callback)),
-          JobReturnHandler::MetricInfix("GetAllLoginsAsync")));
+          MetricsRecorder(MetricInfix("GetAllLoginsAsync"))));
 }
 
 void PasswordStoreAndroidBackend::RemoveLoginsCreatedBetweenAsync(
@@ -441,7 +449,7 @@
                          // Include all urls.
                          base::BindRepeating([](const GURL&) { return true; }),
                          delete_begin, delete_end, std::move(callback)),
-          JobReturnHandler::MetricInfix("GetAllLoginsAsync")));
+          MetricsRecorder(MetricInfix("GetAllLoginsAsync"))));
 }
 
 void PasswordStoreAndroidBackend::DisableAutoSignInForOriginsAsync(
@@ -541,7 +549,24 @@
   QueueNewJob(job_id, JobReturnHandler(
                           base::BindOnce(&ValidateSignonRealm, std::move(form),
                                          include_psl, std::move(callback)),
-                          JobReturnHandler::MetricInfix("GetLoginsAsync")));
+                          MetricsRecorder(MetricInfix("GetLoginsAsync"))));
+}
+
+LoginsOrErrorReply
+PasswordStoreAndroidBackend::ReportMetricsAndInvokeCallbackForLoginsRetrieval(
+    const MetricInfix& metric_infix,
+    LoginsReply callback) {
+  return base::BindOnce(
+      [](MetricsRecorder metrics_recorder, LoginsReply callback,
+         LoginsResultOrError results) {
+        metrics_recorder.RecordMetrics(
+            /*success=*/!(
+                absl::holds_alternative<PasswordStoreBackendError>(results)),
+            /*error=*/absl::nullopt);
+        std::move(callback).Run(
+            GetLoginsOrEmptyListOnFailure(std::move(results)));
+      },
+      MetricsRecorder(metric_infix), std::move(callback));
 }
 
 }  // namespace password_manager
diff --git a/chrome/browser/password_manager/android/password_store_android_backend.h b/chrome/browser/password_manager/android/password_store_android_backend.h
index 83af430a..9d8acf5 100644
--- a/chrome/browser/password_manager/android/password_store_android_backend.h
+++ b/chrome/browser/password_manager/android/password_store_android_backend.h
@@ -79,20 +79,49 @@
         this};
   };
 
-  // Wraps the handler for one or multiple asynchronous jobs (if successful).
-  // Also provides means to record metrics about the jobs (if successful or
-  // not). An object of this type shall be created and stored in
-  // |request_for_job_| once an asynchronous begins, and destroyed once jobs are
-  // finished.
+  using MetricInfix = base::StrongAlias<struct MetricNameTag, std::string>;
+
+  // Records metrics for an asynchronous job or a series of jobs. The job is expected to have
+  // started when the MetricsRecorder instance is created. Latency is reported in RecordMetrics()
+  // under that assumption.
+  class MetricsRecorder {
+   public:
+    MetricsRecorder();
+    explicit MetricsRecorder(MetricInfix metric_name);
+    MetricsRecorder(MetricsRecorder&&);
+    MetricsRecorder& operator=(MetricsRecorder&&);
+    ~MetricsRecorder();
+
+    // Records the following metrics:
+    // - "PasswordManager.PasswordStoreAndroidBackend.<metric_infix_>.Latency"
+    // - "PasswordManager.PasswordStoreAndroidBackend.<metric_infix_>.Success"
+    // When |error| is specified, the following metrcis are recorded in
+    // addition:
+    // - "PasswordManager.PasswordStoreAndroidBackend.APIError"
+    // - "PasswordManager.PasswordStoreAndroidBackend.ErrorCode"
+    // - "PasswordManager.PasswordStoreAndroidBackend.<metric_infix_>.APIError"
+    // - "PasswordManager.PasswordStoreAndroidBackend.<metric_infix_>.ErrorCode"
+    void RecordMetrics(bool success,
+                       absl::optional<AndroidBackendError> error) const;
+
+   private:
+    MetricInfix metric_infix_;
+    base::Time start_ = base::Time::Now();
+  };
+
+  // Wraps the handler for an asynchronous job (if successful) and invokes the
+  // supplied metrics recorded upon completion. An object of this type shall be
+  // created and stored in |request_for_job_| once an asynchronous begins, and
+  // destroyed once the job is finished.
   class JobReturnHandler {
    public:
     using ErrorReply = base::OnceClosure;
-    using MetricInfix = base::StrongAlias<struct MetricNameTag, std::string>;
 
     JobReturnHandler();
-    JobReturnHandler(LoginsOrErrorReply callback, MetricInfix metric_name);
+    JobReturnHandler(LoginsOrErrorReply callback,
+                     MetricsRecorder metrics_recorder);
     JobReturnHandler(PasswordStoreChangeListReply callback,
-                     MetricInfix metric_infix);
+                     MetricsRecorder metrics_recorder);
     JobReturnHandler(JobReturnHandler&&);
     JobReturnHandler& operator=(JobReturnHandler&&);
     ~JobReturnHandler();
@@ -107,21 +136,12 @@
       return std::move(absl::get<T>(success_callback_));
     }
 
-    // Records metrics for this job:
-    // - "PasswordManager.PasswordStoreAndroidBackend.<metric_infix_>.Latency"
-    // - "PasswordManager.PasswordStoreAndroidBackend.<metric_infix_>.Success"
-    // In case of failure. the following are recorded in addition:
-    // - "PasswordManager.PasswordStoreAndroidBackend.APIError"
-    // - "PasswordManager.PasswordStoreAndroidBackend.ErrorCode"
-    // - "PasswordManager.PasswordStoreAndroidBackend.<metric_infix_>.APIError"
-    // - "PasswordManager.PasswordStoreAndroidBackend.<metric_infix_>.ErrorCode"
     void RecordMetrics(absl::optional<AndroidBackendError> error) const;
 
    private:
     absl::variant<LoginsOrErrorReply, PasswordStoreChangeListReply>
         success_callback_;
-    MetricInfix metric_infix_;
-    base::Time start_ = base::Time::Now();
+    MetricsRecorder metrics_recorder_;
   };
 
   using JobId = PasswordStoreAndroidBackendBridge::JobId;
@@ -198,6 +218,13 @@
       PasswordStoreChangeListReply reply,
       LoginsResultOrError result);
 
+  // Creates a metrics recorder that records latency and success metrics for
+  // logins retrieval operation with |metric_infix| name prior to calling
+  // |callback|.
+  LoginsOrErrorReply ReportMetricsAndInvokeCallbackForLoginsRetrieval(
+      const MetricInfix& metric_infix,
+      LoginsReply callback);
+
   // Observer to propagate remote form changes to.
   RemoteChangesReceived remote_form_changes_received_;
 
diff --git a/chrome/browser/performance_manager/policies/working_set_trimmer_policy_arcvm_unittest.cc b/chrome/browser/performance_manager/policies/working_set_trimmer_policy_arcvm_unittest.cc
index 8d9163f2..4e826ee 100644
--- a/chrome/browser/performance_manager/policies/working_set_trimmer_policy_arcvm_unittest.cc
+++ b/chrome/browser/performance_manager/policies/working_set_trimmer_policy_arcvm_unittest.cc
@@ -142,14 +142,18 @@
 
 // Tests that IsEligibleForReclaim() returns false when ARCVM is focused.
 TEST_F(WorkingSetTrimmerPolicyArcVmTest, WindowFocused) {
+  // Create container window as the parent for other windows.
+  aura::Window container_window(nullptr, aura::client::WINDOW_TYPE_NORMAL);
+  container_window.Init(ui::LAYER_NOT_DRAWN);
+
   // Create two fake windows.
-  aura::Window* arc_window =
-      aura::test::CreateTestWindow(SK_ColorGREEN, 0, gfx::Rect(), nullptr);
+  aura::Window* arc_window = aura::test::CreateTestWindow(
+      SK_ColorGREEN, 0, gfx::Rect(), &container_window);
   arc_window->SetProperty(aura::client::kAppType,
                           static_cast<int>(ash::AppType::ARC_APP));
   ASSERT_TRUE(ash::IsArcWindow(arc_window));
-  aura::Window* chrome_window =
-      aura::test::CreateTestWindow(SK_ColorRED, 0, gfx::Rect(), nullptr);
+  aura::Window* chrome_window = aura::test::CreateTestWindow(
+      SK_ColorRED, 0, gfx::Rect(), &container_window);
   ASSERT_FALSE(ash::IsArcWindow(chrome_window));
 
   // Initially, Chrome window is focused.
diff --git a/chrome/browser/policy/BUILD.gn b/chrome/browser/policy/BUILD.gn
index 24080c6..f42ec2af 100644
--- a/chrome/browser/policy/BUILD.gn
+++ b/chrome/browser/policy/BUILD.gn
@@ -60,8 +60,12 @@
 
   if (!is_android) {
     sources += [
+      "extension_policy_test_base.cc",
+      "extension_policy_test_base.h",
       "policy_test_utils.cc",
       "policy_test_utils.h",
+      "safe_browsing_policy_test.cc",
+      "safe_browsing_policy_test.h",
       "safe_search_policy_test.cc",
       "safe_search_policy_test.h",
       "url_blocking_policy_test_utils.cc",
@@ -100,7 +104,20 @@
 
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
-  sources = []
+  sources = [ "site_isolation_policy_browsertest.cc" ]
+
+  deps = [
+    ":test_support",
+    "//base",
+    "//chrome/app:command_ids",
+    "//chrome/browser",
+    "//chrome/common",
+    "//chrome/test:test_support",
+    "//components/policy/core/common",
+    "//components/prefs",
+    "//content/test:test_support",
+    "//testing/gmock",
+  ]
 
   if (!is_android) {
     sources += [
@@ -117,28 +134,18 @@
       "test/web_rtc_local_ips_allowed_urls_policy_browsertest.cc",
     ]
 
-    deps = [
-      ":test_support",
-      "//base",
-      "//chrome/app:command_ids",
-      "//chrome/browser",
+    deps += [
       "//chrome/browser/devtools:test_support",
       "//chrome/browser/profiles:profile",
-      "//chrome/common",
-      "//chrome/test:test_support",
       "//components/bookmarks/common:common",
       "//components/enterprise",
       "//components/enterprise:test_support",
       "//components/policy:chrome_settings_proto_generated_compile",
       "//components/policy/core/browser",
-      "//components/policy/core/common",
       "//components/policy/core/common:common_constants",
       "//components/policy/proto",
       "//components/policy/test_support",
-      "//components/prefs",
       "//components/webrtc:webrtc",
-      "//content/test:test_support",
-      "//testing/gmock",
     ]
   }
 
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 9706f0d..2e53a16 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -1290,6 +1290,9 @@
   { key::kChromadToCloudMigrationEnabled,
     ash::prefs::kChromadToCloudMigrationEnabled,
     base::Value::Type::BOOLEAN },
+  { key::kProjectorEnabled,
+    ash::prefs::kProjectorAllowByPolicy,
+    base::Value::Type::BOOLEAN },
 #endif // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if defined(OS_WIN)
diff --git a/chrome/browser/policy/extension_policy_browsertest.cc b/chrome/browser/policy/extension_policy_browsertest.cc
index 3b08355..aee5a39183 100644
--- a/chrome/browser/policy/extension_policy_browsertest.cc
+++ b/chrome/browser/policy/extension_policy_browsertest.cc
@@ -33,6 +33,7 @@
 #include "chrome/browser/extensions/shared_module_service.h"
 #include "chrome/browser/extensions/unpacked_installer.h"
 #include "chrome/browser/extensions/updater/extension_updater.h"
+#include "chrome/browser/policy/extension_policy_test_base.h"
 #include "chrome/browser/policy/policy_test_utils.h"
 #include "chrome/browser/policy/profile_policy_connector_builder.h"
 #include "chrome/browser/profiles/profile.h"
@@ -205,7 +206,7 @@
       click_event);
 }
 
-class ExtensionPolicyTest : public PolicyTest {
+class ExtensionPolicyTest : public ExtensionPolicyTestBase {
  public:
   ExtensionPolicyTest() = default;
 
@@ -219,11 +220,11 @@
         std::make_unique<extensions::ScopedIgnoreContentVerifierForTest>();
     test_extension_cache_ = std::make_unique<extensions::ExtensionCacheFake>();
     // Base class SetUp() should be invoked at the end as it runs the test body.
-    PolicyTest::SetUp();
+    ExtensionPolicyTestBase::SetUp();
   }
 
   void SetUpOnMainThread() override {
-    PolicyTest::SetUpOnMainThread();
+    ExtensionPolicyTestBase::SetUpOnMainThread();
     if (extension_service()->updater()) {
       extension_service()->updater()->SetExtensionCacheForTesting(
           test_extension_cache_.get());
@@ -231,7 +232,7 @@
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    PolicyTest::SetUpCommandLine(command_line);
+    ExtensionPolicyTestBase::SetUpCommandLine(command_line);
     // Some bots are flaky due to slower loading interacting with
     // deferred commits.
     command_line->AppendSwitch(blink::switches::kAllowPreCommitInput);
diff --git a/chrome/browser/policy/extension_policy_test_base.cc b/chrome/browser/policy/extension_policy_test_base.cc
new file mode 100644
index 0000000..8744962
--- /dev/null
+++ b/chrome/browser/policy/extension_policy_test_base.cc
@@ -0,0 +1,31 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/policy/extension_policy_test_base.h"
+
+#include "base/path_service.h"
+#include "chrome/browser/extensions/chrome_test_extension_loader.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/ui_test_utils.h"
+
+namespace policy {
+
+const base::FilePath::CharType kTestExtensionsDir[] =
+    FILE_PATH_LITERAL("extensions");
+
+ExtensionPolicyTestBase::ExtensionPolicyTestBase() = default;
+
+ExtensionPolicyTestBase::~ExtensionPolicyTestBase() = default;
+
+scoped_refptr<const extensions::Extension>
+ExtensionPolicyTestBase::LoadUnpackedExtension(
+    const base::FilePath::StringType& name) {
+  base::FilePath extension_path(ui_test_utils::GetTestFilePath(
+      base::FilePath(kTestExtensionsDir), base::FilePath(name)));
+  extensions::ChromeTestExtensionLoader loader(browser()->profile());
+  return loader.LoadExtension(extension_path);
+}
+
+}  // namespace policy
diff --git a/chrome/browser/policy/extension_policy_test_base.h b/chrome/browser/policy/extension_policy_test_base.h
new file mode 100644
index 0000000..a430d02
--- /dev/null
+++ b/chrome/browser/policy/extension_policy_test_base.h
@@ -0,0 +1,31 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_POLICY_EXTENSION_POLICY_TEST_BASE_H_
+#define CHROME_BROWSER_POLICY_EXTENSION_POLICY_TEST_BASE_H_
+
+#include "base/files/file_path.h"
+#include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/test/base/chrome_test_utils.h"
+
+namespace extensions {
+class Extension;
+}  // namespace extensions
+
+namespace policy {
+
+extern const base::FilePath::CharType kTestExtensionsDir[];
+
+class ExtensionPolicyTestBase : public PolicyTest {
+ protected:
+  ExtensionPolicyTestBase();
+  ~ExtensionPolicyTestBase() override;
+
+  scoped_refptr<const extensions::Extension> LoadUnpackedExtension(
+      const base::FilePath::StringType& name);
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_POLICY_EXTENSION_POLICY_TEST_BASE_H_
diff --git a/chrome/browser/policy/policy_test_utils.cc b/chrome/browser/policy/policy_test_utils.cc
index fa2e86de..b54f81961 100644
--- a/chrome/browser/policy/policy_test_utils.cc
+++ b/chrome/browser/policy/policy_test_utils.cc
@@ -14,16 +14,10 @@
 #include "base/test/bind.h"
 #include "base/values.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/extensions/chrome_test_extension_loader.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/net/safe_search_util.h"
-#include "chrome/test/base/ui_test_utils.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/policy_constants.h"
-#include "components/security_interstitials/content/security_interstitial_page.h"
-#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
 #include "components/variations/variations_params_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
@@ -34,14 +28,9 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 using content::BrowserThread;
-using testing::_;
-using testing::Return;
 
 namespace policy {
 
-const base::FilePath::CharType kTestExtensionsDir[] =
-    FILE_PATH_LITERAL("extensions");
-
 void GetTestDataDirectory(base::FilePath* test_data_directory) {
   ASSERT_TRUE(
       base::PathService::Get(chrome::DIR_TEST_DATA, test_data_directory));
@@ -68,14 +57,6 @@
       {{"sendingThreshold", "1.0"}}, command_line);
 }
 
-scoped_refptr<const extensions::Extension> PolicyTest::LoadUnpackedExtension(
-    const base::FilePath::StringType& name) {
-  base::FilePath extension_path(ui_test_utils::GetTestFilePath(
-      base::FilePath(kTestExtensionsDir), base::FilePath(name)));
-  extensions::ChromeTestExtensionLoader loader(browser()->profile());
-  return loader.LoadExtension(extension_path);
-}
-
 void PolicyTest::UpdateProviderPolicy(const PolicyMap& policy) {
   PolicyMap policy_with_defaults = policy.Clone();
 #if defined(OS_CHROMEOS)
@@ -118,22 +99,6 @@
   return xhr_result && execute_result;
 }
 
-bool PolicyTest::IsShowingInterstitial(content::WebContents* tab) {
-  security_interstitials::SecurityInterstitialTabHelper* helper =
-      security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
-          tab);
-  if (!helper) {
-    return false;
-  }
-  return helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting() !=
-         nullptr;
-}
-
-void PolicyTest::WaitForInterstitial(content::WebContents* tab) {
-  ASSERT_TRUE(IsShowingInterstitial(tab));
-  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetMainFrame()));
-}
-
 void PolicyTest::FlushBlocklistPolicy() {
   // Updates of the URLBlocklist are done on IO, after building the blocklist
   // on the blocking pool, which is initiated from IO.
diff --git a/chrome/browser/policy/policy_test_utils.h b/chrome/browser/policy/policy_test_utils.h
index 0b6e9121..d9c1b3b 100644
--- a/chrome/browser/policy/policy_test_utils.h
+++ b/chrome/browser/policy/policy_test_utils.h
@@ -16,14 +16,8 @@
 class WebContents;
 }  // namespace content
 
-namespace extensions {
-class Extension;
-}  // namespace extensions
-
 namespace policy {
 
-extern const base::FilePath::CharType kTestExtensionsDir[];
-
 class PolicyMap;
 
 void GetTestDataDirectory(base::FilePath* test_data_directory);
@@ -49,9 +43,6 @@
 
   void SetScreenshotPolicy(bool enabled);
 
-  scoped_refptr<const extensions::Extension> LoadUnpackedExtension(
-      const base::FilePath::StringType& name);
-
   void UpdateProviderPolicy(const PolicyMap& policy);
 
   static void SetPolicy(PolicyMap* policies,
@@ -65,12 +56,6 @@
   static bool FetchSubresource(content::WebContents* web_contents,
                                const GURL& url);
 
-  bool IsShowingInterstitial(content::WebContents* tab);
-
-  void WaitForInterstitial(content::WebContents* tab);
-
-  int IsEnhancedProtectionMessageVisibleOnInterstitial();
-
   void FlushBlocklistPolicy();
 
   testing::NiceMock<MockConfigurationPolicyProvider> provider_;
diff --git a/chrome/browser/policy/safe_browsing_policy_test.cc b/chrome/browser/policy/safe_browsing_policy_test.cc
new file mode 100644
index 0000000..cbaa765f
--- /dev/null
+++ b/chrome/browser/policy/safe_browsing_policy_test.cc
@@ -0,0 +1,30 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/policy/safe_browsing_policy_test.h"
+
+#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
+#include "content/public/browser/web_contents.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+SafeBrowsingPolicyTest::SafeBrowsingPolicyTest() = default;
+
+SafeBrowsingPolicyTest::~SafeBrowsingPolicyTest() = default;
+
+bool SafeBrowsingPolicyTest::IsShowingInterstitial(content::WebContents* tab) {
+  security_interstitials::SecurityInterstitialTabHelper* helper =
+      security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
+          tab);
+  return helper &&
+         helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting();
+}
+
+void SafeBrowsingPolicyTest::WaitForInterstitial(content::WebContents* tab) {
+  ASSERT_TRUE(IsShowingInterstitial(tab));
+  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetMainFrame()));
+}
+
+}  // namespace policy
diff --git a/chrome/browser/policy/safe_browsing_policy_test.h b/chrome/browser/policy/safe_browsing_policy_test.h
new file mode 100644
index 0000000..be64845
--- /dev/null
+++ b/chrome/browser/policy/safe_browsing_policy_test.h
@@ -0,0 +1,28 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_POLICY_SAFE_BROWSING_POLICY_TEST_H_
+#define CHROME_BROWSER_POLICY_SAFE_BROWSING_POLICY_TEST_H_
+
+#include "chrome/browser/policy/policy_test_utils.h"
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace policy {
+
+class SafeBrowsingPolicyTest : public PolicyTest {
+ public:
+  SafeBrowsingPolicyTest();
+  ~SafeBrowsingPolicyTest() override;
+
+  bool IsShowingInterstitial(content::WebContents* tab);
+
+  void WaitForInterstitial(content::WebContents* tab);
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_POLICY_SAFE_BROWSING_POLICY_TEST_H_
diff --git a/chrome/browser/policy/site_isolation_policy_browsertest.cc b/chrome/browser/policy/site_isolation_policy_browsertest.cc
index 50ad7f3..cd6cf79 100644
--- a/chrome/browser/policy/site_isolation_policy_browsertest.cc
+++ b/chrome/browser/policy/site_isolation_policy_browsertest.cc
@@ -3,15 +3,14 @@
 // found in the LICENSE file.
 
 #include "base/cxx17_backports.h"
+#include "base/feature_list.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
+#include "chrome/test/base/chrome_test_utils.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "components/policy/policy_constants.h"
@@ -24,9 +23,10 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_utils.h"
 #include "url/gurl.h"
 
-class SiteIsolationPolicyBrowserTest : public InProcessBrowserTest {
+class SiteIsolationPolicyBrowserTest : public PlatformBrowserTest {
  public:
   SiteIsolationPolicyBrowserTest(const SiteIsolationPolicyBrowserTest&) =
       delete;
@@ -42,7 +42,7 @@
   };
 
   void CheckExpectations(Expectations* expectations, size_t count) {
-    content::BrowserContext* context = browser()->profile();
+    content::BrowserContext* context = chrome_test_utils::GetProfile(this);
     for (size_t i = 0; i < count; ++i) {
       const GURL url(expectations[i].url);
       auto instance = content::SiteInstance::CreateForURL(context, url);
@@ -270,6 +270,9 @@
 #if defined(OS_ANDROID)
   EXPECT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kDisableSiteIsolationForPolicy));
-#endif
+  EXPECT_EQ(content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites(),
+            base::FeatureList::IsEnabled(features::kSitePerProcess));
+#else
   EXPECT_TRUE(content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites());
+#endif  // defined(OS_ANDROID)
 }
diff --git a/chrome/browser/policy/test/certificate_transparency_policy_browsertest.cc b/chrome/browser/policy/test/certificate_transparency_policy_browsertest.cc
index 1e34fcdb..e50889b 100644
--- a/chrome/browser/policy/test/certificate_transparency_policy_browsertest.cc
+++ b/chrome/browser/policy/test/certificate_transparency_policy_browsertest.cc
@@ -6,7 +6,7 @@
 #include "base/values.h"
 #include "chrome/browser/interstitials/security_interstitial_page_test_utils.h"
 #include "chrome/browser/net/system_network_context_manager.h"
-#include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/browser/policy/safe_browsing_policy_test.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/policy/core/common/policy_map.h"
@@ -48,7 +48,7 @@
                      required));
 }
 
-class CertificateTransparencyPolicyTest : public PolicyTest {
+class CertificateTransparencyPolicyTest : public SafeBrowsingPolicyTest {
  public:
   CertificateTransparencyPolicyTest() {
     SystemNetworkContextManager::SetEnableCertificateTransparencyForTesting(
diff --git a/chrome/browser/policy/test/full_screen_allowed_policy_browsertest.cc b/chrome/browser/policy/test/full_screen_allowed_policy_browsertest.cc
index eb3f30b..9322016 100644
--- a/chrome/browser/policy/test/full_screen_allowed_policy_browsertest.cc
+++ b/chrome/browser/policy/test/full_screen_allowed_policy_browsertest.cc
@@ -7,7 +7,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/browser/policy/extension_policy_test_base.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -71,7 +71,9 @@
 
 }  // namespace
 
-IN_PROC_BROWSER_TEST_F(PolicyTest, FullscreenAllowedBrowser) {
+typedef ExtensionPolicyTestBase FullscreenPolicyTest;
+
+IN_PROC_BROWSER_TEST_F(FullscreenPolicyTest, FullscreenAllowedBrowser) {
   PolicyMap policies;
   policies.Set(key::kFullscreenAllowed, POLICY_LEVEL_MANDATORY,
                POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, base::Value(false),
@@ -86,7 +88,7 @@
   EXPECT_FALSE(browser_window->IsFullscreen());
 }
 
-IN_PROC_BROWSER_TEST_F(PolicyTest, FullscreenAllowedApp) {
+IN_PROC_BROWSER_TEST_F(FullscreenPolicyTest, FullscreenAllowedApp) {
   PolicyMap policies;
   policies.Set(key::kFullscreenAllowed, POLICY_LEVEL_MANDATORY,
                POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, base::Value(false),
diff --git a/chrome/browser/policy/test/note_taking_on_lock_screen_policy_browsertest.cc b/chrome/browser/policy/test/note_taking_on_lock_screen_policy_browsertest.cc
index 5676867..667c4447 100644
--- a/chrome/browser/policy/test/note_taking_on_lock_screen_policy_browsertest.cc
+++ b/chrome/browser/policy/test/note_taking_on_lock_screen_policy_browsertest.cc
@@ -8,7 +8,7 @@
 #include "base/command_line.h"
 #include "base/values.h"
 #include "chrome/browser/ash/note_taking_helper.h"
-#include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/browser/policy/extension_policy_test_base.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -23,7 +23,7 @@
 
 namespace policy {
 
-class NoteTakingOnLockScreenPolicyTest : public PolicyTest {
+class NoteTakingOnLockScreenPolicyTest : public ExtensionPolicyTestBase {
  public:
   NoteTakingOnLockScreenPolicyTest() = default;
   ~NoteTakingOnLockScreenPolicyTest() override = default;
@@ -39,7 +39,7 @@
     command_line->AppendSwitchASCII(
         extensions::switches::kAllowlistedExtensionID, kTestAppId);
     command_line->AppendSwitch(ash::switches::kAshForceEnableStylusTools);
-    PolicyTest::SetUpCommandLine(command_line);
+    ExtensionPolicyTestBase::SetUpCommandLine(command_line);
   }
 
   void SetUserLevelPrefValue(const std::string& app_id,
diff --git a/chrome/browser/policy/test/policy_browsertest.cc b/chrome/browser/policy/test/policy_browsertest.cc
index c0b30ce..cb65a97f 100644
--- a/chrome/browser/policy/test/policy_browsertest.cc
+++ b/chrome/browser/policy/test/policy_browsertest.cc
@@ -55,6 +55,7 @@
 #include "chrome/browser/devtools/devtools_window_testing.h"
 #include "chrome/browser/extensions/api/chrome_extensions_api_client.h"
 #include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/browser/policy/safe_browsing_policy_test.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -393,7 +394,7 @@
 
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-class NetworkTimePolicyTest : public PolicyTest {
+class NetworkTimePolicyTest : public SafeBrowsingPolicyTest {
  public:
   NetworkTimePolicyTest() = default;
   NetworkTimePolicyTest(const NetworkTimePolicyTest&) = delete;
diff --git a/chrome/browser/policy/test/safe_browsing_policy_browsertest.cc b/chrome/browser/policy/test/safe_browsing_policy_browsertest.cc
index 4ada881..e8425e16 100644
--- a/chrome/browser/policy/test/safe_browsing_policy_browsertest.cc
+++ b/chrome/browser/policy/test/safe_browsing_policy_browsertest.cc
@@ -8,7 +8,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/browser/policy/safe_browsing_policy_test.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/chrome_password_protection_service.h"
 #include "chrome/browser/ui/browser.h"
@@ -27,7 +27,8 @@
 
 namespace policy {
 
-int PolicyTest::IsEnhancedProtectionMessageVisibleOnInterstitial() {
+int IsEnhancedProtectionMessageVisibleOnInterstitial(
+    SafeBrowsingPolicyTest* browser_test) {
   const std::string command = base::StringPrintf(
       "var node = document.getElementById('enhanced-protection-message');"
       "if (node) {"
@@ -43,8 +44,8 @@
       security_interstitials::CMD_ERROR);
 
   content::WebContents* tab =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  WaitForInterstitial(tab);
+      browser_test->browser()->tab_strip_model()->GetActiveWebContents();
+  browser_test->WaitForInterstitial(tab);
   int result = 0;
   EXPECT_TRUE(content::ExecuteScriptAndExtractInt(tab->GetMainFrame(), command,
                                                   &result));
@@ -52,7 +53,8 @@
 }
 
 // Test extended reporting is managed by policy.
-IN_PROC_BROWSER_TEST_F(PolicyTest, SafeBrowsingExtendedReportingPolicyManaged) {
+IN_PROC_BROWSER_TEST_F(SafeBrowsingPolicyTest,
+                       SafeBrowsingExtendedReportingPolicyManaged) {
   // Set the extended reporting pref to True and ensure the enterprise policy
   // can overwrite it.
   PrefService* prefs = browser()->profile()->GetPrefs();
@@ -83,7 +85,7 @@
 
 // Test that when Safe Browsing state is managed by policy, the enhanced
 // protection message does not appear on SSL blocking pages.
-IN_PROC_BROWSER_TEST_F(PolicyTest, SafeBrowsingStatePolicyManaged) {
+IN_PROC_BROWSER_TEST_F(SafeBrowsingPolicyTest, SafeBrowsingStatePolicyManaged) {
   net::EmbeddedTestServer https_server_expired(
       net::EmbeddedTestServer::TYPE_HTTPS);
   https_server_expired.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
@@ -100,7 +102,7 @@
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(),
                                            https_server_expired.GetURL("/")));
   EXPECT_EQ(security_interstitials::CMD_TEXT_FOUND,
-            IsEnhancedProtectionMessageVisibleOnInterstitial());
+            IsEnhancedProtectionMessageVisibleOnInterstitial(this));
 
   // Set the enterprise policy to force standard protection.
   PolicyMap policies;
@@ -120,12 +122,12 @@
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(),
                                            https_server_expired.GetURL("/")));
   EXPECT_EQ(security_interstitials::CMD_TEXT_NOT_FOUND,
-            IsEnhancedProtectionMessageVisibleOnInterstitial());
+            IsEnhancedProtectionMessageVisibleOnInterstitial(this));
 }
 
 // Test that when safe browsing allowlist domains are set by policy, safe
 // browsing service gets the correct value.
-IN_PROC_BROWSER_TEST_F(PolicyTest, SafeBrowsingAllowlistDomains) {
+IN_PROC_BROWSER_TEST_F(SafeBrowsingPolicyTest, SafeBrowsingAllowlistDomains) {
   // Without setting up the enterprise policy,
   // |GetSafeBrowsingDomainsPref(..) should return empty list.
   const PrefService* const prefs = browser()->profile()->GetPrefs();
@@ -170,7 +172,7 @@
 
 // Test that when password protection login URLs are set by policy, password
 // protection service gets the correct value.
-IN_PROC_BROWSER_TEST_F(PolicyTest, PasswordProtectionLoginURLs) {
+IN_PROC_BROWSER_TEST_F(SafeBrowsingPolicyTest, PasswordProtectionLoginURLs) {
   // Without setting up the enterprise policy,
   // |GetPasswordProtectionLoginURLsPref(..) should return empty list.
   const PrefService* const prefs = browser()->profile()->GetPrefs();
@@ -213,7 +215,8 @@
 
 // Test that when password protection change password URL is set by policy,
 // password protection service gets the correct value.
-IN_PROC_BROWSER_TEST_F(PolicyTest, PasswordProtectionChangePasswordURL) {
+IN_PROC_BROWSER_TEST_F(SafeBrowsingPolicyTest,
+                       PasswordProtectionChangePasswordURL) {
   // Without setting up the enterprise policy,
   // |GetEnterpriseChangePasswordURL(..) should return default GAIA change
   // password URL.
@@ -270,7 +273,7 @@
 // Test that when password protection warning trigger is set for users who are
 // not signed-into Chrome, Chrome password protection service gets the correct
 // value.
-IN_PROC_BROWSER_TEST_F(PolicyTest,
+IN_PROC_BROWSER_TEST_F(SafeBrowsingPolicyTest,
                        PasswordProtectionWarningTriggerNotLoggedIn) {
   MockPasswordProtectionService mock_service(
       g_browser_process->safe_browsing_service(), browser()->profile());
@@ -305,7 +308,8 @@
 // Test that when password protection warning trigger is set for Gmail users,
 // Chrome password protection service gets the correct
 // value.
-IN_PROC_BROWSER_TEST_F(PolicyTest, PasswordProtectionWarningTriggerGmail) {
+IN_PROC_BROWSER_TEST_F(SafeBrowsingPolicyTest,
+                       PasswordProtectionWarningTriggerGmail) {
   MockPasswordProtectionService mock_service(
       g_browser_process->safe_browsing_service(), browser()->profile());
 
@@ -342,7 +346,8 @@
 
 // Test that when password protection warning trigger is set for GSuite users,
 // Chrome password protection service gets the correct value.
-IN_PROC_BROWSER_TEST_F(PolicyTest, PasswordProtectionWarningTriggerGSuite) {
+IN_PROC_BROWSER_TEST_F(SafeBrowsingPolicyTest,
+                       PasswordProtectionWarningTriggerGSuite) {
   MockPasswordProtectionService mock_service(
       g_browser_process->safe_browsing_service(), browser()->profile());
   const PrefService* const prefs = browser()->profile()->GetPrefs();
diff --git a/chrome/browser/policy/test/ssl_error_overriding_allowed_policy_browsertest.cc b/chrome/browser/policy/test/ssl_error_overriding_allowed_policy_browsertest.cc
index 43558da7..af73d28 100644
--- a/chrome/browser/policy/test/ssl_error_overriding_allowed_policy_browsertest.cc
+++ b/chrome/browser/policy/test/ssl_error_overriding_allowed_policy_browsertest.cc
@@ -5,7 +5,7 @@
 #include <vector>
 
 #include "chrome/browser/interstitials/security_interstitial_page_test_utils.h"
-#include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/browser/policy/safe_browsing_policy_test.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/pref_names.h"
@@ -38,7 +38,8 @@
 
 // Test that when SSL error overriding policies are unset, the proceed link
 // appears on SSL blocking pages.
-IN_PROC_BROWSER_TEST_F(PolicyTest, SSLErrorOverridingAllowedDefaults) {
+IN_PROC_BROWSER_TEST_F(SafeBrowsingPolicyTest,
+                       SSLErrorOverridingAllowedDefaults) {
   net::EmbeddedTestServer https_server_expired(
       net::EmbeddedTestServer::TYPE_HTTPS);
   https_server_expired.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
@@ -69,7 +70,8 @@
 
 // Test that when SSL error overriding is allowed, the origin list is ignored
 // and the proceed link appears on SSL blocking pages.
-IN_PROC_BROWSER_TEST_F(PolicyTest, SSLErrorOverridingAllowedEnabled) {
+IN_PROC_BROWSER_TEST_F(SafeBrowsingPolicyTest,
+                       SSLErrorOverridingAllowedEnabled) {
   net::EmbeddedTestServer https_server_expired(
       net::EmbeddedTestServer::TYPE_HTTPS);
   https_server_expired.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
@@ -115,7 +117,8 @@
 
 // Test that when SSL error overriding is disabled, the proceed link does not
 // appear appear on SSL blocking pages.
-IN_PROC_BROWSER_TEST_F(PolicyTest, SSLErrorOverridingAllowedDisabled) {
+IN_PROC_BROWSER_TEST_F(SafeBrowsingPolicyTest,
+                       SSLErrorOverridingAllowedDisabled) {
   net::EmbeddedTestServer https_server_expired(
       net::EmbeddedTestServer::TYPE_HTTPS);
   https_server_expired.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
@@ -156,7 +159,7 @@
 // Test that when SSL error overriding is disallowed by policy and the origin
 // list is configured, the proceed link does not appear on SSL blocking pages if
 // the page is not on the origin list.
-IN_PROC_BROWSER_TEST_F(PolicyTest,
+IN_PROC_BROWSER_TEST_F(SafeBrowsingPolicyTest,
                        SSLErrorOverridingAllowedForOriginsWrongOrigin) {
   net::EmbeddedTestServer https_server_expired(
       net::EmbeddedTestServer::TYPE_HTTPS);
@@ -213,7 +216,8 @@
 // Test that when SSL error overriding is disallowed by policy and the origin
 // list is configured incorrectly, the proceed link does not appear on SSL
 // blocking pages.
-IN_PROC_BROWSER_TEST_F(PolicyTest, SSLErrorOverridingForOriginsBadInput) {
+IN_PROC_BROWSER_TEST_F(SafeBrowsingPolicyTest,
+                       SSLErrorOverridingForOriginsBadInput) {
   net::EmbeddedTestServer https_server_expired(
       net::EmbeddedTestServer::TYPE_HTTPS);
   https_server_expired.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
@@ -266,7 +270,8 @@
 
 // Test that when SSL error overriding is disallowed by policy and the origin
 // list is empty, the proceed link does not appear on SSL blocking pages.
-IN_PROC_BROWSER_TEST_F(PolicyTest, SSLErrorOverridingForOriginsEmptyList) {
+IN_PROC_BROWSER_TEST_F(SafeBrowsingPolicyTest,
+                       SSLErrorOverridingForOriginsEmptyList) {
   net::EmbeddedTestServer https_server_expired(
       net::EmbeddedTestServer::TYPE_HTTPS);
   https_server_expired.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
@@ -318,7 +323,8 @@
 // Test that when SSL error overriding is disallowed by policy and the origin
 // list is configured, the proceed link appears on SSL blocking pages if the
 // page is on the origin list.
-IN_PROC_BROWSER_TEST_F(PolicyTest, SSLErrorOverridingAllowedForOrigins) {
+IN_PROC_BROWSER_TEST_F(SafeBrowsingPolicyTest,
+                       SSLErrorOverridingAllowedForOrigins) {
   net::EmbeddedTestServer https_server_expired(
       net::EmbeddedTestServer::TYPE_HTTPS);
   https_server_expired.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
index 9e34f1e6..9cfb071 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
@@ -5,6 +5,7 @@
 import {ActionRecorder} from './action_recorder.js';
 import {FocusRingManager} from './focus_ring_manager.js';
 import {MenuManager} from './menu_manager.js';
+import {SwitchAccessMetrics} from './metrics.js';
 import {Navigator} from './navigator.js';
 import {SAChildNode, SARootNode} from './nodes/switch_access_node.js';
 import {SwitchAccess} from './switch_access.js';
@@ -98,6 +99,8 @@
    * @param {!SwitchAccessMenuAction} action
    */
   static performAction(action) {
+    SwitchAccessMetrics.recordMenuAction(action);
+
     // If feature flag is enabled, perform action and escape if successful.
     if (SwitchAccess.instance.multistepAutomationFeaturesEnabled()) {
       if (ActionManager.performActionMultistep(action)) {
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/metrics.js b/chrome/browser/resources/chromeos/accessibility/switch_access/metrics.js
index dfc6585..c993af1 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/metrics.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/metrics.js
@@ -5,14 +5,14 @@
 /**
  * Class to record metrics for Switch Access.
  */
-const SwitchAccessMetrics = {
+export const SwitchAccessMetrics = {
   /**
-   * @param {string} action
+   * @param {string} menuAction
    */
-  recordMenuAction: (action) => {
+  recordMenuAction: (menuAction) => {
     const metricName = 'Accessibility.CrosSwitchAccess.MenuAction.' +
-        SwitchAccessMetrics.toUpperCamelCase(action);
-    chrome.metricsPrivate.recordUserAction(metricName);
+        SwitchAccessMetrics.toUpperCamelCase(menuAction);
+    chrome.metricsPrivate.recordBoolean(metricName, true);
   },
 
   /**
diff --git a/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.html b/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.html
index 99e4eeb..b785970e 100644
--- a/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.html
+++ b/chrome/browser/resources/chromeos/login/components/dialogs/oobe_adaptive_dialog.html
@@ -154,6 +154,9 @@
       #contentContainer {
         width: var(--oobe-adaptive-dialog-content-width);
       }
+      #contentContainer ::slotted(*) {
+        max-width: var(--oobe-adaptive-dialog-content-width);
+      }
 
       .vertical-mode-centering {
         align-items: var(--oobe-adaptive-dialog-item-alignment);
diff --git a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
index 58dcd02..a85c95c5 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
@@ -235,6 +235,7 @@
 js_library("gesture_navigation.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/common/gesture_navigation.m.js" ]
   deps = [
+    "../../components:oobe_cr_lottie.m",
     "../../components/behaviors:login_screen_behavior.m",
     "../../components/behaviors:multi_step_behavior.m",
     "../../components/behaviors:oobe_i18n_behavior.m",
diff --git a/chrome/browser/resources/chromeos/login/screens/common/gesture_navigation.html b/chrome/browser/resources/chromeos/login/screens/common/gesture_navigation.html
index da77926..2ca697d 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/gesture_navigation.html
+++ b/chrome/browser/resources/chromeos/login/screens/common/gesture_navigation.html
@@ -4,9 +4,9 @@
 
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/cr_lottie/cr_lottie.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 
+<link rel="import" href="../../components/oobe_cr_lottie.html">
 <link rel="import" href="../../components/behaviors/login_screen_behavior.html">
 <link rel="import" href="../../components/behaviors/multi_step_behavior.html">
 <link rel="import" href="../../components/behaviors/oobe_i18n_behavior.html">
@@ -22,7 +22,8 @@
     <style include="oobe-dialog-host-styles">
       .gesture-animation-container {
         overflow: hidden;
-        width: min(480px, var(--oobe-adaptive-dialog-content-width));
+        width: 100%;
+        height: 100%;
       }
 
       .gesture-list-item {
@@ -96,12 +97,9 @@
       <p slot="subtitle">
         [[i18nDynamic(locale, 'gestureNavigationHomeDescription')]]
       </p>
-      <div slot="content" class="content-centered">
-        <div class="gesture-animation-container">
-          <cr-lottie animation-url="gesture_go_home.json"
-                     class="gesture-animation">
-          </cr-lottie>
-        </div>
+      <div slot="content" class="content-centered gesture-animation-container">
+        <oobe-cr-lottie animation-url="gesture_go_home.json"
+                    class="gesture-animation">
       </div>
       <div slot="back-navigation">
         <oobe-back-button on-click="onBack_"
@@ -123,12 +121,10 @@
       <p slot="subtitle">
         [[i18nDynamic(locale, 'gestureNavigationOverviewDescription')]]
       </p>
-      <div slot="content" class="content-centered">
-        <div class="gesture-animation-container">
-          <cr-lottie animation-url="gesture_hotseat_overview.json"
-                     class="gesture-animation">
-          </cr-lottie>
-        </div>
+      <div slot="content" class="content-centered gesture-animation-container">
+        <oobe-cr-lottie animation-url="gesture_hotseat_overview.json"
+                    class="gesture-animation">
+        </oobe-cr-lottie>
       </div>
       <div slot="back-navigation">
         <oobe-back-button on-click="onBack_"
@@ -149,12 +145,10 @@
       <p slot="subtitle">
         [[i18nDynamic(locale, 'gestureNavigationBackDescription')]]
       </p>
-      <div slot="content" class="content-centered">
-        <div class="gesture-animation-container">
-          <cr-lottie animation-url="gesture_go_back.json"
-                      class="gesture-animation">
-          </cr-lottie>
-        </div>
+      <div slot="content" class="content-centered gesture-animation-container">
+        <oobe-cr-lottie animation-url="gesture_go_back.json"
+                    class="gesture-animation">
+        </oobe-cr-lottie>
       </div>
       <div slot="back-navigation">
         <oobe-back-button on-click="onBack_"
diff --git a/chrome/browser/resources/chromeos/login/screens/common/gesture_navigation.js b/chrome/browser/resources/chromeos/login/screens/common/gesture_navigation.js
index 1fccd37..39d37813 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/gesture_navigation.js
+++ b/chrome/browser/resources/chromeos/login/screens/common/gesture_navigation.js
@@ -121,9 +121,9 @@
   setPlayCurrentScreenAnimation(enabled) {
     var animation = this.$[this.uiStep].querySelector('.gesture-animation');
     if (animation) {
-      animation.setPlay(enabled);
+      animation.playing = enabled;
     }
   }
 }
 
-customElements.define(GestureNavigation.is, GestureNavigation);
\ No newline at end of file
+customElements.define(GestureNavigation.is, GestureNavigation);
diff --git a/chrome/browser/resources/print_preview/data/user_manager.ts b/chrome/browser/resources/print_preview/data/user_manager.ts
index fd8e412d..e3fc377 100644
--- a/chrome/browser/resources/print_preview/data/user_manager.ts
+++ b/chrome/browser/resources/print_preview/data/user_manager.ts
@@ -20,7 +20,7 @@
 
 const PrintPreviewUserManagerElementBase = WebUIListenerMixin(PolymerElement);
 
-class PrintPreviewUserManagerElement extends
+export class PrintPreviewUserManagerElement extends
     PrintPreviewUserManagerElementBase {
   static get is() {
     return 'print-preview-user-manager';
diff --git a/chrome/browser/resources/print_preview/print_preview.ts b/chrome/browser/resources/print_preview/print_preview.ts
index e9dc09f..09693e743 100644
--- a/chrome/browser/resources/print_preview/print_preview.ts
+++ b/chrome/browser/resources/print_preview/print_preview.ts
@@ -29,6 +29,7 @@
 export {ScalingType} from './data/scaling.js';
 export {Size} from './data/size.js';
 export {Error, State} from './data/state.js';
+export {PrintPreviewUserManagerElement} from './data/user_manager.js';
 export {BackgroundGraphicsModeRestriction, CapabilitiesResponse, ColorModeRestriction, DuplexModeRestriction, NativeInitialSettings, NativeLayer, NativeLayerImpl} from './native_layer.js';
 // <if expr="chromeos or lacros">
 export {PinModeRestriction} from './native_layer.js';
diff --git a/chrome/browser/resources/print_preview/ui/settings_select.ts b/chrome/browser/resources/print_preview/ui/settings_select.ts
index 25aeae40..30c77f7 100644
--- a/chrome/browser/resources/print_preview/ui/settings_select.ts
+++ b/chrome/browser/resources/print_preview/ui/settings_select.ts
@@ -8,7 +8,7 @@
 import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {SelectOption} from '../data/cdd.js';
+import {CapabilityWithReset, SelectOption} from '../data/cdd.js';
 import {getStringForCurrentLocale} from '../print_preview_utils.js';
 
 import {SelectMixin} from './select_mixin.js';
@@ -40,7 +40,7 @@
   }
 
   ariaLabel: string;
-  capability: SelectOption[];
+  capability: CapabilityWithReset&{option: SelectOption[]};
   settingName: string;
   disabled: boolean;
 
diff --git a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.ts b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.ts
index 3897410..08f1acc 100644
--- a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.ts
+++ b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.ts
@@ -525,7 +525,7 @@
 
   private onViewExistingPasswordClick_() {
     chrome.metricsPrivate.recordEnumerationValue(
-        'AddCredentialFromSettingsUserInteractions',
+        'PasswordManager.AddCredentialFromSettings.UserAction',
         AddCredentialFromSettingsUserInteractions.Duplicate_Credential_Viewed,
         AddCredentialFromSettingsUserInteractions.COUNT);
     const existingEntry = this.savedPasswords.find(entry => {
@@ -580,7 +580,7 @@
 
     if (isDuplicate && this.dialogMode_ === PasswordDialogMode.ADD) {
       chrome.metricsPrivate.recordEnumerationValue(
-          'AddCredentialFromSettingsUserInteractions',
+          'PasswordManager.AddCredentialFromSettings.UserAction',
           AddCredentialFromSettingsUserInteractions
               .Duplicated_Credential_Entered,
           AddCredentialFromSettingsUserInteractions.COUNT);
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.ts b/chrome/browser/resources/settings/autofill_page/passwords_section.ts
index 51d58de..fd659ae3 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.ts
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.ts
@@ -617,7 +617,7 @@
 
   private onAddPasswordTap_() {
     chrome.metricsPrivate.recordEnumerationValue(
-        'AddCredentialFromSettingsUserInteractions',
+        'PasswordManager.AddCredentialFromSettings.UserAction',
         AddCredentialFromSettingsUserInteractions.Add_Dialog_Opened,
         AddCredentialFromSettingsUserInteractions.COUNT);
     this.showAddPasswordDialog_ = true;
@@ -627,7 +627,7 @@
 
   private onAddPasswordDialogClosed_() {
     chrome.metricsPrivate.recordEnumerationValue(
-        'AddCredentialFromSettingsUserInteractions',
+        'PasswordManager.AddCredentialFromSettings.UserAction',
         AddCredentialFromSettingsUserInteractions.Add_Dialog_Closed,
         AddCredentialFromSettingsUserInteractions.COUNT);
     this.showAddPasswordDialog_ = false;
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.ts b/chrome/browser/resources/settings/privacy_page/privacy_page.ts
index 4f9be6f..e8114549 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.ts
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.ts
@@ -34,6 +34,7 @@
 import {HatsBrowserProxyImpl, TrustSafetyInteraction} from '../hats_browser_proxy.js';
 import {loadTimeData} from '../i18n_setup.js';
 import {MetricsBrowserProxy, MetricsBrowserProxyImpl} from '../metrics_browser_proxy.js';
+import {SyncStatus} from '../people_page/sync_browser_proxy.js';
 import {PrefsMixin, PrefsMixinInterface} from '../prefs/prefs_mixin.js';
 import {routes} from '../route.js';
 import {RouteObserverMixin, RouteObserverMixinInterface, Router} from '../router.js';
@@ -269,6 +270,11 @@
     this.addWebUIListener(
         'cookieSettingDescriptionChanged',
         (description: string) => this.cookieSettingDescription_ = description);
+
+    this.addWebUIListener(
+        'is-managed-changed', this.onIsManagedChanged_.bind(this));
+    this.addWebUIListener(
+        'sync-status-changed', this.onSyncStatusChanged_.bind(this));
   }
 
   currentRouteChanged() {
@@ -366,6 +372,24 @@
         /* removeSearch */ true);
   }
 
+  private onIsManagedChanged_(isManaged: boolean) {
+    // If the user became managed, then hide the privacy review entry point.
+    // However, if the user was managed before and is no longer now, then do not
+    // make the privacy review entry point visible, as the Settings route for
+    // privacy review would still be unavailable until the page is reloaded.
+    this.enablePrivacyReview_ = this.enablePrivacyReview_ && !isManaged;
+  }
+
+  private onSyncStatusChanged_(syncStatus: SyncStatus) {
+    // If the user signed in to a child user account, then hide the privacy
+    // review entry point. However, if the user was a child user before and is
+    // no longer now then do not make the privacy review entry point visible, as
+    // the Settings route for privacy review would still be unavailable until
+    // the page is reloaded.
+    this.enablePrivacyReview_ =
+        this.enablePrivacyReview_ && !syncStatus.childUser;
+  }
+
   private interactedWithPage_() {
     HatsBrowserProxyImpl.getInstance().trustSafetyInteractionOccurred(
         TrustSafetyInteraction.USED_PRIVACY_CARD);
diff --git a/chrome/browser/search_engines/android/BUILD.gn b/chrome/browser/search_engines/android/BUILD.gn
index c73b9c2c..9f2e3b1 100644
--- a/chrome/browser/search_engines/android/BUILD.gn
+++ b/chrome/browser/search_engines/android/BUILD.gn
@@ -32,6 +32,7 @@
     "//chrome/browser/ui/messages/android:java",
     "//components/browser_ui/settings/android:java",
     "//components/browser_ui/site_settings/android:java",
+    "//components/browser_ui/styles/android:java",
     "//components/browser_ui/util/android:java",
     "//components/browser_ui/widget/android:java",
     "//components/content_settings/android:content_settings_enums_java",
diff --git a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapter.java b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapter.java
index 827c126b..230aa95 100644
--- a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapter.java
+++ b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapter.java
@@ -37,6 +37,7 @@
 import org.chromium.components.browser_ui.site_settings.PermissionInfo;
 import org.chromium.components.browser_ui.site_settings.SingleWebsiteSettings;
 import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.content_settings.ContentSettingValues;
 import org.chromium.components.content_settings.ContentSettingsType;
 import org.chromium.components.location.LocationUtils;
@@ -550,8 +551,8 @@
         int message = getPermissionsLinkMessage(getSearchEngineUrl(templateUrl));
         if (message == 0) return;
 
-        ForegroundColorSpan linkSpan = new ForegroundColorSpan(ApiCompatibilityUtils.getColor(
-                mContext.getResources(), R.color.default_text_color_link));
+        ForegroundColorSpan linkSpan =
+                new ForegroundColorSpan(SemanticColorUtils.getDefaultTextColorLink(mContext));
         link.setVisibility(View.VISIBLE);
         link.setOnClickListener(this);
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 24f42338..7cc9513f 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -126,6 +126,8 @@
     "login/login_tab_helper.h",
     "managed_ui.cc",
     "managed_ui.h",
+    "omnibox/omnibox_pedal_implementations.cc",
+    "omnibox/omnibox_pedal_implementations.h",
     "page_info/chrome_page_info_delegate.cc",
     "page_info/chrome_page_info_delegate.h",
     "page_info/chrome_page_info_ui_delegate.cc",
@@ -1166,8 +1168,6 @@
       "omnibox/chrome_omnibox_navigation_observer.h",
       "omnibox/clipboard_utils.cc",
       "omnibox/clipboard_utils.h",
-      "omnibox/omnibox_pedal_implementations.cc",
-      "omnibox/omnibox_pedal_implementations.h",
       "omnibox/omnibox_tab_helper.cc",
       "omnibox/omnibox_tab_helper.h",
       "omnibox/omnibox_theme.cc",
diff --git a/chrome/browser/ui/android/omnibox/java/res/values/colors.xml b/chrome/browser/ui/android/omnibox/java/res/values/colors.xml
index 4292dce..68a72c6e 100644
--- a/chrome/browser/ui/android/omnibox/java/res/values/colors.xml
+++ b/chrome/browser/ui/android/omnibox/java/res/values/colors.xml
@@ -7,8 +7,8 @@
     <!-- Please see src/ui/android/java/res/values/colors.xml for the shared common colors. -->
 
     <!-- Omnibox Suggestion colors -->
-    <color name="suggestion_url_color">@color/default_text_color_link</color>
-    <color name="suggestion_url_color_incognito">@color/modern_blue_300</color>
+    <macro name="suggestion_url_color">@macro/default_text_color_link</macro>
+    <color name="suggestion_url_color_incognito">@color/default_text_color_link_light</color>
     <color name="answers_description_text_negative">@color/default_red</color>
     <color name="answers_description_text_positive">@color/default_green</color>
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java
index 5da39d5..b3dbe9e 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java
@@ -21,6 +21,7 @@
 import org.chromium.chrome.browser.omnibox.R;
 import org.chromium.chrome.browser.theme.ThemeUtils;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.ui.util.ColorUtils;
 
 /** Provides resources specific to Omnibox. */
@@ -217,10 +218,10 @@
             Context context, @BrandedColorScheme int brandedColorScheme) {
         // Suggestions are only shown when the omnibox is focused, hence LIGHT_THEME and DARK_THEME
         // are ignored as they don't change the result.
-        final @ColorRes int colorId = brandedColorScheme == BrandedColorScheme.INCOGNITO
-                ? R.color.suggestion_url_color_incognito
-                : R.color.suggestion_url_color;
-        return ApiCompatibilityUtils.getColor(context.getResources(), colorId);
+        final @ColorInt int color = brandedColorScheme == BrandedColorScheme.INCOGNITO
+                ? context.getColor(R.color.suggestion_url_color_incognito)
+                : SemanticColorUtils.getDefaultTextColorLink(context);
+        return color;
     }
 
     /**
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProviderTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProviderTest.java
index 77eba05c..a11ce92 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProviderTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProviderTest.java
@@ -25,6 +25,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 import org.chromium.components.browser_ui.styles.ChromeColors;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.testing.local.LocalRobolectricTestRunner;
 
 /** Tests for {@link OmniboxResourceProvider}. */
@@ -219,7 +220,7 @@
     public void getSuggestionUrlTextColor() {
         final Resources resources = mActivity.getResources();
         final int incognitoColor = resources.getColor(R.color.suggestion_url_color_incognito);
-        final int defaultColor = resources.getColor(R.color.suggestion_url_color);
+        final int defaultColor = SemanticColorUtils.getDefaultTextColorLink(mActivity);
 
         assertEquals("Wrong suggestion url text color for LIGHT_THEME.", defaultColor,
                 OmniboxResourceProvider.getSuggestionUrlTextColor(
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index f919bee..1d7f1101 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -5115,6 +5115,10 @@
         Can’t take a screenshot. Try again.
       </message>
 
+      <message name="IDS_LIGHTWEIGHT_REACTIONS_ERROR_MAX_REACTIONS_REACHED" desc="Message displayed in a toast when the user tries to add more than the maximum allowed reactions to the scene.">
+        You can add up to <ph name="MAX_NUM_REACTIONS">%1$d<ex>10</ex></ph> reactions
+      </message>
+
       <!-- Share Screenshot strings -->
       <message name="IDS_SCREENSHOT_EDIT_TITLE" desc="The text shown on the share option for screenshots.">
         Edit
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LIGHTWEIGHT_REACTIONS_ERROR_MAX_REACTIONS_REACHED.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LIGHTWEIGHT_REACTIONS_ERROR_MAX_REACTIONS_REACHED.png.sha1
new file mode 100644
index 0000000..9f3f792
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LIGHTWEIGHT_REACTIONS_ERROR_MAX_REACTIONS_REACHED.png.sha1
@@ -0,0 +1 @@
+736c007464f8a609a22928f4477530d1b32fc9e1
\ No newline at end of file
diff --git a/chrome/browser/ui/ash/projector/projector_app_client_impl.cc b/chrome/browser/ui/ash/projector/projector_app_client_impl.cc
index 2b1fa82c..737a4316 100644
--- a/chrome/browser/ui/ash/projector/projector_app_client_impl.cc
+++ b/chrome/browser/ui/ash/projector/projector_app_client_impl.cc
@@ -39,10 +39,11 @@
 void ProjectorAppClientImpl::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterBooleanPref(
-      ash::prefs::kProjectorCreationFlowEnabled, false,
+      ash::prefs::kProjectorCreationFlowEnabled, /*default_value=*/false,
       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
   registry->RegisterStringPref(
-      ash::prefs::kProjectorCreationFlowLanguage, kUsEnglishLocale,
+      ash::prefs::kProjectorCreationFlowLanguage,
+      /*default_value=*/kUsEnglishLocale,
       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
   registry->RegisterIntegerPref(
       ash::prefs::kProjectorGalleryOnboardingShowCount, 0,
@@ -50,6 +51,8 @@
   registry->RegisterIntegerPref(
       ash::prefs::kProjectorViewerOnboardingShowCount, 0,
       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
+  registry->RegisterBooleanPref(ash::prefs::kProjectorAllowByPolicy,
+                                /*default_value=*/false);
 }
 
 ProjectorAppClientImpl::ProjectorAppClientImpl()
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
index ad405b20..725e1f5 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
@@ -62,18 +62,16 @@
   if (previous && previous->delegate_.get() == delegate.get() &&
       previous->container_view() == container_view) {
     if (base::FeatureList::IsEnabled(
-            features::kAutofillDelayPopupControllerDeletion)) {
-      // Cancels pending deletions of |previous| that were scheduled by
-      // HideViewAndDie(). Otherwise, |previous| would might be destroyed
-      // prematurely.
-      previous->weak_ptr_factory_.InvalidateWeakPtrs();
+            features::kAutofillDelayPopupControllerDeletion) &&
+        previous->self_deletion_task_handle_.IsValid()) {
+      previous->self_deletion_task_handle_.CancelTask();
     }
     previous->SetElementBounds(element_bounds);
     previous->ClearState();
     return previous;
   }
 
-  if (previous.get())
+  if (previous)
     previous->Hide(PopupHidingReason::kViewDestroyed);
 
   AutofillPopupControllerImpl* controller = new AutofillPopupControllerImpl(
@@ -597,12 +595,18 @@
     return;
   }
 
-  base::SequencedTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(
-                     [](base::WeakPtr<AutofillPopupControllerImpl> weak_this) {
-                       delete weak_this.get();
-                     },
-                     GetWeakPtr()));
+  if (self_deletion_task_handle_.IsValid())
+    return;
+
+  self_deletion_task_handle_ =
+      base::SequencedTaskRunnerHandle::Get()->PostCancelableDelayedTask(
+          FROM_HERE,
+          base::BindOnce(
+              [](WeakPtr<AutofillPopupControllerImpl> weak_this) {
+                delete weak_this.get();
+              },
+              weak_ptr_factory_.GetWeakPtr()),
+          base::TimeDelta());
 }
 
 bool AutofillPopupControllerImpl::IsMouseLocked() const {
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.h b/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
index 11b69f0..3cab8b5 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
@@ -15,6 +15,7 @@
 #include "base/i18n/rtl.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/task/delayed_task_handle.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/autofill/autofill_popup_controller.h"
 #include "chrome/browser/ui/autofill/popup_controller_common.h"
@@ -215,6 +216,11 @@
   // line is currently selected.
   absl::optional<int> selected_line_;
 
+  // AutofillPopupControllerImpl deletes itself. To simplify memory management,
+  // we delete the object asynchronously if AutofillDelayPopupControllerDeletion
+  // is enabled (TODO(crbug.com/1277218): Remove "if" clause).
+  base::DelayedTaskHandle self_deletion_task_handle_;
+
   base::WeakPtrFactory<AutofillPopupControllerImpl> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/ui/omnibox/omnibox_pedal_implementations.cc b/chrome/browser/ui/omnibox/omnibox_pedal_implementations.cc
index 8d2b6c0..3456b7b 100644
--- a/chrome/browser/ui/omnibox/omnibox_pedal_implementations.cc
+++ b/chrome/browser/ui/omnibox/omnibox_pedal_implementations.cc
@@ -85,6 +85,7 @@
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalManagePasswords : public OmniboxPedal {
  public:
   OmniboxPedalManagePasswords()
@@ -122,9 +123,11 @@
  protected:
   ~OmniboxPedalManagePasswords() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalUpdateCreditCard : public OmniboxPedal {
  public:
   OmniboxPedalUpdateCreditCard()
@@ -163,9 +166,11 @@
  protected:
   ~OmniboxPedalUpdateCreditCard() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalLaunchIncognito : public OmniboxPedal {
  public:
   OmniboxPedalLaunchIncognito()
@@ -213,9 +218,11 @@
  protected:
   ~OmniboxPedalLaunchIncognito() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalTranslate : public OmniboxPedal {
  public:
   OmniboxPedalTranslate()
@@ -270,9 +277,11 @@
  protected:
   ~OmniboxPedalTranslate() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalUpdateChrome : public OmniboxPedal {
  public:
   OmniboxPedalUpdateChrome()
@@ -305,9 +314,11 @@
  protected:
   ~OmniboxPedalUpdateChrome() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalRunChromeSafetyCheck : public OmniboxPedal {
  public:
   OmniboxPedalRunChromeSafetyCheck()
@@ -351,9 +362,11 @@
  protected:
   ~OmniboxPedalRunChromeSafetyCheck() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalManageSecuritySettings : public OmniboxPedal {
  public:
   OmniboxPedalManageSecuritySettings()
@@ -392,9 +405,11 @@
  protected:
   ~OmniboxPedalManageSecuritySettings() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalManageCookies : public OmniboxPedal {
  public:
   OmniboxPedalManageCookies()
@@ -432,9 +447,11 @@
  protected:
   ~OmniboxPedalManageCookies() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalManageAddresses : public OmniboxPedal {
  public:
   OmniboxPedalManageAddresses()
@@ -472,9 +489,11 @@
  protected:
   ~OmniboxPedalManageAddresses() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalManageSync : public OmniboxPedal {
  public:
   OmniboxPedalManageSync()
@@ -507,9 +526,11 @@
  protected:
   ~OmniboxPedalManageSync() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalManageSiteSettings : public OmniboxPedal {
  public:
   OmniboxPedalManageSiteSettings()
@@ -543,9 +564,11 @@
  protected:
   ~OmniboxPedalManageSiteSettings() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalAuthRequired : public OmniboxPedal {
  public:
   explicit OmniboxPedalAuthRequired(OmniboxPedalId id,
@@ -560,9 +583,11 @@
  protected:
   ~OmniboxPedalAuthRequired() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalCreateGoogleDoc : public OmniboxPedalAuthRequired {
  public:
   OmniboxPedalCreateGoogleDoc()
@@ -604,9 +629,11 @@
  protected:
   ~OmniboxPedalCreateGoogleDoc() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalCreateGoogleSheet : public OmniboxPedalAuthRequired {
  public:
   OmniboxPedalCreateGoogleSheet()
@@ -648,9 +675,11 @@
  protected:
   ~OmniboxPedalCreateGoogleSheet() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalCreateGoogleSlide : public OmniboxPedalAuthRequired {
  public:
   OmniboxPedalCreateGoogleSlide()
@@ -692,9 +721,11 @@
  protected:
   ~OmniboxPedalCreateGoogleSlide() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalCreateGoogleCalendarEvent : public OmniboxPedalAuthRequired {
  public:
   OmniboxPedalCreateGoogleCalendarEvent()
@@ -738,9 +769,11 @@
  protected:
   ~OmniboxPedalCreateGoogleCalendarEvent() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalCreateGoogleSite : public OmniboxPedalAuthRequired {
  public:
   OmniboxPedalCreateGoogleSite()
@@ -783,9 +816,11 @@
  protected:
   ~OmniboxPedalCreateGoogleSite() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalCreateGoogleKeepNote : public OmniboxPedalAuthRequired {
  public:
   OmniboxPedalCreateGoogleKeepNote()
@@ -829,9 +864,11 @@
  protected:
   ~OmniboxPedalCreateGoogleKeepNote() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalCreateGoogleForm : public OmniboxPedalAuthRequired {
  public:
   OmniboxPedalCreateGoogleForm()
@@ -873,9 +910,11 @@
  protected:
   ~OmniboxPedalCreateGoogleForm() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalSeeChromeTips : public OmniboxPedal {
  public:
   OmniboxPedalSeeChromeTips()
@@ -913,9 +952,11 @@
  protected:
   ~OmniboxPedalSeeChromeTips() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalManageGoogleAccount : public OmniboxPedalAuthRequired {
  public:
   OmniboxPedalManageGoogleAccount()
@@ -959,9 +1000,11 @@
  protected:
   ~OmniboxPedalManageGoogleAccount() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalChangeGooglePassword : public OmniboxPedalAuthRequired {
  public:
   OmniboxPedalChangeGooglePassword()
@@ -1005,9 +1048,11 @@
  protected:
   ~OmniboxPedalChangeGooglePassword() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalCloseIncognitoWindows : public OmniboxPedal {
  public:
   OmniboxPedalCloseIncognitoWindows()
@@ -1066,9 +1111,11 @@
  protected:
   ~OmniboxPedalCloseIncognitoWindows() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalPlayChromeDinoGame : public OmniboxPedal {
  public:
   OmniboxPedalPlayChromeDinoGame()
@@ -1081,9 +1128,11 @@
                 IDS_ACC_OMNIBOX_PEDAL_PLAY_CHROME_DINO_GAME),
             GURL("chrome://dino")) {}
 
+#if defined(SUPPORT_PEDALS_VECTOR_ICONS)
   const gfx::VectorIcon& GetVectorIcon() const override {
     return omnibox::kDinoIcon;
   }
+#endif
 
   std::vector<SynonymGroupSpec> SpecifySynonymGroups(
       bool locale_is_english) const override {
@@ -1134,9 +1183,11 @@
  protected:
   ~OmniboxPedalPlayChromeDinoGame() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalFindMyPhone : public OmniboxPedalAuthRequired {
  public:
   OmniboxPedalFindMyPhone()
@@ -1172,9 +1223,11 @@
  protected:
   ~OmniboxPedalFindMyPhone() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalManageGooglePrivacy : public OmniboxPedalAuthRequired {
  public:
   OmniboxPedalManageGooglePrivacy()
@@ -1211,9 +1264,11 @@
  protected:
   ~OmniboxPedalManageGooglePrivacy() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalManageChromeSettings : public OmniboxPedal {
  public:
   OmniboxPedalManageChromeSettings()
@@ -1245,9 +1300,11 @@
  protected:
   ~OmniboxPedalManageChromeSettings() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalManageChromeDownloads : public OmniboxPedal {
  public:
   OmniboxPedalManageChromeDownloads()
@@ -1279,9 +1336,11 @@
  protected:
   ~OmniboxPedalManageChromeDownloads() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalViewChromeHistory : public OmniboxPedal {
  public:
   OmniboxPedalViewChromeHistory()
@@ -1313,9 +1372,11 @@
  protected:
   ~OmniboxPedalViewChromeHistory() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalShareThisPage : public OmniboxPedal {
  public:
   OmniboxPedalShareThisPage()
@@ -1360,9 +1421,11 @@
  protected:
   ~OmniboxPedalShareThisPage() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalManageChromeAccessibility : public OmniboxPedal {
  public:
   OmniboxPedalManageChromeAccessibility()
@@ -1394,9 +1457,11 @@
  protected:
   ~OmniboxPedalManageChromeAccessibility() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalManageChromeOSAccessibility : public OmniboxPedal {
  public:
   OmniboxPedalManageChromeOSAccessibility()
@@ -1432,9 +1497,11 @@
  protected:
   ~OmniboxPedalManageChromeOSAccessibility() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalCustomizeChromeFonts : public OmniboxPedal {
  public:
   OmniboxPedalCustomizeChromeFonts()
@@ -1466,9 +1533,11 @@
  protected:
   ~OmniboxPedalCustomizeChromeFonts() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalManageChromeThemes : public OmniboxPedal {
  public:
   OmniboxPedalManageChromeThemes()
@@ -1500,9 +1569,11 @@
  protected:
   ~OmniboxPedalManageChromeThemes() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
+#if !defined(OS_ANDROID)
 class OmniboxPedalCustomizeSearchEngines : public OmniboxPedal {
  public:
   OmniboxPedalCustomizeSearchEngines()
@@ -1534,6 +1605,7 @@
  protected:
   ~OmniboxPedalCustomizeSearchEngines() override = default;
 };
+#endif  // !defined(OS_ANDROID)
 
 // =============================================================================
 
@@ -1556,6 +1628,12 @@
     pedals.insert(std::make_pair(pedal->id(), base::WrapRefCounted(pedal)));
   };
 
+#if defined(OS_ANDROID)
+  if (OmniboxFieldTrial::IsPedalsAndroidBatch1Enabled()) {
+    add(new OmniboxPedalClearBrowsingData(incognito));
+  }
+#else  // defined(OS_ANDROID)
+
   add(new OmniboxPedalClearBrowsingData(incognito));
   add(new OmniboxPedalManagePasswords());
   add(new OmniboxPedalUpdateCreditCard());
@@ -1598,12 +1676,14 @@
     // platform is different from other desktop platforms.
     add(new OmniboxPedalShareThisPage());
     add(new OmniboxPedalManageChromeAccessibility());
-#else
+#else   // !defined(OS_CHROMEOS)
     add(new OmniboxPedalManageChromeOSAccessibility());
-#endif
+#endif  // !defined(OS_CHROMEOS)
     add(new OmniboxPedalCustomizeChromeFonts());
     add(new OmniboxPedalManageChromeThemes());
     add(new OmniboxPedalCustomizeSearchEngines());
   }
+#endif  // defined(OS_ANDROID)
+
   return pedals;
 }
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc
index 62a9fb4..8753a01 100644
--- a/chrome/browser/ui/search/search_tab_helper.cc
+++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/chrome_select_file_policy.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
-#include "chrome/browser/ui/search/ntp_user_data_logger.h"
 #include "chrome/browser/ui/search/omnibox_utils.h"
 #include "chrome/browser/ui/search/search_ipc_router_policy_impl.h"
 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
@@ -254,23 +253,8 @@
   if (!load_details.is_main_frame)
     return;
 
-  if (search::IsInstantNTP(web_contents())) {
-    // We (re)create the logger here because
-    // 1. The logger tries to detect whether the NTP is being created at startup
-    //    or from the user opening a new tab, and if we wait until later, it
-    //    won't correctly detect this case.
-    // 2. There can be multiple navigations to NTPs in a single web contents.
-    //    The navigations can be user-triggered or automatic, e.g. we fall back
-    //    to the local NTP if a remote NTP fails to load. Since logging should
-    //    be scoped to the life time of a single NTP we reset the logger every
-    //    time we reach a new NTP.
-    logger_ = std::make_unique<NTPUserDataLogger>(
-        Profile::FromBrowserContext(web_contents()->GetBrowserContext()),
-        // We use the NavigationController's URL since it might differ from the
-        // WebContents URL which is usually chrome://newtab/.
-        web_contents()->GetController().GetVisibleEntry()->GetURL());
+  if (search::IsInstantNTP(web_contents()))
     ipc_router_.SetInputInProgress(IsInputInProgress());
-  }
 
   if (InInstantProcess(instant_service_, web_contents()))
     ipc_router_.OnNavigationEntryCommitted();
diff --git a/chrome/browser/ui/search/search_tab_helper.h b/chrome/browser/ui/search/search_tab_helper.h
index 10ba3fb..21db2a12 100644
--- a/chrome/browser/ui/search/search_tab_helper.h
+++ b/chrome/browser/ui/search/search_tab_helper.h
@@ -40,7 +40,6 @@
 
 class GURL;
 class InstantService;
-class NTPUserDataLogger;
 class Profile;
 class SearchIPCRouterTest;
 class SkBitmap;
@@ -129,8 +128,6 @@
 
   bool is_setting_title_ = false;
 
-  std::unique_ptr<NTPUserDataLogger> logger_;
-
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 
   base::WeakPtrFactory<SearchTabHelper> weak_factory_{this};
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view_unittest.cc b/chrome/browser/ui/views/extensions/extensions_menu_view_unittest.cc
index 576c4c3..7e2d226 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_view_unittest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view_unittest.cc
@@ -33,7 +33,6 @@
 #include "extensions/common/extension.h"
 #include "extensions/test/test_extension_dir.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/events/event.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/layout/animating_layout_manager_test_util.h"
@@ -98,8 +97,6 @@
 
   std::vector<ToolbarActionView*> GetPinnedExtensionViews();
 
-  ToolbarActionView* GetPinnedExtensionView(const std::string& name);
-
   ExtensionsMenuItemView* GetExtensionsMenuItemView(const std::string& name);
 
   // Returns a list of the names of the currently pinned extensions, in order
@@ -171,19 +168,6 @@
   return result;
 }
 
-ToolbarActionView* ExtensionsMenuViewUnitTest::GetPinnedExtensionView(
-    const std::string& name) {
-  std::vector<ToolbarActionView*> actions = GetPinnedExtensionViews();
-  auto it = std::find_if(
-      actions.begin(), actions.end(), [name](ToolbarActionView* action) {
-        return base::UTF16ToUTF8(action->view_controller()->GetActionName()) ==
-               name;
-      });
-  if (it == actions.end())
-    return nullptr;
-  return *it;
-}
-
 ExtensionsMenuItemView* ExtensionsMenuViewUnitTest::GetExtensionsMenuItemView(
     const std::string& name) {
   base::flat_set<ExtensionsMenuItemView*> menu_items =
@@ -329,240 +313,6 @@
   EXPECT_THAT(GetPinnedExtensionNames(), testing::ElementsAre(kName));
 }
 
-TEST_F(ExtensionsMenuViewUnitTest, ReorderPinnedExtensions) {
-  constexpr char kName1[] = "Test 1";
-  InstallExtension(kName1);
-  constexpr char kName2[] = "Test 2";
-  InstallExtension(kName2);
-  constexpr char kName3[] = "Test 3";
-  InstallExtensionAndLayout(kName3);
-
-  EXPECT_EQ(3u, extensions_menu()->extensions_menu_items_for_testing().size());
-
-  for (const char* name : {kName1, kName2, kName3}) {
-    ExtensionsMenuItemView* item = GetExtensionsMenuItemView(name);
-    ASSERT_TRUE(item) << name;
-    ClickPinButton(item);
-    EXPECT_TRUE(extensions_container()->IsActionVisibleOnToolbar(
-        item->view_controller()));
-  }
-
-  WaitForAnimation();
-
-  EXPECT_THAT(GetPinnedExtensionNames(),
-              testing::ElementsAre(kName1, kName2, kName3));
-
-  // Simulate dragging "Test 3" to the first slot.
-  ToolbarActionView* drag_view = GetPinnedExtensionView(kName3);
-  ui::OSExchangeData drag_data;
-  extensions_container()->WriteDragDataForView(drag_view, gfx::Point(),
-                                               &drag_data);
-  gfx::PointF drop_point(GetPinnedExtensionView(kName1)->origin());
-  ui::DropTargetEvent drop_event(drag_data, drop_point, drop_point,
-                                 ui::DragDropTypes::DRAG_MOVE);
-  extensions_container()->OnDragUpdated(drop_event);
-  extensions_container()->OnPerformDrop(drop_event);
-  WaitForAnimation();
-
-  EXPECT_THAT(GetPinnedExtensionNames(),
-              testing::ElementsAre(kName3, kName1, kName2));
-}
-
-TEST_F(ExtensionsMenuViewUnitTest, RunDropCallback) {
-  constexpr char kName1[] = "Test 1";
-  auto ext1 = InstallExtension(kName1);
-  constexpr char kName2[] = "Test 2";
-  auto ext2 = InstallExtension(kName2);
-  constexpr char kName3[] = "Test 3";
-  auto ext3 = InstallExtensionAndLayout(kName3);
-
-  auto* toolbar_model = ToolbarActionsModel::Get(profile());
-  ASSERT_TRUE(toolbar_model);
-
-  toolbar_model->SetActionVisibility(ext1->id(), true);
-  toolbar_model->SetActionVisibility(ext2->id(), true);
-  toolbar_model->SetActionVisibility(ext3->id(), true);
-  WaitForAnimation();
-
-  EXPECT_THAT(GetPinnedExtensionNames(),
-              testing::ElementsAre(kName1, kName2, kName3));
-
-  // Simulate dragging "Test 3" to the first slot.
-  ToolbarActionView* drag_view = GetPinnedExtensionView(kName3);
-  ui::OSExchangeData drag_data;
-  extensions_container()->WriteDragDataForView(drag_view, gfx::Point(),
-                                               &drag_data);
-  gfx::PointF drop_point(GetPinnedExtensionView(kName1)->origin());
-  ui::DropTargetEvent drop_event(drag_data, drop_point, drop_point,
-                                 ui::DragDropTypes::DRAG_MOVE);
-  extensions_container()->OnDragUpdated(drop_event);
-  auto cb = extensions_container()->GetDropCallback(drop_event);
-  ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone;
-  std::move(cb).Run(drop_event, output_drag_op);
-  WaitForAnimation();
-
-  EXPECT_THAT(GetPinnedExtensionNames(),
-              testing::ElementsAre(kName3, kName1, kName2));
-  EXPECT_EQ(output_drag_op, ui::mojom::DragOperation::kMove);
-}
-
-TEST_F(ExtensionsMenuViewUnitTest, ResetDropCallback) {
-  constexpr char kName1[] = "Test 1";
-  auto ext1 = InstallExtension(kName1);
-  constexpr char kName2[] = "Test 2";
-  auto ext2 = InstallExtension(kName2);
-  constexpr char kName3[] = "Test 3";
-  auto ext3 = InstallExtensionAndLayout(kName3);
-
-  auto* toolbar_model = ToolbarActionsModel::Get(profile());
-  ASSERT_TRUE(toolbar_model);
-
-  toolbar_model->SetActionVisibility(ext1->id(), true);
-  toolbar_model->SetActionVisibility(ext2->id(), true);
-  toolbar_model->SetActionVisibility(ext3->id(), true);
-  WaitForAnimation();
-
-  EXPECT_THAT(GetPinnedExtensionNames(),
-              testing::ElementsAre(kName1, kName2, kName3));
-
-  // Simulate dragging "Test 3" to the first slot.
-  ToolbarActionView* drag_view = GetPinnedExtensionView(kName3);
-  ui::OSExchangeData drag_data;
-  extensions_container()->WriteDragDataForView(drag_view, gfx::Point(),
-                                               &drag_data);
-  gfx::PointF drop_point(GetPinnedExtensionView(kName1)->origin());
-  ui::DropTargetEvent drop_event(drag_data, drop_point, drop_point,
-                                 ui::DragDropTypes::DRAG_MOVE);
-  extensions_container()->OnDragUpdated(drop_event);
-  auto cb = extensions_container()->GetDropCallback(drop_event);
-  WaitForAnimation();
-
-  EXPECT_THAT(GetPinnedExtensionNames(),
-              testing::ElementsAre(kName3, kName1, kName2));
-
-  // If the drop callback is reset (and never invoked), the drag should be
-  // aborted, and items should be back in their original order.
-  cb.Reset();
-  WaitForAnimation();
-
-  EXPECT_THAT(GetPinnedExtensionNames(),
-              testing::ElementsAre(kName1, kName2, kName3));
-}
-
-TEST_F(ExtensionsMenuViewUnitTest, InvalidateDropCallbackOnActionAdded) {
-  constexpr char kName1[] = "Test 1";
-  auto ext1 = InstallExtension(kName1);
-  constexpr char kName2[] = "Test 2";
-  auto ext2 = InstallExtensionAndLayout(kName2);
-
-  auto* toolbar_model = ToolbarActionsModel::Get(profile());
-  ASSERT_TRUE(toolbar_model);
-
-  toolbar_model->SetActionVisibility(ext1->id(), true);
-  toolbar_model->SetActionVisibility(ext2->id(), true);
-  WaitForAnimation();
-
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::ElementsAre(kName1, kName2));
-
-  // Simulate dragging "Test 2" to the first slot.
-  ToolbarActionView* drag_view = GetPinnedExtensionView(kName2);
-  ui::OSExchangeData drag_data;
-  extensions_container()->WriteDragDataForView(drag_view, gfx::Point(),
-                                               &drag_data);
-  gfx::PointF drop_point(GetPinnedExtensionView(kName1)->origin());
-  ui::DropTargetEvent drop_event(drag_data, drop_point, drop_point,
-                                 ui::DragDropTypes::DRAG_MOVE);
-  extensions_container()->OnDragUpdated(drop_event);
-  auto cb = extensions_container()->GetDropCallback(drop_event);
-  WaitForAnimation();
-
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::ElementsAre(kName2, kName1));
-
-  constexpr char kName3[] = "Test 3";
-  auto ext3 = InstallExtensionAndLayout(kName3);
-  toolbar_model->SetActionVisibility(ext3->id(), true);
-  WaitForAnimation();
-
-  // The drop callback should be invalidated, and items should be back in their
-  // original order.
-  ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone;
-  std::move(cb).Run(drop_event, output_drag_op);
-  WaitForAnimation();
-
-  EXPECT_THAT(GetPinnedExtensionNames(),
-              testing::ElementsAre(kName1, kName2, kName3));
-}
-
-// ToolbarActionsModel::MovePinnedAction crashes if pinned extensions changes
-// while the drop callback isn't invalidated. This test makes sure this doesn't
-// happen anymore. https://crbug.com/1268239.
-TEST_F(ExtensionsMenuViewUnitTest, InvalidateDropCallbackOnPrefChange) {
-  constexpr char kName1[] = "Test 1";
-  auto ext1 = InstallExtension(kName1);
-  constexpr char kName2[] = "Test 2";
-  auto ext2 = InstallExtensionAndLayout(kName2);
-
-  auto* toolbar_model = ToolbarActionsModel::Get(profile());
-  ASSERT_TRUE(toolbar_model);
-
-  toolbar_model->SetActionVisibility(ext1->id(), true);
-  toolbar_model->SetActionVisibility(ext2->id(), true);
-  WaitForAnimation();
-
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::ElementsAre(kName1, kName2));
-
-  // Simulate dragging "Test 2" to the first slot.
-  ToolbarActionView* drag_view = GetPinnedExtensionView(kName2);
-  ui::OSExchangeData drag_data;
-  extensions_container()->WriteDragDataForView(drag_view, gfx::Point(),
-                                               &drag_data);
-  gfx::PointF drop_point(GetPinnedExtensionView(kName1)->origin());
-  ui::DropTargetEvent drop_event(drag_data, drop_point, drop_point,
-                                 ui::DragDropTypes::DRAG_MOVE);
-  extensions_container()->OnDragUpdated(drop_event);
-  auto cb = extensions_container()->GetDropCallback(drop_event);
-  WaitForAnimation();
-
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::ElementsAre(kName2, kName1));
-
-  extensions::ExtensionPrefs::Get(profile())->SetPinnedExtensions({});
-  WaitForAnimation();
-
-  // The drop callback should be invalidated, and items should be back in their
-  // original order.
-  ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone;
-  std::move(cb).Run(drop_event, output_drag_op);
-  WaitForAnimation();
-
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::ElementsAre());
-}
-
-TEST_F(ExtensionsMenuViewUnitTest, PinnedExtensionsReorderOnPrefChange) {
-  constexpr char kName1[] = "Test 1";
-  const extensions::ExtensionId id1 = InstallExtension(kName1)->id();
-  constexpr char kName2[] = "Test 2";
-  const extensions::ExtensionId id2 = InstallExtension(kName2)->id();
-  constexpr char kName3[] = "Test 3";
-  const extensions::ExtensionId id3 = InstallExtensionAndLayout(kName3)->id();
-
-  for (const char* name : {kName1, kName2, kName3}) {
-    ExtensionsMenuItemView* item = GetExtensionsMenuItemView(name);
-    ASSERT_TRUE(item) << name;
-    ClickPinButton(item);
-  }
-  WaitForAnimation();
-
-  EXPECT_THAT(GetPinnedExtensionNames(),
-              testing::ElementsAre(kName1, kName2, kName3));
-
-  extensions::ExtensionPrefs::Get(profile())->SetPinnedExtensions(
-      {id2, id3, id1});
-  WaitForAnimation();
-
-  EXPECT_THAT(GetPinnedExtensionNames(),
-              testing::ElementsAre(kName2, kName3, kName1));
-}
-
 TEST_F(ExtensionsMenuViewUnitTest, PinnedExtensionLayout) {
   for (int i = 0; i < 3; i++)
     InstallExtensionAndLayout(base::StringPrintf("Test %d", i));
diff --git a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc
index 1dfe4ebe..1f86629 100644
--- a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc
@@ -7,12 +7,15 @@
 #include "base/feature_list.h"
 #include "base/test/metrics/user_action_tester.h"
 #include "build/build_config.h"
+#include "chrome/browser/extensions/chrome_test_extension_loader.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/extensions/extensions_menu_button.h"
 #include "chrome/browser/ui/views/extensions/extensions_menu_item_view.h"
 #include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h"
 #include "chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h"
-#include "testing/gmock/include/gmock/gmock.h"
+#include "extensions/browser/test_extension_registry_observer.h"
+#include "extensions/test/test_extension_dir.h"
 #include "ui/views/view_utils.h"
 
 namespace {
@@ -85,14 +88,6 @@
   // Asserts there is exactly 1 menu item and then returns it.
   ExtensionsMenuItemView* GetOnlyInstalledMenuItem();
 
-  // Returns a list of the views of the currently pinned extensions, in order
-  // from left to right.
-  std::vector<ToolbarActionView*> GetPinnedExtensionViews();
-
-  // Returns a list of the names of the currently pinned extensions, in order
-  // from left to right.
-  std::vector<std::string> GetPinnedExtensionNames();
-
   void ShowTabbedMenu();
 
   void ClickSiteAccessButton();
@@ -123,41 +118,6 @@
   return *items.begin();
 }
 
-std::vector<ToolbarActionView*>
-ExtensionsTabbedMenuViewUnitTest::GetPinnedExtensionViews() {
-  std::vector<ToolbarActionView*> result;
-  for (views::View* child : extensions_container()->children()) {
-    // Ensure we don't downcast the ExtensionsToolbarButton.
-    if (views::IsViewClass<ToolbarActionView>(child)) {
-      ToolbarActionView* const action = static_cast<ToolbarActionView*>(child);
-#if defined(OS_MAC)
-      // TODO(crbug.com/1045212): Use IsActionVisibleOnToolbar() because it
-      // queries the underlying model and not GetVisible(), as that relies on an
-      // animation running, which is not reliable in unit tests on Mac.
-      const bool is_visible = extensions_container()->IsActionVisibleOnToolbar(
-          action->view_controller());
-#else
-      const bool is_visible = action->GetVisible();
-#endif
-      if (is_visible)
-        result.push_back(action);
-    }
-  }
-  return result;
-}
-
-std::vector<std::string>
-ExtensionsTabbedMenuViewUnitTest::GetPinnedExtensionNames() {
-  std::vector<ToolbarActionView*> views = GetPinnedExtensionViews();
-  std::vector<std::string> result;
-  result.resize(views.size());
-  std::transform(
-      views.begin(), views.end(), result.begin(), [](ToolbarActionView* view) {
-        return base::UTF16ToUTF8(view->view_controller()->GetActionName());
-      });
-  return result;
-}
-
 void ExtensionsTabbedMenuViewUnitTest::ShowTabbedMenu() {
   ExtensionsTabbedMenuView::ShowBubble(
       extensions_button(), browser(), extensions_container(),
@@ -264,7 +224,7 @@
 }
 
 TEST_F(ExtensionsTabbedMenuViewUnitTest,
-       InstalledExtensionsAreShownInInstalledTab) {
+       InstalledTab_InstalledExtensionsAreShownInInstalledTab) {
   // To start, there should be no extensions in the menu.
   EXPECT_EQ(installed_items().size(), 0u);
 
@@ -298,9 +258,9 @@
   ASSERT_EQ(items.size(), 4u);
 
   // Basic std::sort would do A,C,Z,b however we want A,b,C,Z
-  EXPECT_THAT(GetNamesFromInstalledItems(items),
-              testing::ElementsAre(kExtensionAName, kExtensionBName,
-                                   kExtensionCName, kExtensionZName));
+  std::vector<std::string> expected_items{kExtensionAName, kExtensionBName,
+                                          kExtensionCName, kExtensionZName};
+  EXPECT_EQ(GetNamesFromInstalledItems(items), expected_items);
 }
 
 TEST_F(ExtensionsTabbedMenuViewUnitTest,
@@ -314,18 +274,18 @@
   ASSERT_TRUE(installed_item);
   ToolbarActionViewController* controller = installed_item->view_controller();
   EXPECT_FALSE(extensions_container()->IsActionVisibleOnToolbar(controller));
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::IsEmpty());
+  EXPECT_EQ(GetPinnedExtensionNames(), std::vector<std::string>{});
 
   ClickPinButton(installed_item);
 
   EXPECT_TRUE(extensions_container()->IsActionVisibleOnToolbar(controller));
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::ElementsAre(kName));
+  EXPECT_EQ(GetPinnedExtensionNames(), std::vector<std::string>{kName});
 
   ClickPinButton(installed_item);  // Unpin.
 
   EXPECT_FALSE(extensions_container()->IsActionVisibleOnToolbar(
       installed_item->view_controller()));
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::IsEmpty());
+  EXPECT_EQ(GetPinnedExtensionNames(), std::vector<std::string>{});
 }
 
 TEST_F(ExtensionsTabbedMenuViewUnitTest,
@@ -338,40 +298,57 @@
   InstallExtension(kExtensionC);
 
   ShowTabbedMenu();
-
   std::vector<ExtensionsMenuItemView*> items = installed_items();
-  EXPECT_EQ(items.size(), 3u);
 
   // Verify the order of the extensions is A,B,C.
-  ASSERT_THAT(GetNamesFromInstalledItems(items),
-              testing::ElementsAre(kExtensionA, kExtensionB, kExtensionC));
+  {
+    EXPECT_EQ(items.size(), 3u);
+    std::vector<std::string> expected_items{kExtensionA, kExtensionB,
+                                            kExtensionC};
+    EXPECT_EQ(GetNamesFromInstalledItems(items), expected_items);
+  }
 
-  ClickPinButton(items.at(0));
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::ElementsAre(kExtensionA));
+  // Pinning an extension should add it to the toolbar.
+  {
+    ClickPinButton(items.at(0));
+    std::vector<std::string> expected_names{kExtensionA};
+    EXPECT_EQ(GetPinnedExtensionNames(), expected_names);
+  }
 
   // Pinning a second extension should add it to the right of the current pinned
   // extensions.
-  ClickPinButton(items.at(1));
-  EXPECT_THAT(GetPinnedExtensionNames(),
-              testing::ElementsAre(kExtensionA, kExtensionB));
+
+  {
+    ClickPinButton(items.at(1));
+    std::vector<std::string> expected_names{kExtensionA, kExtensionB};
+    EXPECT_EQ(GetPinnedExtensionNames(), expected_names);
+  }
 
   // Pinning a third extension should add it to the right of the current pinned
   // extensions.
-  ClickPinButton(items.at(2));
-  EXPECT_THAT(GetPinnedExtensionNames(),
-              testing::ElementsAre(kExtensionA, kExtensionB, kExtensionC));
+  {
+    ClickPinButton(items.at(2));
+    std::vector<std::string> expected_names{kExtensionA, kExtensionB,
+                                            kExtensionC};
+    EXPECT_EQ(GetPinnedExtensionNames(), expected_names);
+  }
 
   // Unpinning the middle extension should remove it from the toolbar without
   // affecting the order of the other pinned extensions.
-  ClickPinButton(items.at(1));
-  EXPECT_THAT(GetPinnedExtensionNames(),
-              testing::ElementsAre(kExtensionA, kExtensionC));
+  {
+    ClickPinButton(items.at(1));
+    std::vector<std::string> expected_names{kExtensionA, kExtensionC};
+    EXPECT_EQ(GetPinnedExtensionNames(), expected_names);
+  }
 
   // Pinning an extension should add it to the right of the current pinned
   // extensions, even if it was pinned and unpinned previously.
-  ClickPinButton(items.at(1));
-  EXPECT_THAT(GetPinnedExtensionNames(),
-              testing::ElementsAre(kExtensionA, kExtensionC, kExtensionB));
+  {
+    ClickPinButton(items.at(1));
+    std::vector<std::string> expected_names{kExtensionA, kExtensionC,
+                                            kExtensionB};
+    EXPECT_EQ(GetPinnedExtensionNames(), expected_names);
+  }
 }
 
 TEST_F(ExtensionsTabbedMenuViewUnitTest,
@@ -411,10 +388,12 @@
   ShowTabbedMenu();
 
   // Verify the order of the extensions is A,C.
-  std::vector<ExtensionsMenuItemView*> items = installed_items();
-  ASSERT_EQ(items.size(), 2u);
-  EXPECT_THAT(GetNamesFromInstalledItems(items),
-              testing::ElementsAre(kExtensionA, kExtensionC));
+  {
+    std::vector<ExtensionsMenuItemView*> items = installed_items();
+    ASSERT_EQ(items.size(), 2u);
+    std::vector<std::string> expected_names{kExtensionA, kExtensionC};
+    EXPECT_EQ(GetNamesFromInstalledItems(items), expected_names);
+  }
 
   // Add a new extension while the menu is open.
   constexpr char kExtensionB[] = "B Extension";
@@ -423,20 +402,133 @@
 
   // Extension should be added in the correct place.
   // Verify the new order is A,B,C.
-  items = installed_items();
-  ASSERT_EQ(items.size(), 3u);
-  EXPECT_THAT(GetNamesFromInstalledItems(items),
-              testing::ElementsAre(kExtensionA, kExtensionB, kExtensionC));
+  {
+    std::vector<ExtensionsMenuItemView*> items = installed_items();
+    ASSERT_EQ(items.size(), 3u);
+    std::vector<std::string> expected_names{kExtensionA, kExtensionB,
+                                            kExtensionC};
+    EXPECT_EQ(GetNamesFromInstalledItems(items), expected_names);
+  }
 
   // Remove a extension while the menu is open
   UninstallExtension(extensionB->id());
   LayoutMenuIfNecessary();
 
   // Verify the new order is A,C.
-  items = installed_items();
-  ASSERT_EQ(items.size(), 2u);
-  EXPECT_THAT(GetNamesFromInstalledItems(items),
-              testing::ElementsAre(kExtensionA, kExtensionC));
+  {
+    std::vector<ExtensionsMenuItemView*> items = installed_items();
+    ASSERT_EQ(items.size(), 2u);
+    std::vector<std::string> expected_names{kExtensionA, kExtensionC};
+    EXPECT_EQ(GetNamesFromInstalledItems(items), expected_names);
+  }
+}
+
+TEST_F(ExtensionsTabbedMenuViewUnitTest,
+       InstalledTab_DisableAndEnableExtension) {
+  constexpr char kName[] = "Test Extension";
+  auto extension_id = InstallExtension(kName)->id();
+
+  ShowTabbedMenu();
+
+  ExtensionsMenuItemView* menu_item = GetOnlyInstalledMenuItem();
+  EXPECT_EQ(installed_items().size(), 1u);
+  ClickPinButton(menu_item);
+
+  DisableExtension(extension_id);
+  LayoutMenuIfNecessary();
+  WaitForAnimation();
+
+  EXPECT_EQ(installed_items().size(), 0u);
+  EXPECT_EQ(GetPinnedExtensionNames(), std::vector<std::string>{});
+
+  EnableExtension(extension_id);
+  LayoutMenuIfNecessary();
+  WaitForAnimation();
+
+  EXPECT_EQ(installed_items().size(), 1u);
+  EXPECT_EQ(GetPinnedExtensionNames(), std::vector<std::string>{kName});
+}
+
+// Tests that when an extension is reloaded it remains visible in the toolbar
+// and extensions menu.
+TEST_F(ExtensionsTabbedMenuViewUnitTest, InstalledTab_ReloadExtension) {
+  // The extension must have a manifest to be reloaded.
+  extensions::TestExtensionDir extension_directory;
+  constexpr char kManifest[] = R"({
+        "name": "Test Extension",
+        "version": "1",
+        "manifest_version": 3
+      })";
+  extension_directory.WriteManifest(kManifest);
+  extensions::ChromeTestExtensionLoader loader(profile());
+  scoped_refptr<const extensions::Extension> extension =
+      loader.LoadExtension(extension_directory.UnpackedPath());
+
+  ShowTabbedMenu();
+
+  ExtensionsMenuItemView* installed_item = GetOnlyInstalledMenuItem();
+  EXPECT_EQ(installed_items().size(), 1u);
+
+  ClickPinButton(installed_item);
+  EXPECT_TRUE(extensions_container()->IsActionVisibleOnToolbar(
+      installed_item->view_controller()));
+
+  // Reload the extension.
+  extensions::TestExtensionRegistryObserver registry_observer(
+      extensions::ExtensionRegistry::Get(profile()));
+  ReloadExtension(extension->id());
+  ASSERT_TRUE(registry_observer.WaitForExtensionLoaded());
+  LayoutMenuIfNecessary();
+
+  // Verify the extension is visible in the menu and on the toolbar.
+  installed_item = GetOnlyInstalledMenuItem();
+  EXPECT_EQ(installed_items().size(), 1u);
+  EXPECT_TRUE(extensions_container()->IsActionVisibleOnToolbar(
+      installed_item->view_controller()));
+}
+
+// Tests that a when an extension is reloaded with manifest errors, and
+// therefore fails to be loaded into Chrome, it's removed from the toolbar and
+// extensions menu.
+TEST_F(ExtensionsTabbedMenuViewUnitTest, InstalledTab_ReloadExtensionFailed) {
+  extensions::TestExtensionDir extension_directory;
+  constexpr char kManifest[] = R"({
+        "name": "Test Extension",
+        "version": "1",
+        "manifest_version": 3
+      })";
+  extension_directory.WriteManifest(kManifest);
+  extensions::ChromeTestExtensionLoader loader(profile());
+  scoped_refptr<const extensions::Extension> extension =
+      loader.LoadExtension(extension_directory.UnpackedPath());
+
+  ShowTabbedMenu();
+
+  ExtensionsMenuItemView* installed_item = GetOnlyInstalledMenuItem();
+  EXPECT_EQ(installed_items().size(), 1u);
+
+  ClickPinButton(installed_item);
+  EXPECT_TRUE(extensions_container()->IsActionVisibleOnToolbar(
+      installed_item->view_controller()));
+
+  // Replace the extension's valid manifest with one containing errors. In this
+  // case, 'version' keys is missing.
+  constexpr char kManifestWithErrors[] = R"({
+        "name": "Test",
+        "manifest_version": 3,
+      })";
+  extension_directory.WriteManifest(kManifestWithErrors);
+
+  // Reload the extension. It should fail due to the manifest errors.
+  extension_service()->ReloadExtensionWithQuietFailure(extension->id());
+  base::RunLoop().RunUntilIdle();
+  LayoutMenuIfNecessary();
+
+  // Verify the extension is no longer visible in the menu or on the toolbar
+  // since it was removed.
+  EXPECT_EQ(installed_items().size(), 0u);
+  for (views::View* child : extensions_container()->children())
+    EXPECT_FALSE(views::IsViewClass<ToolbarActionView>(child));
 }
 
 TEST_F(ExtensionsTabbedMenuViewUnitTest, WindowTitle) {
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container_unittest.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_container_unittest.cc
new file mode 100644
index 0000000..2a4e1ff
--- /dev/null
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container_unittest.cc
@@ -0,0 +1,300 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h"
+
+#include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
+#include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h"
+#include "extensions/common/extension_id.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+
+class ExtensionsToolbarContainerUnitTest : public ExtensionsToolbarUnitTest {
+ public:
+  ExtensionsToolbarContainerUnitTest();
+  ~ExtensionsToolbarContainerUnitTest() override = default;
+  ExtensionsToolbarContainerUnitTest(
+      const ExtensionsToolbarContainerUnitTest&) = delete;
+  ExtensionsToolbarContainerUnitTest& operator=(
+      const ExtensionsToolbarContainerUnitTest&) = delete;
+
+  // Returns the view of the given `extension_id` if the extension is currently
+  // pinned.
+  ToolbarActionView* GetPinnedExtensionView(
+      const extensions::ExtensionId& extension_id);
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+ExtensionsToolbarContainerUnitTest::ExtensionsToolbarContainerUnitTest() {
+  scoped_feature_list_.InitAndEnableFeature(
+      features::kExtensionsMenuAccessControl);
+}
+
+ToolbarActionView* ExtensionsToolbarContainerUnitTest::GetPinnedExtensionView(
+    const extensions::ExtensionId& extension_id) {
+  std::vector<ToolbarActionView*> actions = GetPinnedExtensionViews();
+  auto it =
+      std::find_if(actions.begin(), actions.end(),
+                   [extension_id](ToolbarActionView* action) {
+                     return action->view_controller()->GetId() == extension_id;
+                   });
+  if (it == actions.end())
+    return nullptr;
+  return *it;
+}
+
+TEST_F(ExtensionsToolbarContainerUnitTest, ReorderPinnedExtensions) {
+  constexpr char kExtensionAName[] = "A Extension";
+  auto extensionA = InstallExtension(kExtensionAName);
+  constexpr char kExtensionBName[] = "B Extension";
+  auto extensionB = InstallExtension(kExtensionBName);
+  constexpr char kExtensionCName[] = "C Extension";
+  auto extensionC = InstallExtension(kExtensionCName);
+
+  auto* toolbar_model = ToolbarActionsModel::Get(profile());
+  ASSERT_TRUE(toolbar_model);
+
+  toolbar_model->SetActionVisibility(extensionA->id(), true);
+  toolbar_model->SetActionVisibility(extensionB->id(), true);
+  toolbar_model->SetActionVisibility(extensionC->id(), true);
+  WaitForAnimation();
+
+  // Verify the order is A, B, C.
+  EXPECT_THAT(
+      GetPinnedExtensionNames(),
+      testing::ElementsAre(kExtensionAName, kExtensionBName, kExtensionCName));
+
+  // Simulate dragging extension C to the first slot.
+  ToolbarActionView* drag_view = GetPinnedExtensionView(extensionC->id());
+  ui::OSExchangeData drag_data;
+  extensions_container()->WriteDragDataForView(drag_view, gfx::Point(),
+                                               &drag_data);
+  gfx::PointF drop_point(GetPinnedExtensionView(extensionA->id())->origin());
+  ui::DropTargetEvent drop_event(drag_data, drop_point, drop_point,
+                                 ui::DragDropTypes::DRAG_MOVE);
+  extensions_container()->OnDragUpdated(drop_event);
+  extensions_container()->OnPerformDrop(drop_event);
+  WaitForAnimation();
+
+  // Verify the new order is C, A, B.
+  EXPECT_THAT(
+      GetPinnedExtensionNames(),
+      testing::ElementsAre(kExtensionCName, kExtensionAName, kExtensionBName));
+}
+
+TEST_F(ExtensionsToolbarContainerUnitTest,
+       PinnedExtensionsReorderOnPrefChange) {
+  constexpr char kExtensionAName[] = "A Extension";
+  auto extensionA = InstallExtension(kExtensionAName);
+  constexpr char kExtensionBName[] = "B Extension";
+  auto extensionB = InstallExtension(kExtensionBName);
+  constexpr char kExtensionCName[] = "C Extension";
+  auto extensionC = InstallExtension(kExtensionCName);
+
+  auto* toolbar_model = ToolbarActionsModel::Get(profile());
+  ASSERT_TRUE(toolbar_model);
+
+  toolbar_model->SetActionVisibility(extensionA->id(), true);
+  toolbar_model->SetActionVisibility(extensionB->id(), true);
+  toolbar_model->SetActionVisibility(extensionC->id(), true);
+  WaitForAnimation();
+
+  // Verify the order is A, B, C.
+  EXPECT_THAT(
+      GetPinnedExtensionNames(),
+      testing::ElementsAre(kExtensionAName, kExtensionBName, kExtensionCName));
+
+  // Set the order using prefs.
+  extensions::ExtensionPrefs::Get(profile())->SetPinnedExtensions(
+      {extensionB->id(), extensionC->id(), extensionA->id()});
+  WaitForAnimation();
+
+  // Verify the new order is B, C, A.
+  EXPECT_THAT(
+      GetPinnedExtensionNames(),
+      testing::ElementsAre(kExtensionBName, kExtensionCName, kExtensionAName));
+}
+
+TEST_F(ExtensionsToolbarContainerUnitTest, RunDropCallback) {
+  constexpr char kExtensionAName[] = "A Extension";
+  auto extensionA = InstallExtension(kExtensionAName);
+  constexpr char kExtensionBName[] = "B Extension";
+  auto extensionB = InstallExtension(kExtensionBName);
+  constexpr char kExtensionCName[] = "C Extension";
+  auto extensionC = InstallExtension(kExtensionCName);
+
+  auto* toolbar_model = ToolbarActionsModel::Get(profile());
+  ASSERT_TRUE(toolbar_model);
+
+  toolbar_model->SetActionVisibility(extensionA->id(), true);
+  toolbar_model->SetActionVisibility(extensionB->id(), true);
+  toolbar_model->SetActionVisibility(extensionC->id(), true);
+  WaitForAnimation();
+
+  EXPECT_THAT(
+      GetPinnedExtensionNames(),
+      testing::ElementsAre(kExtensionAName, kExtensionBName, kExtensionCName));
+
+  // Simulate dragging extension C to the first slot.
+  ToolbarActionView* drag_view = GetPinnedExtensionView(extensionC->id());
+  ui::OSExchangeData drag_data;
+  extensions_container()->WriteDragDataForView(drag_view, gfx::Point(),
+                                               &drag_data);
+  gfx::PointF drop_point(GetPinnedExtensionView(extensionA->id())->origin());
+  ui::DropTargetEvent drop_event(drag_data, drop_point, drop_point,
+                                 ui::DragDropTypes::DRAG_MOVE);
+  extensions_container()->OnDragUpdated(drop_event);
+  auto cb = extensions_container()->GetDropCallback(drop_event);
+  ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone;
+  std::move(cb).Run(drop_event, output_drag_op);
+  WaitForAnimation();
+
+  EXPECT_THAT(
+      GetPinnedExtensionNames(),
+      testing::ElementsAre(kExtensionCName, kExtensionAName, kExtensionBName));
+  EXPECT_EQ(output_drag_op, ui::mojom::DragOperation::kMove);
+}
+
+TEST_F(ExtensionsToolbarContainerUnitTest, ResetDropCallback) {
+  constexpr char kExtensionAName[] = "A Extension";
+  auto extensionA = InstallExtension(kExtensionAName);
+  constexpr char kExtensionBName[] = "B Extension";
+  auto extensionB = InstallExtension(kExtensionBName);
+  constexpr char kExtensionCName[] = "C Extension";
+  auto extensionC = InstallExtension(kExtensionCName);
+
+  auto* toolbar_model = ToolbarActionsModel::Get(profile());
+  ASSERT_TRUE(toolbar_model);
+
+  toolbar_model->SetActionVisibility(extensionA->id(), true);
+  toolbar_model->SetActionVisibility(extensionB->id(), true);
+  toolbar_model->SetActionVisibility(extensionC->id(), true);
+  WaitForAnimation();
+
+  EXPECT_THAT(
+      GetPinnedExtensionNames(),
+      testing::ElementsAre(kExtensionAName, kExtensionBName, kExtensionCName));
+
+  // Simulate dragging "C Extension" to the first slot.
+  ToolbarActionView* drag_view = GetPinnedExtensionView(extensionC->id());
+  ui::OSExchangeData drag_data;
+  extensions_container()->WriteDragDataForView(drag_view, gfx::Point(),
+                                               &drag_data);
+  gfx::PointF drop_point(GetPinnedExtensionView(extensionA->id())->origin());
+  ui::DropTargetEvent drop_event(drag_data, drop_point, drop_point,
+                                 ui::DragDropTypes::DRAG_MOVE);
+  extensions_container()->OnDragUpdated(drop_event);
+  auto cb = extensions_container()->GetDropCallback(drop_event);
+  WaitForAnimation();
+
+  EXPECT_THAT(
+      GetPinnedExtensionNames(),
+      testing::ElementsAre(kExtensionCName, kExtensionAName, kExtensionBName));
+
+  // If the drop callback is reset (and never invoked), the drag should be
+  // aborted, and items should be back in their original order.
+  cb.Reset();
+  WaitForAnimation();
+
+  EXPECT_THAT(
+      GetPinnedExtensionNames(),
+      testing::ElementsAre(kExtensionAName, kExtensionBName, kExtensionCName));
+}
+
+TEST_F(ExtensionsToolbarContainerUnitTest,
+       InvalidateDropCallbackOnActionAdded) {
+  constexpr char kExtensionAName[] = "A Extension";
+  auto extensionA = InstallExtension(kExtensionAName);
+  constexpr char kExtensionBName[] = "B Extension";
+  auto extensionB = InstallExtension(kExtensionBName);
+
+  auto* toolbar_model = ToolbarActionsModel::Get(profile());
+  ASSERT_TRUE(toolbar_model);
+
+  toolbar_model->SetActionVisibility(extensionA->id(), true);
+  toolbar_model->SetActionVisibility(extensionB->id(), true);
+  WaitForAnimation();
+
+  EXPECT_THAT(GetPinnedExtensionNames(),
+              testing::ElementsAre(kExtensionAName, kExtensionBName));
+
+  // Simulate dragging extension B to the first slot.
+  ToolbarActionView* drag_view = GetPinnedExtensionView(extensionB->id());
+  ui::OSExchangeData drag_data;
+  extensions_container()->WriteDragDataForView(drag_view, gfx::Point(),
+                                               &drag_data);
+  gfx::PointF drop_point(GetPinnedExtensionView(extensionA->id())->origin());
+  ui::DropTargetEvent drop_event(drag_data, drop_point, drop_point,
+                                 ui::DragDropTypes::DRAG_MOVE);
+  extensions_container()->OnDragUpdated(drop_event);
+  auto cb = extensions_container()->GetDropCallback(drop_event);
+  WaitForAnimation();
+
+  EXPECT_THAT(GetPinnedExtensionNames(),
+              testing::ElementsAre(kExtensionBName, kExtensionAName));
+
+  constexpr char kExtensionCName[] = "C Extension";
+  auto extensionC = InstallExtension(kExtensionCName);
+  toolbar_model->SetActionVisibility(extensionC->id(), true);
+  WaitForAnimation();
+
+  // The drop callback should be invalidated, and items should be back in their
+  // original order.
+  ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone;
+  std::move(cb).Run(drop_event, output_drag_op);
+  WaitForAnimation();
+
+  EXPECT_THAT(
+      GetPinnedExtensionNames(),
+      testing::ElementsAre(kExtensionAName, kExtensionBName, kExtensionCName));
+}
+
+// ToolbarActionsModel::MovePinnedAction crashes if pinned extensions changes
+// while the drop callback isn't invalidated. This test makes sure this doesn't
+// happen anymore. https://crbug.com/1268239.
+TEST_F(ExtensionsToolbarContainerUnitTest, InvalidateDropCallbackOnPrefChange) {
+  constexpr char kExtensionAName[] = "A Extension";
+  auto extensionA = InstallExtension(kExtensionAName);
+  constexpr char kExtensionBName[] = "B Extension";
+  auto extensionB = InstallExtension(kExtensionBName);
+
+  auto* toolbar_model = ToolbarActionsModel::Get(profile());
+  ASSERT_TRUE(toolbar_model);
+
+  toolbar_model->SetActionVisibility(extensionA->id(), true);
+  toolbar_model->SetActionVisibility(extensionB->id(), true);
+  WaitForAnimation();
+
+  EXPECT_THAT(GetPinnedExtensionNames(),
+              testing::ElementsAre(kExtensionAName, kExtensionBName));
+
+  // Simulate dragging extension B to the first slot.
+  ToolbarActionView* drag_view = GetPinnedExtensionView(extensionB->id());
+  ui::OSExchangeData drag_data;
+  extensions_container()->WriteDragDataForView(drag_view, gfx::Point(),
+                                               &drag_data);
+  gfx::PointF drop_point(GetPinnedExtensionView(extensionA->id())->origin());
+  ui::DropTargetEvent drop_event(drag_data, drop_point, drop_point,
+                                 ui::DragDropTypes::DRAG_MOVE);
+  extensions_container()->OnDragUpdated(drop_event);
+  auto cb = extensions_container()->GetDropCallback(drop_event);
+  WaitForAnimation();
+
+  EXPECT_THAT(GetPinnedExtensionNames(),
+              testing::ElementsAre(kExtensionBName, kExtensionAName));
+
+  extensions::ExtensionPrefs::Get(profile())->SetPinnedExtensions({});
+  WaitForAnimation();
+
+  // The drop callback should be invalidated, and items should be back in their
+  // original order.
+  ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone;
+  std::move(cb).Run(drop_event, output_drag_op);
+  WaitForAnimation();
+
+  EXPECT_THAT(GetPinnedExtensionNames(), testing::ElementsAre());
+}
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.cc
index 0674285..4e7da46 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.cc
@@ -7,12 +7,14 @@
 #include "build/build_config.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/test_extension_system.h"
+#include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
 #include "components/crx_file/id_util.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
 #include "extensions/common/value_builder.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/views/layout/animating_layout_manager_test_util.h"
+#include "ui/views/view_utils.h"
 
 namespace {
 
@@ -99,6 +101,40 @@
   button->OnMouseReleased(release_event);
 }
 
+std::vector<ToolbarActionView*>
+ExtensionsToolbarUnitTest::GetPinnedExtensionViews() {
+  std::vector<ToolbarActionView*> result;
+  for (views::View* child : extensions_container()->children()) {
+    // Ensure we don't downcast the ExtensionsToolbarButton.
+    if (views::IsViewClass<ToolbarActionView>(child)) {
+      ToolbarActionView* const action = static_cast<ToolbarActionView*>(child);
+#if defined(OS_MAC)
+      // TODO(crbug.com/1045212): Use IsActionVisibleOnToolbar() because it
+      // queries the underlying model and not GetVisible(), as that relies on an
+      // animation running, which is not reliable in unit tests on Mac.
+      const bool is_visible = extensions_container()->IsActionVisibleOnToolbar(
+          action->view_controller());
+#else
+      const bool is_visible = action->GetVisible();
+#endif
+      if (is_visible)
+        result.push_back(action);
+    }
+  }
+  return result;
+}
+
+std::vector<std::string> ExtensionsToolbarUnitTest::GetPinnedExtensionNames() {
+  std::vector<ToolbarActionView*> views = GetPinnedExtensionViews();
+  std::vector<std::string> result;
+  result.resize(views.size());
+  std::transform(
+      views.begin(), views.end(), result.begin(), [](ToolbarActionView* view) {
+        return base::UTF16ToUTF8(view->view_controller()->GetActionName());
+      });
+  return result;
+}
+
 void ExtensionsToolbarUnitTest::WaitForAnimation() {
 #if defined(OS_MAC)
   // TODO(crbug.com/1045212): we avoid using animations on Mac due to the lack
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h b/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h
index 6f8c49e6..ae79b440 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h
@@ -64,6 +64,14 @@
   // Triggers the press and release event of the given `button`.
   void ClickButton(views::Button* button) const;
 
+  // Returns a list of the views of the currently pinned extensions, in order
+  // from left to right.
+  std::vector<ToolbarActionView*> GetPinnedExtensionViews();
+
+  // Returns a list of the names of the currently pinned extensions, in order
+  // from left to right.
+  std::vector<std::string> GetPinnedExtensionNames();
+
   // Waits for the extensions container to animate (on pin, unpin, pop-out,
   // etc.)
   void WaitForAnimation();
diff --git a/chrome/browser/ui/views/frame/browser_root_view.cc b/chrome/browser/ui/views/frame/browser_root_view.cc
index 612684da..31074108 100644
--- a/chrome/browser/ui/views/frame/browser_root_view.cc
+++ b/chrome/browser/ui/views/frame/browser_root_view.cc
@@ -168,10 +168,14 @@
 
     // Check if the file is supported.
     if (url.SchemeIsFile()) {
-      content::RenderFrameHost* rfh = browser_view_->browser()
-                                          ->tab_strip_model()
-                                          ->GetActiveWebContents()
-                                          ->GetMainFrame();
+      // Avoid crashing while the tab strip is being initialized or is empty.
+      content::WebContents* web_contents =
+          browser_view_->browser()->tab_strip_model()->GetActiveWebContents();
+      if (!web_contents) {
+        return;
+      }
+
+      content::RenderFrameHost* rfh = web_contents->GetMainFrame();
       base::ThreadPool::PostTaskAndReplyWithResult(
           FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
           base::BindOnce(&FindURLMimeType, url),
diff --git a/chrome/browser/ui/views/frame/browser_root_view_browsertest.cc b/chrome/browser/ui/views/frame/browser_root_view_browsertest.cc
index 4972f59..043feb3 100644
--- a/chrome/browser/ui/views/frame/browser_root_view_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_root_view_browsertest.cc
@@ -100,4 +100,21 @@
   EXPECT_EQ(output_drag_op, ui::mojom::DragOperation::kCopy);
   EXPECT_EQ(tab_strip_model->count(), 2);
 }
+
+IN_PROC_BROWSER_TEST_F(BrowserRootViewBrowserTest, OnDragEnteredNoTabs) {
+  auto* tab_strip_model = browser()->tab_strip_model();
+  EXPECT_EQ(tab_strip_model->count(), 1);
+  EXPECT_EQ(tab_strip_model->active_index(), 0);
+  tab_strip_model->CloseAllTabs();
+  EXPECT_EQ(tab_strip_model->count(), 0);
+  EXPECT_EQ(tab_strip_model->active_index(), -1);
+
+  ui::OSExchangeData data;
+  data.SetURL(GURL("file:///test.txt"), std::u16string());
+  ui::DropTargetEvent event(data, gfx::PointF(), gfx::PointF(),
+                            ui::DragDropTypes::DRAG_COPY);
+
+  // Ensure OnDragEntered() doesn't crash when there are no active tabs.
+  browser_root_view()->OnDragEntered(event);
+}
 #endif  // #if !BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index b8dbe2b..17fee3b 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -257,7 +257,13 @@
 }
 
 void OmniboxViewViews::OnTabChanged(content::WebContents* web_contents) {
-  // These have a reference to the WebContents, avoid a dangling pointer.
+  // The context menu holds references to share_submenu_model_ and
+  // send_tab_to_self_sub_menu_model_; invalidate it here so we can destroy
+  // those below.
+  InvalidateContextMenu();
+
+  // These have a reference to the WebContents, which might be being destroyed
+  // here:
   share_submenu_model_.reset();
   send_tab_to_self_sub_menu_model_.reset();
 
diff --git a/chrome/browser/ui/views/safe_browsing/tailored_security_unconsented_modal.cc b/chrome/browser/ui/views/safe_browsing/tailored_security_unconsented_modal.cc
index 51d0fc1e..0178c82 100644
--- a/chrome/browser/ui/views/safe_browsing/tailored_security_unconsented_modal.cc
+++ b/chrome/browser/ui/views/safe_browsing/tailored_security_unconsented_modal.cc
@@ -7,19 +7,34 @@
 #include "base/metrics/histogram_functions.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/tailored_security/tailored_security_outcome.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/grit/generated_resources.h"
+#include "chrome/grit/theme_resources.h"
 #include "components/constrained_window/constrained_window_views.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/base/models/image_model.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/canvas_image_source.h"
+#include "ui/gfx/image/image_skia_operations.h"
+#include "ui/views/bubble/bubble_frame_view.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/layout/layout_provider.h"
 
 namespace safe_browsing {
 
 namespace {
 
+constexpr int kAvatarSize = 40;
+constexpr int kAvatarOffset = 45;
+constexpr int kImageOffset = 5;
+
 void RecordModalOutcomeAndRunCallback(TailoredSecurityOutcome outcome,
                                       base::OnceClosure callback) {
   base::UmaHistogramEnumeration(
@@ -39,6 +54,55 @@
       chrome::FindBrowserWithWebContents(web_contents));
 }
 
+class CircleImageSource : public gfx::CanvasImageSource {
+ public:
+  CircleImageSource(int size, SkColor color)
+      : gfx::CanvasImageSource(gfx::Size(size, size)), color_(color) {}
+
+  CircleImageSource(const CircleImageSource&) = delete;
+  CircleImageSource& operator=(const CircleImageSource&) = delete;
+
+  ~CircleImageSource() override = default;
+
+  void Draw(gfx::Canvas* canvas) override {
+    float radius = size().width() / 2.0f;
+    cc::PaintFlags flags;
+    flags.setStyle(cc::PaintFlags::kFill_Style);
+    flags.setAntiAlias(true);
+    flags.setColor(color_);
+    canvas->DrawCircle(gfx::PointF(radius, radius), radius, flags);
+  }
+
+ private:
+  SkColor color_;
+};
+
+class SuperimposedOffsetImageSource : public gfx::CanvasImageSource {
+ public:
+  SuperimposedOffsetImageSource(const gfx::ImageSkia& first,
+                                const gfx::ImageSkia& second)
+      : gfx::CanvasImageSource(gfx::Size(first.size().width(),
+                                         first.size().height() + kImageOffset)),
+        first_(first),
+        second_(second) {}
+
+  SuperimposedOffsetImageSource(const SuperimposedOffsetImageSource&) = delete;
+  SuperimposedOffsetImageSource& operator=(
+      const SuperimposedOffsetImageSource&) = delete;
+
+  ~SuperimposedOffsetImageSource() override = default;
+
+  // gfx::CanvasImageSource override.
+  void Draw(gfx::Canvas* canvas) override {
+    canvas->DrawImageInt(first_, 0, kImageOffset);
+    canvas->DrawImageInt(second_, kAvatarOffset, kAvatarOffset + kImageOffset);
+  }
+
+ private:
+  const gfx::ImageSkia first_;
+  const gfx::ImageSkia second_;
+};
+
 }  // namespace
 
 /*static*/
@@ -86,6 +150,44 @@
   return false;
 }
 
+void TailoredSecurityUnconsentedModal::AddedToWidget() {
+  signin::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForProfile(
+          Profile::FromBrowserContext(web_contents_->GetBrowserContext()));
+  if (!identity_manager ||
+      !identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin))
+    return;
+
+  gfx::ImageSkia avatar_image = identity_manager
+                                    ->FindExtendedAccountInfoByAccountId(
+                                        identity_manager->GetPrimaryAccountId(
+                                            signin::ConsentLevel::kSignin))
+                                    .account_image.AsImageSkia();
+
+  gfx::ImageSkia sized_avatar_image =
+      gfx::ImageSkiaOperations::CreateResizedImage(
+          avatar_image, skia::ImageOperations::RESIZE_BEST,
+          gfx::Size(kAvatarSize, kAvatarSize));
+  gfx::ImageSkia cropped_avatar_image =
+      gfx::ImageSkiaOperations::CreateMaskedImage(
+          sized_avatar_image,
+          gfx::CanvasImageSource::MakeImageSkia<CircleImageSource>(
+              sized_avatar_image.width(), SK_ColorWHITE));
+  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+  gfx::ImageSkia header_image =
+      *bundle.GetImageSkiaNamed(IDR_TAILORED_SECURITY_UNCONSENTED);
+  gfx::ImageSkia header_and_avatar(
+      std::make_unique<SuperimposedOffsetImageSource>(header_image,
+                                                      cropped_avatar_image),
+      gfx::Size(header_image.size().width(),
+                header_image.size().height() + kImageOffset));
+
+  auto image_view = std::make_unique<views::ImageView>(
+      ui::ImageModel::FromImageSkia(header_and_avatar));
+  image_view->SetVerticalAlignment(views::ImageView::Alignment::kLeading);
+  GetBubbleFrameView()->SetHeaderView(std::move(image_view));
+}
+
 BEGIN_METADATA(TailoredSecurityUnconsentedModal, views::DialogDelegateView)
 END_METADATA
 
diff --git a/chrome/browser/ui/views/safe_browsing/tailored_security_unconsented_modal.h b/chrome/browser/ui/views/safe_browsing/tailored_security_unconsented_modal.h
index 0fb33cfd..f3e3a350 100644
--- a/chrome/browser/ui/views/safe_browsing/tailored_security_unconsented_modal.h
+++ b/chrome/browser/ui/views/safe_browsing/tailored_security_unconsented_modal.h
@@ -19,8 +19,6 @@
 
 // A tab modal dialog that is shown when the user's tailored security bit
 // changes and the user isn't consented to sync.
-//
-// TODO(crbug/1257622): Add profile icon as seen in mocks.
 class TailoredSecurityUnconsentedModal : public views::DialogDelegateView {
  public:
   METADATA_HEADER(TailoredSecurityUnconsentedModal);
@@ -40,6 +38,9 @@
   bool ShouldShowCloseButton() const override;
 
  private:
+  // views::DialogDelegateView:
+  void AddedToWidget() override;
+
   raw_ptr<content::WebContents> web_contents_;
 };
 
diff --git a/chrome/browser/ui/window_sizer/window_sizer_chromeos_uitest.cc b/chrome/browser/ui/window_sizer/window_sizer_chromeos_uitest.cc
index eac2892..57ab7d1 100644
--- a/chrome/browser/ui/window_sizer/window_sizer_chromeos_uitest.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer_chromeos_uitest.cc
@@ -17,9 +17,8 @@
 #include "components/keep_alive_registry/keep_alive_types.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "content/public/test/browser_test.h"
+#include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/events/test/event_generator.h"
-#include "ui/views/controls/menu/menu_config.h"
-#include "ui/views/controls/menu/menu_controller.h"
 #include "ui/views/view.h"
 #include "ui/views/view_model.h"
 #include "ui/wm/core/coordinate_conversion.h"
@@ -53,47 +52,30 @@
   generator.ClickLeftButton();
 }
 
-// Launch a new browser window by clicking the "New window" context menu item.
-void OpenBrowserUsingContextMenuOnRootWindow(aura::Window* root) {
-  ui::test::EventGenerator generator(root);
-  gfx::Point chrome_icon = GetChromeIconBoundsInScreen(root).CenterPoint();
-  generator.MoveMouseTo(chrome_icon);
-  generator.PressRightButton();
-
-  // Move the cursor up to the "New window" menu option - assumes menu content.
-  ash::ShelfView* shelf_view =
-      ash::Shelf::ForWindow(root)->GetShelfViewForTesting();
-  const int offset =
-      // Top half of the button we just clicked on.
-      shelf_view->GetButtonSize() / 2 +
-      // Space between shelf top and menu bottom. Here we get this menu with
-      // a right-click but long-pressing yields the same result. All menus
-      // here use a touchable layout.
-      views::MenuConfig::instance().touchable_anchor_offset +
-      // 3 menu items we don't want, and go over part of the one we want.
-      3.2 * views::MenuConfig::instance().touchable_menu_height;
-  generator.MoveMouseBy(0, -offset);
-  generator.ReleaseRightButton();
-}
-
 class WindowSizerTest : public InProcessBrowserTest {
  public:
-  WindowSizerTest() {}
-
+  WindowSizerTest() = default;
   WindowSizerTest(const WindowSizerTest&) = delete;
   WindowSizerTest& operator=(const WindowSizerTest&) = delete;
-
-  ~WindowSizerTest() override {}
+  ~WindowSizerTest() override = default;
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     // Make screens sufficiently wide to host 2 browsers side by side.
     command_line->AppendSwitchASCII("ash-host-window-bounds",
-                                    "600x600,601+0-600x600");
+                                    "800x600,801+0-800x600");
   }
+
+  ui::ScopedAnimationDurationScaleMode zero_duration_{
+      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION};
 };
 
-// TODO(crbug.com/1038342): Test is flaky.
-IN_PROC_BROWSER_TEST_F(WindowSizerTest, DISABLED_OpenBrowserUsingShelfItem) {
+// TODO(crbug.com/1038342): Test is flaky on sanitizers.
+#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER)
+#define MAYBE_OpenBrowserUsingShelfItem DISABLED_OpenBrowserUsingShelfItem
+#else
+#define MAYBE_OpenBrowserUsingShelfItem OpenBrowserUsingShelfItem
+#endif
+IN_PROC_BROWSER_TEST_F(WindowSizerTest, MAYBE_OpenBrowserUsingShelfItem) {
   // Don't shutdown when closing the last browser window.
   ScopedKeepAlive test_keep_alive(KeepAliveOrigin::BROWSER_PROCESS_CHROMEOS,
                                   KeepAliveRestartOption::DISABLED);
@@ -137,46 +119,4 @@
   EXPECT_EQ(root_windows[0], ash::Shell::GetRootWindowForNewWindows());
 }
 
-// TODO(crbug.com/1038342): Test is flaky.
-IN_PROC_BROWSER_TEST_F(WindowSizerTest, DISABLED_OpenBrowserUsingContextMenu) {
-  // Don't shutdown when closing the last browser window.
-  ScopedKeepAlive test_keep_alive(KeepAliveOrigin::BROWSER_PROCESS_CHROMEOS,
-                                  KeepAliveRestartOption::DISABLED);
-  aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
-  BrowserList* browser_list = BrowserList::GetInstance();
-  EnsureShelfInitialization();
-
-  views::MenuController::TurnOffMenuSelectionHoldForTest();
-
-  ASSERT_EQ(1u, browser_list->size());
-  EXPECT_EQ(root_windows[0], ash::Shell::GetRootWindowForNewWindows());
-  CloseBrowserSynchronously(browser_list->get(0));
-
-  OpenBrowserUsingContextMenuOnRootWindow(root_windows[1]);
-
-  // A new browser window should be opened on the 2nd display.
-  display::Screen* screen = display::Screen::GetScreen();
-  std::pair<display::Display, display::Display> displays =
-      ui_test_utils::GetDisplays(screen);
-  ASSERT_EQ(1u, browser_list->size());
-  EXPECT_EQ(displays.second.id(),
-            screen
-                ->GetDisplayNearestWindow(
-                    browser_list->get(0)->window()->GetNativeWindow())
-                .id());
-  EXPECT_EQ(root_windows[1], ash::Shell::GetRootWindowForNewWindows());
-
-  CloseBrowserSynchronously(browser_list->get(0));
-  OpenBrowserUsingContextMenuOnRootWindow(root_windows[0]);
-
-  // A new browser window should be opened on the 1st display.
-  ASSERT_EQ(1u, browser_list->size());
-  EXPECT_EQ(displays.first.id(),
-            screen
-                ->GetDisplayNearestWindow(
-                    browser_list->get(0)->window()->GetNativeWindow())
-                .id());
-  EXPECT_EQ(root_windows[0], ash::Shell::GetRootWindowForNewWindows());
-}
-
 }  // namespace
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 6374f6d..411643f 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1639569614-51af581decdc00b7af5310dccd59d8dd19f402a7.profdata
+chrome-linux-main-1639591173-b7d6da1a5c5f60f0b1959e21ff4b40acc3fbb5da.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 6412b29..5014d5c 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1639569614-d251f83da686019cc9a1332b1064979740a5536b.profdata
+chrome-mac-main-1639591173-0244749f96275593dcee7f94bfec65b371fce288.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 9dec62a..2837d9c 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1639558498-bfc8cbd53a95441e4b362960336fa324f065b029.profdata
+chrome-win32-main-1639580383-b8027ac27b70be2c1132df9ac8ecd76a61e2f927.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index fd9a56a..fa53f5a 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1639569614-a364248aacb276f65024141a5cfb87f1882ed3eb.profdata
+chrome-win64-main-1639591173-ef599e7e9308fc3ef78ebdbcc6c39423184c7325.profdata
diff --git a/chrome/chrome_repack_locales.gni b/chrome/chrome_repack_locales.gni
index 7e67226..350c3f8 100644
--- a/chrome/chrome_repack_locales.gni
+++ b/chrome/chrome_repack_locales.gni
@@ -27,6 +27,8 @@
       "${root_gen_dir}/chrome/generated_resources_",
       "${root_gen_dir}/chrome/locale_settings_",
       "${root_gen_dir}/chrome/platform_locale_settings_",
+      "${root_gen_dir}/components/omnibox/resources/omnibox_pedal_synonyms_",
+      "${root_gen_dir}/components/omnibox/resources/omnibox_resources_",
       "${root_gen_dir}/components/strings/components_locale_settings_",
       "${root_gen_dir}/components/strings/components_strings_",
       "${root_gen_dir}/third_party/blink/public/strings/blink_strings_",
@@ -45,6 +47,8 @@
       "//chrome/app:generated_resources",
       "//chrome/app/resources:locale_settings",
       "//chrome/app/resources:platform_locale_settings",
+      "//components/omnibox/resources:omnibox_pedal_synonyms",
+      "//components/omnibox/resources:omnibox_resources",
       "//components/strings:components_locale_settings",
       "//components/strings:components_strings",
       "//device/bluetooth/strings",
@@ -60,17 +64,6 @@
       deps += invoker.deps
     }
 
-    # Omnibox resources are currently desktop-only, but this may change.
-    if (!is_android) {
-      source_patterns += [
-        "${root_gen_dir}/components/omnibox/resources/omnibox_pedal_synonyms_",
-        "${root_gen_dir}/components/omnibox/resources/omnibox_resources_",
-      ]
-      deps += [
-        "//components/omnibox/resources:omnibox_pedal_synonyms",
-        "//components/omnibox/resources:omnibox_resources",
-      ]
-    }
     if (is_chromeos_ash) {
       source_patterns += [
         "${root_gen_dir}/ash/shortcut_viewer/strings/shortcut_viewer_strings_",
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index d126cc96..44fef6f 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -430,11 +430,22 @@
     "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "location": "component"
   }],
-  "languageSettingsPrivate": {
-    "channel": "trunk",
-    "extension_types": ["extension", "platform_app"],
-    "location": "component"
-  },
+  "languageSettingsPrivate": [
+    {
+      "channel": "trunk",
+      "extension_types": ["extension", "platform_app"],
+      "location": "component"
+    },
+    {
+      "channel": "stable",
+      "extension_types": ["extension"],
+      "location": "component",
+      "platforms": ["chromeos"],
+      "allowlist": [
+        "371AC6869D2138CE58123741E69F67469206909F" // AccessibilityCommon extension. https://crbug.com/1220107
+      ]
+    }
+  ],
   "lockWindowFullscreenPrivate": {
     "channel": "stable",
     "component_extensions_auto_granted": false,
@@ -801,8 +812,9 @@
     "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   "speechRecognitionPrivate": {
-    "channel": "dev",
+    "channel": "stable",
     "extension_types": ["extension"],
+    "location": "component",
     "platforms": ["chromeos"],
     "allowlist": [
       "371AC6869D2138CE58123741E69F67469206909F" // AccessibilityCommon extension. https://crbug.com/1220107
diff --git a/chrome/common/extensions/permissions/chrome_permission_message_rules.cc b/chrome/common/extensions/permissions/chrome_permission_message_rules.cc
index 215fc53..fce9603 100644
--- a/chrome/common/extensions/permissions/chrome_permission_message_rules.cc
+++ b/chrome/common/extensions/permissions/chrome_permission_message_rules.cc
@@ -430,6 +430,10 @@
        {APIPermissionID::kVideoCapture},
        {}},
 
+      {IDS_EXTENSION_PROMPT_WARNING_SPEECH_RECOGNITION,
+       {APIPermissionID::kSpeechRecognitionPrivate},
+       {}},
+
       {IDS_EXTENSION_PROMPT_WARNING_GEOLOCATION,
        {APIPermissionID::kGeolocation},
        {}},
diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc
index 2377268..1247e74e 100644
--- a/chrome/common/extensions/permissions/permission_set_unittest.cc
+++ b/chrome/common/extensions/permissions/permission_set_unittest.cc
@@ -842,8 +842,6 @@
   skip.insert(APIPermissionID::kResourcesPrivate);
   skip.insert(APIPermissionID::kRtcPrivate);
   skip.insert(APIPermissionID::kSafeBrowsingPrivate);
-  // TODO(crbug.com/1220107): Add a permission for speechRecognitionPrivate.
-  skip.insert(APIPermissionID::kSpeechRecognitionPrivate);
   skip.insert(APIPermissionID::kSystemPrivate);
   skip.insert(APIPermissionID::kTabCaptureForTab);
   skip.insert(APIPermissionID::kTerminalPrivate);
diff --git a/chrome/common/safe_browsing/download_type_util.cc b/chrome/common/safe_browsing/download_type_util.cc
index 6023b70..ce2a89e2 100644
--- a/chrome/common/safe_browsing/download_type_util.cc
+++ b/chrome/common/safe_browsing/download_type_util.cc
@@ -87,7 +87,8 @@
            file.MatchesExtension(FILE_PATH_LITERAL(".ppsx")) ||
            file.MatchesExtension(FILE_PATH_LITERAL(".ppsm")) ||
            file.MatchesExtension(FILE_PATH_LITERAL(".sldx")) ||
-           file.MatchesExtension(FILE_PATH_LITERAL(".rtf")))
+           file.MatchesExtension(FILE_PATH_LITERAL(".rtf")) ||
+           file.MatchesExtension(FILE_PATH_LITERAL(".wll")))
     return ClientDownloadRequest::DOCUMENT;
 
   return ClientDownloadRequest::WIN_EXECUTABLE;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 8ef8e21..85d0971 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1739,7 +1739,6 @@
       "../browser/policy/policy_initialization_browsertest.cc",
       "../browser/policy/policy_network_browsertest.cc",
       "../browser/policy/policy_prefs_browsertest.cc",
-      "../browser/policy/site_isolation_policy_browsertest.cc",
       "../browser/policy/test/allow_sync_xhr_in_page_dismissal_browsertest.cc",
       "../browser/policy/test/browsing_history_policy_browsertest.cc",
       "../browser/policy/test/cast_policy_browsertest.cc",
@@ -7706,6 +7705,7 @@
       "../browser/ui/views/extensions/extensions_menu_item_unittest.cc",
       "../browser/ui/views/extensions/extensions_menu_view_unittest.cc",
       "../browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc",
+      "../browser/ui/views/extensions/extensions_toolbar_container_unittest.cc",
       "../browser/ui/views/extensions/extensions_toolbar_controls_unittest.cc",
       "../browser/ui/views/extensions/extensions_toolbar_unittest.cc",
       "../browser/ui/views/extensions/extensions_toolbar_unittest.h",
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 75d6773f..7064ab1 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -17539,5 +17539,40 @@
         }
       }
     ]
+  },
+  "ProjectorEnabled": {
+    "os": [
+      "chromeos_ash"
+    ],
+    "policy_pref_mapping_tests": [
+      {
+        "policies": {},
+        "prefs": {
+          "ash.projector.allow_by_policy": {
+            "default_value": false
+          }
+        }
+      },
+      {
+        "policies": {
+          "ProjectorEnabled": false
+        },
+        "prefs": {
+          "ash.projector.allow_by_policy": {
+            "value": false
+          }
+        }
+      },
+      {
+        "policies": {
+          "ProjectorEnabled": true
+        },
+        "prefs": {
+          "ash.projector.allow_by_policy": {
+            "value": true
+          }
+        }
+      }
+    ]
   }
 }
diff --git a/chrome/test/data/webui/print_preview/BUILD.gn b/chrome/test/data/webui/print_preview/BUILD.gn
index 66cac052e..8009019 100644
--- a/chrome/test/data/webui/print_preview/BUILD.gn
+++ b/chrome/test/data/webui/print_preview/BUILD.gn
@@ -28,9 +28,13 @@
   "print_preview_sidebar_test.ts",
   "print_preview_test_utils.ts",
   "restore_state_test.ts",
-  "user_manager_test.js",
+  "user_manager_test.ts",
 ]
 
+if (is_mac || is_win) {
+  preprocessed_tests += [ "system_dialog_browsertest.ts" ]
+}
+
 # Print Preview test files that do not require preprocessing. If adding
 # // <if expr to any file below, move it to the list above.
 non_preprocessed_tests = [
@@ -62,8 +66,7 @@
   "scaling_settings_interactive_test.ts",
   "scaling_settings_test.ts",
   "select_mixin_test.ts",
-  "settings_select_test.js",
-  "system_dialog_browsertest.js",
+  "settings_select_test.ts",
   "test_plugin_proxy.ts",
 ]
 
diff --git a/chrome/test/data/webui/print_preview/settings_select_test.js b/chrome/test/data/webui/print_preview/settings_select_test.ts
similarity index 69%
rename from chrome/test/data/webui/print_preview/settings_select_test.js
rename to chrome/test/data/webui/print_preview/settings_select_test.ts
index 7ab0a22..8546782 100644
--- a/chrome/test/data/webui/print_preview/settings_select_test.js
+++ b/chrome/test/data/webui/print_preview/settings_select_test.ts
@@ -3,21 +3,21 @@
 // found in the LICENSE file.
 
 import 'chrome://print/print_preview.js';
-import {assert} from 'chrome://resources/js/assert.m.js';
+
+import {PrintPreviewModelElement, PrintPreviewSettingsSelectElement} from 'chrome://print/print_preview.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, fakeDataBind} from 'chrome://webui-test/test_util.js';
+
 import {getMediaSizeCapabilityWithCustomNames, selectOption} from './print_preview_test_utils.js';
 
 suite('SettingsSelectTest', function() {
-  /** @type {?PrintPreviewSettingsSelectElement} */
-  let settingsSelect = null;
+  let settingsSelect: PrintPreviewSettingsSelectElement;
 
-  /** @type {?PrintPreviewModelElement} */
-  let model = null;
+  let model: PrintPreviewModelElement;
 
-  /** @override */
   setup(function() {
-    PolymerTest.clearBody();
+    document.body.innerHTML = '';
     model = document.createElement('print-preview-model');
     document.body.appendChild(model);
 
@@ -35,20 +35,20 @@
     // Set a capability with custom paper sizes.
     settingsSelect.settingName = 'mediaSize';
     settingsSelect.capability = getMediaSizeCapabilityWithCustomNames();
-    const customLocalizedMediaName = settingsSelect.capability.option[0]
-                                         .custom_display_name_localized[0]
-                                         .value;
+    const customLocalizedMediaName =
+        settingsSelect.capability!.option[0]!.custom_display_name_localized![0]!
+            .value;
     const customMediaName =
-        settingsSelect.capability.option[1].custom_display_name;
+        settingsSelect.capability!.option[1]!.custom_display_name;
     flush();
 
-    const select = settingsSelect.shadowRoot.querySelector('select');
+    const select = settingsSelect.shadowRoot!.querySelector('select')!;
     // Verify that the selected option and names are as expected.
     assertEquals(0, select.selectedIndex);
     assertEquals(2, select.options.length);
     assertEquals(
-        customLocalizedMediaName, select.options[0].textContent.trim());
-    assertEquals(customMediaName, select.options[1].textContent.trim());
+        customLocalizedMediaName, select.options[0]!.textContent!.trim());
+    assertEquals(customMediaName, select.options[1]!.textContent!.trim());
   });
 
   test('set setting', async () => {
@@ -72,23 +72,23 @@
       ],
     };
     flush();
-    const option0 = JSON.stringify(settingsSelect.capability.option[0]);
-    const option1 = JSON.stringify(settingsSelect.capability.option[1]);
-    const select = settingsSelect.shadowRoot.querySelector('select');
+    const option0 = JSON.stringify(settingsSelect.capability!.option[0]!);
+    const option1 = JSON.stringify(settingsSelect.capability!.option[1]!);
+    const select = settingsSelect.shadowRoot!.querySelector('select')!;
 
     // Normally done for initialization by the model and parent section.
     settingsSelect.set(
-        'settings.fruit.value', settingsSelect.capability.option[1]);
+        'settings.fruit.value', settingsSelect.capability!.option[1]!);
     settingsSelect.selectValue(option1);
 
     // Verify that the selected option and names are as expected.
     assertEquals(2, select.options.length);
     assertEquals(1, select.selectedIndex);
     assertFalse(settingsSelect.getSetting('fruit').setFromUi);
-    assertEquals('lime', select.options[0].textContent.trim());
-    assertEquals('orange', select.options[1].textContent.trim());
-    assertEquals(option0, select.options[0].value);
-    assertEquals(option1, select.options[1].value);
+    assertEquals('lime', select.options[0]!.textContent!.trim());
+    assertEquals('orange', select.options[1]!.textContent!.trim());
+    assertEquals(option0, select.options[0]!.value);
+    assertEquals(option1, select.options[1]!.value);
 
     // Verify that selecting an new option in the dropdown sets the setting.
     await selectOption(settingsSelect, option0);
diff --git a/chrome/test/data/webui/print_preview/system_dialog_browsertest.js b/chrome/test/data/webui/print_preview/system_dialog_browsertest.js
deleted file mode 100644
index 5ba9176..0000000
--- a/chrome/test/data/webui/print_preview/system_dialog_browsertest.js
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {Destination, DestinationConnectionStatus, DestinationOrigin, DestinationType, NativeLayerImpl, PluginProxyImpl, PrintPreviewLinkContainerElement, PrintPreviewSidebarElement, whenReady} from 'chrome://print/print_preview.js';
-import {assert} from 'chrome://resources/js/assert.m.js';
-import {isWindows} from 'chrome://resources/js/cr.m.js';
-import {eventToPromise, waitBeforeNextRender} from 'chrome://webui-test/test_util.js';
-import {NativeLayerStub} from './native_layer_stub.js';
-import {getCddTemplate, getDefaultInitialSettings, selectOption} from './print_preview_test_utils.js';
-import {TestPluginProxy} from './test_plugin_proxy.js';
-
-window.system_dialog_browsertest = {};
-system_dialog_browsertest.suiteName = 'SystemDialogBrowserTest';
-/** @enum {string} */
-system_dialog_browsertest.TestNames = {
-  LinkTriggersLocalPrint: 'link triggers local print',
-  InvalidSettingsDisableLink: 'invalid settings disable link',
-};
-
-suite(system_dialog_browsertest.suiteName, function() {
-  /** @type {?PrintPreviewSidebarElement} */
-  let sidebar = null;
-
-  /** @type {?NativeLayer} */
-  let nativeLayer = null;
-
-  /** @type {?PrintPreviewLinkContainerElement} */
-  let linkContainer = null;
-
-  /** @type {?HTMLElement} */
-  let link = null;
-
-  /** @type {string} */
-  let printTicketKey = '';
-
-  /** @override */
-  setup(function() {
-    nativeLayer = new NativeLayerStub();
-    NativeLayerImpl.setInstance(nativeLayer);
-    document.body.innerHTML = '';
-
-    const initialSettings = getDefaultInitialSettings();
-    nativeLayer.setInitialSettings(initialSettings);
-    nativeLayer.setLocalDestinations(
-        [{deviceName: initialSettings.printerName, printerName: 'FooName'}]);
-    const pluginProxy = new TestPluginProxy();
-    PluginProxyImpl.setInstance(pluginProxy);
-
-    const page = document.createElement('print-preview-app');
-    document.body.appendChild(page);
-    const previewArea = page.$.previewArea;
-    sidebar = page.shadowRoot.querySelector('print-preview-sidebar');
-    return Promise
-        .all([
-          waitBeforeNextRender(page),
-          whenReady(),
-          nativeLayer.whenCalled('getInitialSettings'),
-          nativeLayer.whenCalled('getPrinterCapabilities'),
-        ])
-        .then(function() {
-          linkContainer =
-              sidebar.shadowRoot.querySelector('print-preview-link-container');
-          return nativeLayer.whenCalled('getPreview');
-        })
-        .then(function() {
-          assertEquals('FooDevice', page.destination_.id);
-          link = isWindows ? linkContainer.$.systemDialogLink :
-                             linkContainer.$.openPdfInPreviewLink;
-          printTicketKey = isWindows ? 'showSystemDialog' : 'openPDFInPreview';
-        });
-  });
-
-  test(
-      assert(system_dialog_browsertest.TestNames.LinkTriggersLocalPrint),
-      function() {
-        assertFalse(linkContainer.disabled);
-        assertFalse(link.hidden);
-        link.click();
-        // Should result in a print call and dialog should close.
-        return nativeLayer.whenCalled('print').then(function(printTicket) {
-          expectTrue(JSON.parse(printTicket)[printTicketKey]);
-          return nativeLayer.whenCalled('dialogClose');
-        });
-      });
-
-  test(
-      assert(system_dialog_browsertest.TestNames.InvalidSettingsDisableLink),
-      function() {
-        assertFalse(linkContainer.disabled);
-        assertFalse(link.hidden);
-
-        const moreSettingsElement =
-            sidebar.shadowRoot.querySelector('print-preview-more-settings');
-        moreSettingsElement.$.label.click();
-        const scalingSettings =
-            sidebar.shadowRoot.querySelector('print-preview-scaling-settings');
-        assertFalse(scalingSettings.hidden);
-        nativeLayer.resetResolver('getPreview');
-        let previewCalls = 0;
-
-        // Set scaling settings to custom.
-        return selectOption(
-                   scalingSettings,
-                   scalingSettings.ScalingValue.CUSTOM.toString())
-            .then(() => {
-              previewCalls = nativeLayer.getCallCount('getPreview');
-
-              // Set an invalid input.
-              const scalingSettingsInput =
-                  scalingSettings.shadowRoot
-                      .querySelector('print-preview-number-settings-section')
-                      .$.userValue.inputElement;
-              scalingSettingsInput.value = '0';
-              scalingSettingsInput.dispatchEvent(
-                  new CustomEvent('input', {composed: true, bubbles: true}));
-
-              return eventToPromise('input-change', scalingSettings);
-            })
-            .then(() => {
-              // Expect disabled print button
-              const parentElement = sidebar.shadowRoot.querySelector(
-                  'print-preview-button-strip');
-              const printButton =
-                  parentElement.shadowRoot.querySelector('.action-button');
-              assertTrue(printButton.disabled);
-              assertTrue(linkContainer.disabled);
-              assertFalse(link.hidden);
-              assertTrue(link.querySelector('cr-icon-button').disabled);
-
-              // No new preview
-              assertEquals(
-                  previewCalls, nativeLayer.getCallCount('getPreview'));
-            });
-      });
-});
diff --git a/chrome/test/data/webui/print_preview/system_dialog_browsertest.ts b/chrome/test/data/webui/print_preview/system_dialog_browsertest.ts
new file mode 100644
index 0000000..d68c9ec
--- /dev/null
+++ b/chrome/test/data/webui/print_preview/system_dialog_browsertest.ts
@@ -0,0 +1,142 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {CrButtonElement, NativeLayerImpl, PluginProxyImpl, PrintPreviewLinkContainerElement, PrintPreviewSidebarElement, ScalingType, whenReady} from 'chrome://print/print_preview.js';
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {eventToPromise, waitBeforeNextRender} from 'chrome://webui-test/test_util.js';
+import {NativeLayerStub} from './native_layer_stub.js';
+import {getDefaultInitialSettings, selectOption} from './print_preview_test_utils.js';
+import {TestPluginProxy} from './test_plugin_proxy.js';
+
+const system_dialog_browsertest = {
+  suiteName: 'SystemDialogBrowserTest',
+  TestNames: {
+    LinkTriggersLocalPrint: 'link triggers local print',
+    InvalidSettingsDisableLink: 'invalid settings disable link',
+  },
+};
+
+Object.assign(window, {system_dialog_browsertest: system_dialog_browsertest});
+
+suite(system_dialog_browsertest.suiteName, function() {
+  let sidebar: PrintPreviewSidebarElement;
+
+  let nativeLayer: NativeLayerStub;
+
+  let linkContainer: PrintPreviewLinkContainerElement;
+
+  let link: HTMLElement;
+
+  // <if expr="is_win">
+  let printTicketKey: string = 'showSystemDialog';
+  // </if>
+  // <if expr="is_macosx">
+  let printTicketKey: string = 'openPDFinPreview';
+  // </if>
+
+  setup(function() {
+    nativeLayer = new NativeLayerStub();
+    NativeLayerImpl.setInstance(nativeLayer);
+    document.body.innerHTML = '';
+
+    const initialSettings = getDefaultInitialSettings();
+    nativeLayer.setInitialSettings(initialSettings);
+    nativeLayer.setLocalDestinations(
+        [{deviceName: initialSettings.printerName, printerName: 'FooName'}]);
+    const pluginProxy = new TestPluginProxy();
+    PluginProxyImpl.setInstance(pluginProxy);
+
+    const page = document.createElement('print-preview-app');
+    document.body.appendChild(page);
+    sidebar = page.shadowRoot!.querySelector('print-preview-sidebar')!;
+    return Promise
+        .all([
+          waitBeforeNextRender(page),
+          whenReady(),
+          nativeLayer.whenCalled('getInitialSettings'),
+          nativeLayer.whenCalled('getPrinterCapabilities'),
+        ])
+        .then(function() {
+          linkContainer = sidebar.shadowRoot!.querySelector(
+              'print-preview-link-container')!;
+          return nativeLayer.whenCalled('getPreview');
+        })
+        .then(function() {
+          assertEquals(
+              'FooDevice',
+              sidebar.shadowRoot!
+                  .querySelector(
+                      'print-preview-destination-settings')!.destination!.id);
+          // <if expr="is_win">
+          link = linkContainer.$.systemDialogLink;
+          // </if>
+          // <if expr="is_macosx">
+          link = linkContainer.$.openPdfInPreviewLink;
+          // </if>
+        });
+  });
+
+  test(
+      assert(system_dialog_browsertest.TestNames.LinkTriggersLocalPrint),
+      function() {
+        assertFalse(linkContainer.disabled);
+        assertFalse(link.hidden);
+        link.click();
+        // Should result in a print call and dialog should close.
+        return nativeLayer.whenCalled('print').then((printTicket: string) => {
+          assertTrue(JSON.parse(printTicket)[printTicketKey]);
+          return nativeLayer.whenCalled('dialogClose');
+        });
+      });
+
+  test(
+      assert(system_dialog_browsertest.TestNames.InvalidSettingsDisableLink),
+      function() {
+        assertFalse(linkContainer.disabled);
+        assertFalse(link.hidden);
+
+        const moreSettingsElement =
+            sidebar.shadowRoot!.querySelector('print-preview-more-settings')!;
+        moreSettingsElement.$.label.click();
+        const scalingSettings = sidebar.shadowRoot!.querySelector(
+            'print-preview-scaling-settings')!;
+        assertFalse(scalingSettings.hidden);
+        nativeLayer.resetResolver('getPreview');
+        let previewCalls = 0;
+
+        // Set scaling settings to custom.
+        return selectOption(scalingSettings, ScalingType.CUSTOM.toString())
+            .then(() => {
+              previewCalls = nativeLayer.getCallCount('getPreview');
+
+              // Set an invalid input.
+              const scalingSettingsInput =
+                  scalingSettings.shadowRoot!
+                      .querySelector('print-preview-number-settings-section')!.$
+                      .userValue.inputElement;
+              scalingSettingsInput.value = '0';
+              scalingSettingsInput.dispatchEvent(
+                  new CustomEvent('input', {composed: true, bubbles: true}));
+
+              return eventToPromise('input-change', scalingSettings);
+            })
+            .then(() => {
+              // Expect disabled print button
+              const parentElement = sidebar.shadowRoot!.querySelector(
+                  'print-preview-button-strip')!;
+              const printButton =
+                  parentElement.shadowRoot!.querySelector<CrButtonElement>(
+                      '.action-button')!;
+              assertTrue(printButton.disabled);
+              assertTrue(linkContainer.disabled);
+              assertFalse(link.hidden);
+              assertTrue(link.querySelector('cr-icon-button')!.disabled);
+
+              // No new preview
+              assertEquals(
+                  previewCalls, nativeLayer.getCallCount('getPreview'));
+            });
+      });
+});
diff --git a/chrome/test/data/webui/print_preview/tsconfig_base.json b/chrome/test/data/webui/print_preview/tsconfig_base.json
index 6dbee097..63561619 100644
--- a/chrome/test/data/webui/print_preview/tsconfig_base.json
+++ b/chrome/test/data/webui/print_preview/tsconfig_base.json
@@ -1,7 +1,6 @@
 {
   "extends": "../../../../../tools/typescript/tsconfig_base.json",
   "compilerOptions": {
-    "allowJs": true,
     "typeRoots": [
       "./../../../../../third_party/node/node_modules/@types"
     ]
diff --git a/chrome/test/data/webui/print_preview/user_manager_test.js b/chrome/test/data/webui/print_preview/user_manager_test.ts
similarity index 81%
rename from chrome/test/data/webui/print_preview/user_manager_test.js
rename to chrome/test/data/webui/print_preview/user_manager_test.ts
index 35546ce..15f5297 100644
--- a/chrome/test/data/webui/print_preview/user_manager_test.js
+++ b/chrome/test/data/webui/print_preview/user_manager_test.ts
@@ -2,12 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {CloudPrintInterfaceImpl, DestinationStore, NativeLayerImpl} from 'chrome://print/print_preview.js';
-import {assert} from 'chrome://resources/js/assert.m.js';
+import {CloudPrintInterfaceImpl, DestinationStore, LocalDestinationInfo, NativeLayerImpl, PrintPreviewUserManagerElement} from 'chrome://print/print_preview.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
+import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 import {CloudPrintInterfaceStub} from './cloud_print_interface_stub.js';
-
 // <if expr="chromeos or lacros">
 import {setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
 // </if>
@@ -17,27 +16,21 @@
 
 
 suite('UserManagerTest', function() {
-  /** @type {?PrintPreviewUserManagerElement} */
-  let userManager = null;
+  let userManager: PrintPreviewUserManagerElement;
 
-  /** @type {?DestinationStore} */
-  let destinationStore = null;
+  let destinationStore: DestinationStore;
 
-  /** @type {?NativeLayer} */
-  let nativeLayer = null;
+  let nativeLayer: NativeLayerStub;
 
-  /** @type {?CloudPrintInterface} */
-  let cloudPrintInterface = null;
+  let cloudPrintInterface: CloudPrintInterfaceStub;
 
-  const account1 = 'foo@chromium.org';
-  const account2 = 'bar@chromium.org';
+  const account1: string = 'foo@chromium.org';
+  const account2: string = 'bar@chromium.org';
 
-  /** @override */
   suiteSetup(function() {
     setupTestListenerElement();
   });
 
-  /** @override */
   setup(function() {
     document.body.innerHTML = '';
 
@@ -57,14 +50,12 @@
     // Initialize destination store.
     destinationStore = createDestinationStore();
     destinationStore.setCloudPrintInterface(cloudPrintInterface);
-    const localDestinations = [];
-    const destinations = getDestinations(localDestinations);
+    const localDestinations: LocalDestinationInfo[] = [];
+    getDestinations(localDestinations);
     nativeLayer.setLocalDestinations(localDestinations);
 
     // Set up user manager
-    userManager.appKioskMode = false;
     userManager.destinationStore = destinationStore;
-    userManager.shouldReloadCookies = false;
     userManager.cloudPrintDisabled = false;
     document.body.appendChild(userManager);
 
diff --git a/chrome/test/data/webui/settings/privacy_page_test.ts b/chrome/test/data/webui/settings/privacy_page_test.ts
index ebf0d81..f4a3b6138 100644
--- a/chrome/test/data/webui/settings/privacy_page_test.ts
+++ b/chrome/test/data/webui/settings/privacy_page_test.ts
@@ -7,7 +7,7 @@
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {ClearBrowsingDataBrowserProxyImpl, ContentSettingsTypes, CookiePrimarySetting, SafeBrowsingSetting, SiteSettingsPrefsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
-import {HatsBrowserProxyImpl, MetricsBrowserProxyImpl, PrivacyPageBrowserProxyImpl, Route, Router, routes, SecureDnsMode, SettingsPrivacyPageElement, TrustSafetyInteraction} from 'chrome://settings/settings.js';
+import {HatsBrowserProxyImpl, MetricsBrowserProxyImpl, PrivacyPageBrowserProxyImpl, Route, Router, routes, SecureDnsMode, SettingsPrivacyPageElement, StatusAction, SyncStatus, TrustSafetyInteraction} from 'chrome://settings/settings.js';
 
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
@@ -239,8 +239,37 @@
     return flushTasks();
   });
 
-  test('privacyReviewRowVisible', function() {
+  test('privacyReviewRowVisibleChildAccount', function() {
     assertTrue(isChildVisible(page, '#privacyReviewLinkRow'));
+
+    // The user signs in to a child user account. This hides the privacy review
+    // entry point.
+    const syncStatus:
+        SyncStatus = {childUser: true, statusAction: StatusAction.NO_ACTION};
+    webUIListenerCallback('sync-status-changed', syncStatus);
+    flush();
+    assertFalse(isChildVisible(page, '#privacyReviewLinkRow'));
+
+    // The user is no longer signed in to a child user account. This doesn't
+    // show the entry point.
+    syncStatus.childUser = false;
+    webUIListenerCallback('sync-status-changed', syncStatus);
+    flush();
+    assertFalse(isChildVisible(page, '#privacyReviewLinkRow'));
+  });
+
+  test('privacyReviewRowVisibleManaged', function() {
+    assertTrue(isChildVisible(page, '#privacyReviewLinkRow'));
+
+    // The user becomes managed. This hides the privacy review entry point.
+    webUIListenerCallback('is-managed-changed', true);
+    flush();
+    assertFalse(isChildVisible(page, '#privacyReviewLinkRow'));
+
+    // The user is no longer managed. This doesn't show the entry point.
+    webUIListenerCallback('is-managed-changed', false);
+    flush();
+    assertFalse(isChildVisible(page, '#privacyReviewLinkRow'));
   });
 
   test('privacyReviewRowClick', function() {
diff --git a/chrome/updater/prefs.cc b/chrome/updater/prefs.cc
index 9673a80..9eec592 100644
--- a/chrome/updater/prefs.cc
+++ b/chrome/updater/prefs.cc
@@ -4,13 +4,16 @@
 
 #include "chrome/updater/prefs.h"
 
+#include <functional>
+#include <memory>
+#include <string>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
-#include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/prefs_impl.h"
 #include "chrome/updater/updater_scope.h"
@@ -133,12 +136,14 @@
 }
 
 void PrefsCommitPendingWrites(PrefService* pref_service) {
-  // Waits in the run loop until pending writes complete.
-  base::RunLoop runloop;
-  pref_service->CommitPendingWrite(base::BindOnce(
-      [](base::OnceClosure quit_closure) { std::move(quit_closure).Run(); },
-      runloop.QuitWhenIdleClosure()));
-  runloop.Run();
+  base::WaitableEvent write_complete_event;
+  pref_service->CommitPendingWrite({}, base::BindOnce(
+                                           [](base::WaitableEvent& event) {
+                                             VLOG(1) << "Prefs committed.";
+                                             event.Signal();
+                                           },
+                                           std::ref(write_complete_event)));
+  write_complete_event.Wait();
 }
 
 }  // namespace updater
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 61a823b..cb90099 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-14395.0.0
\ No newline at end of file
+14396.0.0
\ No newline at end of file
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index f9ff708..8a9cc785f 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -23,6 +23,10 @@
 const base::Feature kBluetoothPhoneFilter{"BluetoothPhoneFilter",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables dark/light mode feature.
+const base::Feature kDarkLightMode{"DarkLightMode",
+                                   base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Disables translation services of the Quick Answers V2.
 const base::Feature kDisableQuickAnswersV2Translation{
     "DisableQuickAnswersV2Translation", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -35,6 +39,10 @@
   return base::FeatureList::IsEnabled(kBluetoothAdvertisementMonitoring);
 }
 
+bool IsDarkLightModeEnabled() {
+  return base::FeatureList::IsEnabled(kDarkLightMode);
+}
+
 bool IsQuickAnswersV2TranslationDisabled() {
   return base::FeatureList::IsEnabled(kDisableQuickAnswersV2Translation);
 }
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index 6bd8e89..9c939cd 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -26,6 +26,7 @@
 extern const base::Feature kBluetoothAdvertisementMonitoring;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kBluetoothPhoneFilter;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kDarkLightMode;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kDisableQuickAnswersV2Translation;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
@@ -35,6 +36,7 @@
 
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 bool IsBluetoothAdvertisementMonitoringEnabled();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsDarkLightModeEnabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsQuickAnswersV2TranslationDisabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 bool IsQuickAnswersV2SettingsSubToggleEnabled();
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
index f7f97e7..bce5a10 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
@@ -50,6 +50,7 @@
 import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.browser_ui.site_settings.AutoDarkMetrics.AutoDarkSettingsChangeSource;
 import org.chromium.components.browser_ui.site_settings.FourStateCookieSettingsPreference.CookieSettingsState;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.content_settings.ContentSettingValues;
 import org.chromium.components.content_settings.ContentSettingsType;
 import org.chromium.components.content_settings.CookieControlsMode;
@@ -296,7 +297,7 @@
 
         // Color the first part of the title blue.
         ForegroundColorSpan blueSpan = new ForegroundColorSpan(
-                ApiCompatibilityUtils.getColor(getResources(), R.color.default_text_color_link));
+                SemanticColorUtils.getDefaultTextColorAccent1(getContext()));
         spannable.setSpan(blueSpan, 0, spannable.length() - prefCount.length(),
                 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
 
diff --git a/components/browser_ui/styles/android/java/res/values/semantic_colors_dynamic.xml b/components/browser_ui/styles/android/java/res/values/semantic_colors_dynamic.xml
index 4c1c5bf..b7f3f279 100644
--- a/components/browser_ui/styles/android/java/res/values/semantic_colors_dynamic.xml
+++ b/components/browser_ui/styles/android/java/res/values/semantic_colors_dynamic.xml
@@ -33,5 +33,5 @@
 
     <!-- Colors that will be experimented with. -->
     <!--<macro name="default_text_color_link">?attr/colorPrimary</macro>-->
-    <macro name="default_text_color_link">@color/default_text_color_link</macro>
+    <macro name="default_text_color_link">@color/default_text_color_link_baseline</macro>
 </resources>
diff --git a/components/browser_ui/styles/android/java/res/values/themes.xml b/components/browser_ui/styles/android/java/res/values/themes.xml
index f3c3995..2f1ef35a 100644
--- a/components/browser_ui/styles/android/java/res/values/themes.xml
+++ b/components/browser_ui/styles/android/java/res/values/themes.xml
@@ -6,6 +6,8 @@
 <resources>
     <!-- Colors should be mirrored by Base.Theme.BrowserUI.DialogWhenLarge. -->
     <style name="Base.V21.Theme.BrowserUI" parent="Theme.MaterialComponents.DayNight.NoActionBar">
+        <item name="dynamicColorThemeOverlay">@style/ThemeOverlay.BrowserUI.DynamicColors</item>
+
         <!-- Color palettes -->
         <item name="colorPrimary">@color/baseline_primary_600</item>
         <item name="colorPrimaryDark">@android:color/black</item>
@@ -26,7 +28,7 @@
         <!-- Text colors-->
         <item name="android:textColorPrimary">@color/default_text_color_list</item>
         <item name="android:textColorSecondary">@color/default_text_color_secondary_list</item>
-        <item name="android:textColorLink">@color/default_text_color_link</item>
+        <item name="android:textColorLink">@macro/default_text_color_link</item>
         <item name="android:textColorHighlight">@color/text_highlight_color</item>
         <item name="android:textColorHint">@color/default_text_color_hint_list</item>
 
@@ -67,6 +69,8 @@
 
     <!-- Colors should be mirrored by Base.V21.Theme.BrowserUI. -->
     <style name="Base.Theme.BrowserUI.DialogWhenLarge" parent="Theme.MaterialComponents.DayNight.DialogWhenLarge">
+        <item name="dynamicColorThemeOverlay">@style/ThemeOverlay.BrowserUI.DynamicColors</item>
+
          <!-- Color palettes -->
         <item name="colorPrimary">@color/baseline_primary_600</item>
         <item name="colorPrimaryDark">@android:color/black</item>
@@ -87,7 +91,7 @@
         <!-- Text colors-->
         <item name="android:textColorPrimary">@color/default_text_color_list</item>
         <item name="android:textColorSecondary">@color/default_text_color_secondary_list</item>
-        <item name="android:textColorLink">@color/default_text_color_link</item>
+        <item name="android:textColorLink">@macro/default_text_color_link</item>
         <item name="android:textColorHighlight">@color/text_highlight_color</item>
         <item name="android:textColorHint">@color/default_text_color_hint_list</item>
 
@@ -221,6 +225,11 @@
     <style name="ThemeRefactorOverlay.Enabled.SettingsPromo" parent="">
         <item name="elevationOverlayEnabled">true</item>
     </style>
+    <style name="ThemeOverlay.BrowserUI.DynamicColors" parent="ThemeOverlay.Material3.DynamicColors.DayNight">
+        <item name="elevationOverlayColor">?attr/colorPrimary</item>
+        <item name="elevationOverlayAccentColor">@android:color/transparent</item>
+    </style>
+    <!-- TODO(skym): Move this into ThemeOverlay.BrowserUI.DynamicColors instead. -->
     <!-- Applied after the dynamic colors to override the undesired overrides done by the dynamic
          color overlay, e.g. android:textColorHighlight. -->
     <style name="ThemeOverlay.DynamicColorOverrides" parent="">
diff --git a/components/browser_ui/styles/android/java/src/org/chromium/components/browser_ui/styles/SemanticColorUtils.java b/components/browser_ui/styles/android/java/src/org/chromium/components/browser_ui/styles/SemanticColorUtils.java
index 4537fb9..cd2245a 100644
--- a/components/browser_ui/styles/android/java/src/org/chromium/components/browser_ui/styles/SemanticColorUtils.java
+++ b/components/browser_ui/styles/android/java/src/org/chromium/components/browser_ui/styles/SemanticColorUtils.java
@@ -106,4 +106,10 @@
     public static @ColorInt int getNavigationBubbleBackgroundColor(Context context) {
         return getDefaultBgColorElev2(context);
     }
+
+    // Colors that will be experimented with. This is independent of |IS_FULL_DYNAMIC_COLORS|.
+    /** Returns the semantic color value that corresponds to default_text_color_link. */
+    public static @ColorInt int getDefaultTextColorLink(Context context) {
+        return context.getColor(R.color.default_text_color_link_baseline);
+    }
 }
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json
index 242d046..50f18253 100644
--- a/components/certificate_transparency/data/log_list.json
+++ b/components/certificate_transparency/data/log_list.json
@@ -1,6 +1,6 @@
 {
-  "version": "4.57",
-  "log_list_timestamp": "2021-12-13T01:34:37Z",
+  "version": "4.59",
+  "log_list_timestamp": "2021-12-15T01:34:24Z",
   "operators": [
     {
       "name": "Google",
diff --git a/components/client_hints/browser/BUILD.gn b/components/client_hints/browser/BUILD.gn
index 857d45e..168697b 100644
--- a/components/client_hints/browser/BUILD.gn
+++ b/components/client_hints/browser/BUILD.gn
@@ -12,6 +12,7 @@
     "//components/client_hints/common",
     "//components/content_settings/core/browser",
     "//components/content_settings/core/common",
+    "//components/embedder_support:browser_util",
     "//content/public/browser",
     "//services/network/public/cpp",
     "//third_party/blink/public/common:headers",
diff --git a/components/client_hints/browser/DEPS b/components/client_hints/browser/DEPS
index e821239..9a103f6 100644
--- a/components/client_hints/browser/DEPS
+++ b/components/client_hints/browser/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+components/content_settings/core/browser",
+  "+components/embedder_support",
   "+components/keyed_service",
   "+content/public/browser",
   "+third_party/blink/public/common/user_agent",
diff --git a/components/client_hints/browser/client_hints.cc b/components/client_hints/browser/client_hints.cc
index 35d2e25f..689115fa2 100644
--- a/components/client_hints/browser/client_hints.cc
+++ b/components/client_hints/browser/client_hints.cc
@@ -17,6 +17,7 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/content_settings/core/common/content_settings_utils.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 #include "services/network/public/cpp/client_hints.h"
@@ -84,12 +85,12 @@
     network::NetworkQualityTracker* network_quality_tracker,
     HostContentSettingsMap* settings_map,
     scoped_refptr<content_settings::CookieSettings> cookie_settings,
-    const blink::UserAgentMetadata& user_agent_metadata)
+    PrefService* pref_service)
     : context_(context),
       network_quality_tracker_(network_quality_tracker),
       settings_map_(settings_map),
       cookie_settings_(cookie_settings),
-      user_agent_metadata_(user_agent_metadata) {
+      pref_service_(pref_service) {
   DCHECK(context_);
   DCHECK(network_quality_tracker_);
   DCHECK(settings_map_);
@@ -137,7 +138,7 @@
 }
 
 blink::UserAgentMetadata ClientHints::GetUserAgentMetadata() {
-  return user_agent_metadata_;
+  return embedder_support::GetUserAgentMetadata(pref_service_);
 }
 
 void ClientHints::PersistClientHints(
diff --git a/components/client_hints/browser/client_hints.h b/components/client_hints/browser/client_hints.h
index 2dbf811..9593653 100644
--- a/components/client_hints/browser/client_hints.h
+++ b/components/client_hints/browser/client_hints.h
@@ -17,6 +17,7 @@
 
 class GURL;
 class HostContentSettingsMap;
+class PrefService;
 
 namespace client_hints {
 
@@ -27,7 +28,7 @@
               network::NetworkQualityTracker* network_quality_tracker,
               HostContentSettingsMap* settings_map,
               scoped_refptr<content_settings::CookieSettings> cookie_settings,
-              const blink::UserAgentMetadata& user_agent_metadata);
+              PrefService* pref_service);
 
   ClientHints(const ClientHints&) = delete;
   ClientHints& operator=(const ClientHints&) = delete;
@@ -61,8 +62,8 @@
   raw_ptr<network::NetworkQualityTracker> network_quality_tracker_ = nullptr;
   raw_ptr<HostContentSettingsMap> settings_map_ = nullptr;
   scoped_refptr<content_settings::CookieSettings> cookie_settings_;
-  blink::UserAgentMetadata user_agent_metadata_;
   std::vector<network::mojom::WebClientHintsType> additional_hints_;
+  raw_ptr<PrefService> pref_service_;
 };
 
 }  // namespace client_hints
diff --git a/components/crash/core/common/BUILD.gn b/components/crash/core/common/BUILD.gn
index 3c374e37..81460a6 100644
--- a/components/crash/core/common/BUILD.gn
+++ b/components/crash/core/common/BUILD.gn
@@ -195,7 +195,11 @@
     sources += [ "crash_key_breakpad_unittest.cc" ]
   }
 
+  # TODO(crbug.com/1186718): Enable when crash keys are supported on Fuchsia.
   if (is_fuchsia) {
-    sources -= [ "crash_key_unittest.cc" ]
+    sources -= [
+      "crash_key_unittest.cc",
+      "crash_keys_unittest.cc",
+    ]
   }
 }
diff --git a/components/enterprise/BUILD.gn b/components/enterprise/BUILD.gn
index cc23d57..bc3eb01 100644
--- a/components/enterprise/BUILD.gn
+++ b/components/enterprise/BUILD.gn
@@ -91,33 +91,37 @@
 source_set("unit_tests") {
   testonly = true
 
-  sources = []
-  deps = []
+  sources = [ "browser/reporting/report_uploader_unittest.cc" ]
+
+  deps = [
+    ":enterprise",
+    "//base/test:test_support",
+    "//build:chromeos_buildflags",
+    "//components/policy/core/common:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
 
   if (!is_chromeos_ash) {
     sources += [
       "browser/controller/browser_dm_token_storage_unittest.cc",
       "browser/reporting/cloud_reporting_policy_handler_unittest.cc",
-      "browser/reporting/report_uploader_unittest.cc",
     ]
 
     deps += [
-      ":enterprise",
       ":test_support",
       "//base",
-      "//base/test:test_support",
-      "//build:chromeos_buildflags",
-      "//components/enterprise/common/proto:extensions_workflow_events_proto",
       "//components/policy/core/browser:test_support",
-      "//components/policy/core/common:test_support",
       "//components/prefs:test_support",
-      "//components/reporting/client:test_support",
-      "//testing/gmock",
-      "//testing/gtest",
     ]
   }
 
-  if (!is_android && !is_chromeos_ash) {
+  if (!is_android) {
     sources += [ "browser/reporting/real_time_uploader_unittest.cc" ]
+    deps += [
+      "//base",
+      "//components/enterprise/common/proto:extensions_workflow_events_proto",
+      "//components/reporting/client:test_support",
+    ]
   }
 }
diff --git a/components/favicon_base/favicon_util.h b/components/favicon_base/favicon_util.h
index 274bc3c..d9a2e3ff 100644
--- a/components/favicon_base/favicon_util.h
+++ b/components/favicon_base/favicon_util.h
@@ -45,6 +45,6 @@
         favicon_bitmap_results,
     int desired_size_in_pixel);
 
-}  // namspace favicon_base
+}  // namespace favicon_base
 
 #endif  // COMPONENTS_FAVICON_BASE_FAVICON_UTIL_H_
diff --git a/components/heap_profiling/in_process/heap_profiler_controller_unittest.cc b/components/heap_profiling/in_process/heap_profiler_controller_unittest.cc
index 102432e..12544ab 100644
--- a/components/heap_profiling/in_process/heap_profiler_controller_unittest.cc
+++ b/components/heap_profiling/in_process/heap_profiler_controller_unittest.cc
@@ -108,12 +108,21 @@
     sampler->RecordFree(reinterpret_cast<void*>(0x1337));
   }
 
+  // Initialize `mute_hooks_` before `task_environment_` so that memory
+  // allocations aren't sampled while TaskEnvironment creates a thread. The
+  // sampling is crashing in the hooked FreeFunc on some test bots.
+  base::PoissonAllocationSampler::ScopedMuteHookedSamplesForTesting mute_hooks_;
+
+  // Create `feature_list_` before `task_environment_` and destroy it after to
+  // avoid a race in destruction.
+  base::test::ScopedFeatureList feature_list_;
+
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
-  base::test::ScopedFeatureList feature_list_;
-  base::PoissonAllocationSampler::ScopedMuteHookedSamplesForTesting mute_hooks_;
+
   std::unique_ptr<HeapProfilerController> controller_;
   base::HistogramTester histogram_tester_;
+
   // `sample_received_` is read from the main thread and written from a
   // background thread, but does not need to be atomic because the write happens
   // during a scheduled sample and the read happens well after that.
diff --git a/components/infobars/android/res/layout/infobar_control_message.xml b/components/infobars/android/res/layout/infobar_control_message.xml
index 53fc786..720ea96 100644
--- a/components/infobars/android/res/layout/infobar_control_message.xml
+++ b/components/infobars/android/res/layout/infobar_control_message.xml
@@ -10,4 +10,4 @@
     android:layout_height="wrap_content"
     android:textDirection="locale"
     android:textAppearance="@style/TextAppearance.TextLarge.Primary"
-    android:textColorLink="@color/default_text_color_link" />
+    android:textColorLink="@macro/default_text_color_link" />
diff --git a/components/nacl/common/nacl_host_messages.h b/components/nacl/common/nacl_host_messages.h
index e7e82a7..1b8a9ad 100644
--- a/components/nacl/common/nacl_host_messages.h
+++ b/components/nacl/common/nacl_host_messages.h
@@ -36,7 +36,6 @@
   IPC_STRUCT_TRAITS_MEMBER(resource_prefetch_request_list)
   IPC_STRUCT_TRAITS_MEMBER(render_frame_id)
   IPC_STRUCT_TRAITS_MEMBER(permission_bits)
-  IPC_STRUCT_TRAITS_MEMBER(uses_nonsfi_mode)
   IPC_STRUCT_TRAITS_MEMBER(process_type)
 IPC_STRUCT_TRAITS_END()
 
diff --git a/components/nacl/common/nacl_messages.h b/components/nacl/common/nacl_messages.h
index 498b5e9f..520e802 100644
--- a/components/nacl/common/nacl_messages.h
+++ b/components/nacl/common/nacl_messages.h
@@ -29,7 +29,7 @@
 #if defined(OS_POSIX)
   IPC_STRUCT_TRAITS_MEMBER(debug_stub_server_bound_socket)
 #endif
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_NACL_NONSFI)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
   IPC_STRUCT_TRAITS_MEMBER(ppapi_browser_channel_handle)
   IPC_STRUCT_TRAITS_MEMBER(ppapi_renderer_channel_handle)
   IPC_STRUCT_TRAITS_MEMBER(trusted_service_channel_handle)
diff --git a/components/nacl/common/nacl_paths.cc b/components/nacl/common/nacl_paths.cc
index 71615dc..80aa177 100644
--- a/components/nacl/common/nacl_paths.cc
+++ b/components/nacl/common/nacl_paths.cc
@@ -16,8 +16,6 @@
 // File name of the nacl_helper and nacl_helper_bootstrap, Linux only.
 const base::FilePath::CharType kInternalNaClHelperFileName[] =
     FILE_PATH_LITERAL("nacl_helper");
-const base::FilePath::CharType kInternalNaClHelperNonSfiFileName[] =
-    FILE_PATH_LITERAL("nacl_helper_nonsfi");
 const base::FilePath::CharType kInternalNaClHelperBootstrapFileName[] =
     FILE_PATH_LITERAL("nacl_helper_bootstrap");
 
@@ -40,8 +38,6 @@
 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
     case FILE_NACL_HELPER:
       return GetNaClHelperPath(kInternalNaClHelperFileName, result);
-    case FILE_NACL_HELPER_NONSFI:
-      return GetNaClHelperPath(kInternalNaClHelperNonSfiFileName, result);
     case FILE_NACL_HELPER_BOOTSTRAP:
       return GetNaClHelperPath(kInternalNaClHelperBootstrapFileName, result);
 #endif
diff --git a/components/nacl/common/nacl_paths.h b/components/nacl/common/nacl_paths.h
index 935f582..69571dd 100644
--- a/components/nacl/common/nacl_paths.h
+++ b/components/nacl/common/nacl_paths.h
@@ -17,7 +17,6 @@
 
 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
   FILE_NACL_HELPER = PATH_START,  // Full path to Linux nacl_helper executable.
-  FILE_NACL_HELPER_NONSFI,        // path to nacl_helper_nonsfi executable,
   FILE_NACL_HELPER_BOOTSTRAP,     // ... and nacl_helper_bootstrap executable.
 #endif
 
diff --git a/components/nacl/common/nacl_switches.cc b/components/nacl/common/nacl_switches.cc
index 749d6fdd..b9135b4 100644
--- a/components/nacl/common/nacl_switches.cc
+++ b/components/nacl/common/nacl_switches.cc
@@ -12,10 +12,6 @@
 // Enables debugging via RSP over a socket.
 const char kEnableNaClDebug[]               = "enable-nacl-debug";
 
-// Enables Non-SFI mode, in which programs can be run without NaCl's SFI
-// sandbox.
-const char kEnableNaClNonSfiMode[]          = "enable-nacl-nonsfi-mode";
-
 // Force use of the Subzero as the PNaCl translator instead of LLC.
 const char kForcePNaClSubzero[] = "force-pnacl-subzero";
 
@@ -23,11 +19,6 @@
 // (used for launching NaCl loader processes on 64-bit Windows).
 const char kNaClBrokerProcess[]             = "nacl-broker";
 
-// Disable sandbox even for non SFI mode. This is particularly unsafe
-// as non SFI NaCl heavily relies on the seccomp sandbox.
-const char kNaClDangerousNoSandboxNonSfi[]  =
-    "nacl-dangerous-no-sandbox-nonsfi";
-
 // Uses NaCl manifest URL to choose whether NaCl program will be debugged by
 // debug stub.
 // Switch value format: [!]pattern1,pattern2,...,patternN. Each pattern uses
@@ -45,10 +36,6 @@
 const char kNaClGdb[]                       = "nacl-gdb";
 
 // Value for --type that causes the process to run as a NativeClient loader
-// for non SFI mode.
-const char kNaClLoaderNonSfiProcess[]       = "nacl-loader-nonsfi";
-
-// Value for --type that causes the process to run as a NativeClient loader
 // for SFI mode.
 const char kNaClLoaderProcess[]             = "nacl-loader";
 
diff --git a/components/nacl/common/nacl_switches.h b/components/nacl/common/nacl_switches.h
index 9139960..e04606a 100644
--- a/components/nacl/common/nacl_switches.h
+++ b/components/nacl/common/nacl_switches.h
@@ -13,14 +13,11 @@
 // alongside the definition of their values in the .cc file.
 extern const char kDisablePnaclCrashThrottling[];
 extern const char kEnableNaClDebug[];
-extern const char kEnableNaClNonSfiMode[];
 extern const char kForcePNaClSubzero[];
 extern const char kNaClBrokerProcess[];
-extern const char kNaClDangerousNoSandboxNonSfi[];
 extern const char kNaClDebugMask[];
 extern const char kNaClGdbScript[];
 extern const char kNaClGdb[];
-extern const char kNaClLoaderNonSfiProcess[];
 extern const char kNaClLoaderProcess[];
 
 }  // namespace switches
diff --git a/components/nacl/common/nacl_types.cc b/components/nacl/common/nacl_types.cc
index 3c4c6e92..97aabe9e 100644
--- a/components/nacl/common/nacl_types.cc
+++ b/components/nacl/common/nacl_types.cc
@@ -62,7 +62,6 @@
         resource_prefetch_request_list,
     int render_frame_id,
     uint32_t permission_bits,
-    bool uses_nonsfi_mode,
     NaClAppProcessType process_type)
     : manifest_url(manifest_url),
       nexe_file(nexe_file),
@@ -71,7 +70,6 @@
       resource_prefetch_request_list(resource_prefetch_request_list),
       render_frame_id(render_frame_id),
       permission_bits(permission_bits),
-      uses_nonsfi_mode(uses_nonsfi_mode),
       process_type(process_type) {}
 
 NaClLaunchParams::NaClLaunchParams(const NaClLaunchParams& other) = default;
diff --git a/components/nacl/common/nacl_types.h b/components/nacl/common/nacl_types.h
index fc217d2..c4310ee 100644
--- a/components/nacl/common/nacl_types.h
+++ b/components/nacl/common/nacl_types.h
@@ -130,7 +130,6 @@
                        resource_prefetch_request_list,
                    int render_frame_id,
                    uint32_t permission_bits,
-                   bool uses_nonsfi_mode,
                    NaClAppProcessType process_type);
   NaClLaunchParams(const NaClLaunchParams& other);
   ~NaClLaunchParams();
@@ -146,7 +145,6 @@
 
   int render_frame_id = 0;
   uint32_t permission_bits = 0;
-  bool uses_nonsfi_mode = false;
 
   NaClAppProcessType process_type = kUnknownNaClProcessType;
 };
diff --git a/components/nacl/renderer/nexe_load_manager.cc b/components/nacl/renderer/nexe_load_manager.cc
index 08044f9..cfc17c7 100644
--- a/components/nacl/renderer/nexe_load_manager.cc
+++ b/components/nacl/renderer/nexe_load_manager.cc
@@ -87,8 +87,7 @@
       is_installed_(false),
       exit_status_(-1),
       nexe_size_(0),
-      plugin_instance_(content::PepperPluginInstance::Get(pp_instance)),
-      nonsfi_(false) {
+      plugin_instance_(content::PepperPluginInstance::Get(pp_instance)) {
   set_exit_status(-1);
   SetLastError("");
   HistogramEnumerateOsArch(GetSandboxArch());
diff --git a/components/nacl/renderer/nexe_load_manager.h b/components/nacl/renderer/nexe_load_manager.h
index 0a0aff8e..11911bcf 100644
--- a/components/nacl/renderer/nexe_load_manager.h
+++ b/components/nacl/renderer/nexe_load_manager.h
@@ -123,9 +123,6 @@
     crash_info_shmem_region_ = std::move(shmem_region);
   }
 
-  bool nonsfi() const { return nonsfi_; }
-  void set_nonsfi(bool nonsfi) { nonsfi_ = nonsfi; }
-
   void ReportDeadNexe();
 
   // Copies a crash log to the console, one line at a time.
@@ -182,9 +179,6 @@
 
   base::Time pnacl_start_time_;
 
-  // A flag that indicates if the plugin is using Non-SFI mode.
-  bool nonsfi_;
-
   base::ReadOnlySharedMemoryRegion crash_info_shmem_region_;
 
   std::unique_ptr<TrustedPluginChannel> trusted_plugin_channel_;
diff --git a/components/nacl/renderer/ppb_nacl_private_impl.cc b/components/nacl/renderer/ppb_nacl_private_impl.cc
index c086395..4054234c6 100644
--- a/components/nacl/renderer/ppb_nacl_private_impl.cc
+++ b/components/nacl/renderer/ppb_nacl_private_impl.cc
@@ -465,8 +465,7 @@
                            nexe_file_info->token_lo, nexe_file_info->token_hi,
                            resource_prefetch_request_list,
                            GetFrameRoutingID(instance), perm_bits,
-                           // TODO(b/200965779): Remove nonsfi parameter.
-                           /*uses_nonsfi_mode=*/PP_FALSE, process_type),
+                           process_type),
           &launch_result, &error_message_string))) {
     ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
         FROM_HERE, base::BindOnce(callback.func, callback.user_data,
diff --git a/components/omnibox/browser/actions/omnibox_action.cc b/components/omnibox/browser/actions/omnibox_action.cc
index 4b6aa9e..4271cf8 100644
--- a/components/omnibox/browser/actions/omnibox_action.cc
+++ b/components/omnibox/browser/actions/omnibox_action.cc
@@ -11,7 +11,7 @@
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "ui/base/l10n/l10n_util.h"
 
-#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
+#if defined(SUPPORT_PEDALS_VECTOR_ICONS)
 #include "components/omnibox/browser/vector_icons.h"  // nogncheck
 #endif
 
@@ -81,7 +81,7 @@
   return true;
 }
 
-#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
+#if defined(SUPPORT_PEDALS_VECTOR_ICONS)
 const gfx::VectorIcon& OmniboxAction::GetVectorIcon() const {
   // TODO(tommycli): Replace with real icon.
   return omnibox::kPedalIcon;
diff --git a/components/omnibox/browser/actions/omnibox_action.h b/components/omnibox/browser/actions/omnibox_action.h
index e7a9013..6d49c84 100644
--- a/components/omnibox/browser/actions/omnibox_action.h
+++ b/components/omnibox/browser/actions/omnibox_action.h
@@ -20,6 +20,7 @@
 #include "url/gurl.h"
 
 #if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
+#define SUPPORT_PEDALS_VECTOR_ICONS
 namespace gfx {
 struct VectorIcon;
 }
@@ -134,7 +135,7 @@
   virtual bool IsReadyToTrigger(const AutocompleteInput& input,
                                 const AutocompleteProviderClient& client) const;
 
-#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
+#if defined(SUPPORT_PEDALS_VECTOR_ICONS)
   // Returns the vector icon to represent this Action.
   virtual const gfx::VectorIcon& GetVectorIcon() const;
 #endif
diff --git a/components/omnibox/browser/actions/omnibox_pedal.cc b/components/omnibox/browser/actions/omnibox_pedal.cc
index a3f1511..3e6be22 100644
--- a/components/omnibox/browser/actions/omnibox_pedal.cc
+++ b/components/omnibox/browser/actions/omnibox_pedal.cc
@@ -17,7 +17,7 @@
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "ui/base/l10n/l10n_util.h"
 
-#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
+#if defined(SUPPORT_PEDALS_VECTOR_ICONS)
 #include "components/omnibox/browser/vector_icons.h"  // nogncheck
 #endif
 
@@ -253,7 +253,7 @@
   url_ = url;
 }
 
-#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
+#if defined(SUPPORT_PEDALS_VECTOR_ICONS)
 // static
 const gfx::VectorIcon& OmniboxPedal::GetDefaultVectorIcon() {
   return omnibox::kPedalIcon;
diff --git a/components/omnibox/browser/actions/omnibox_pedal.h b/components/omnibox/browser/actions/omnibox_pedal.h
index 6fc3689..174a085 100644
--- a/components/omnibox/browser/actions/omnibox_pedal.h
+++ b/components/omnibox/browser/actions/omnibox_pedal.h
@@ -184,7 +184,7 @@
   // Sets the destination URL for the Pedal.
   void SetNavigationUrl(const GURL& url);
 
-#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
+#if defined(SUPPORT_PEDALS_VECTOR_ICONS)
   // Returns the default vector icon to use for Pedals that do not specify one.
   static const gfx::VectorIcon& GetDefaultVectorIcon();
 #endif
@@ -226,7 +226,7 @@
   // OmniboxAction overrides:
   void RecordActionShown(size_t position) const override;
   void RecordActionExecuted(size_t position) const override;
-#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
+#if defined(SUPPORT_PEDALS_VECTOR_ICONS)
   const gfx::VectorIcon& GetVectorIcon() const override;
 #endif
   size_t EstimateMemoryUsage() const override;
diff --git a/components/optimization_guide/core/decision_tree_prediction_model_unittest.cc b/components/optimization_guide/core/decision_tree_prediction_model_unittest.cc
index 4a479d1..a93e134 100644
--- a/components/optimization_guide/core/decision_tree_prediction_model_unittest.cc
+++ b/components/optimization_guide/core/decision_tree_prediction_model_unittest.cc
@@ -74,8 +74,8 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   model_info->add_supported_host_model_features("agg1");
 
   std::unique_ptr<PredictionModel> model =
@@ -104,8 +104,8 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   model_info->add_supported_host_model_features("agg1");
 
   std::unique_ptr<PredictionModel> model =
@@ -134,8 +134,8 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   model_info->add_supported_host_model_features("agg1");
 
   std::unique_ptr<PredictionModel> model =
@@ -164,8 +164,8 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   model_info->add_supported_host_model_features("agg1");
 
   std::unique_ptr<PredictionModel> model =
@@ -194,8 +194,8 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   model_info->add_supported_host_model_features("agg1");
 
   std::unique_ptr<PredictionModel> model =
@@ -211,8 +211,8 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   model_info->add_supported_host_model_features("agg1");
 
   std::unique_ptr<PredictionModel> model =
@@ -228,8 +228,8 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   model_info->add_supported_host_model_features("agg1");
 
   std::unique_ptr<PredictionModel> model =
@@ -245,8 +245,8 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   model_info->add_supported_host_model_features("agg1");
 
   std::unique_ptr<PredictionModel> model =
@@ -266,8 +266,8 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   model_info->add_supported_host_model_features("agg1");
 
   std::unique_ptr<PredictionModel> model =
@@ -288,8 +288,8 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   model_info->add_supported_host_model_features("agg1");
 
   std::unique_ptr<PredictionModel> model =
@@ -310,8 +310,8 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   model_info->add_supported_host_model_features("agg1");
 
   std::unique_ptr<PredictionModel> model =
@@ -346,8 +346,8 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   model_info->add_supported_host_model_features("agg1");
 
   std::unique_ptr<PredictionModel> model =
@@ -382,8 +382,8 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   model_info->add_supported_host_model_features("agg1");
 
   std::unique_ptr<PredictionModel> model =
@@ -396,8 +396,8 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   model_info->add_supported_host_model_features("agg1");
 
   std::unique_ptr<PredictionModel> model =
@@ -422,8 +422,8 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   model_info->add_supported_host_model_features("agg1");
 
   std::unique_ptr<PredictionModel> model =
diff --git a/components/optimization_guide/core/optimization_guide_store_unittest.cc b/components/optimization_guide/core/optimization_guide_store_unittest.cc
index bf60d8a..b1ea475 100644
--- a/components/optimization_guide/core/optimization_guide_store_unittest.cc
+++ b/components/optimization_guide/core/optimization_guide_store_unittest.cc
@@ -60,8 +60,8 @@
   model_info->set_version(1);
   model_info->set_optimization_target(
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   return prediction_model;
 }
 
diff --git a/components/optimization_guide/core/prediction_model.cc b/components/optimization_guide/core/prediction_model.cc
index 1cf077bd..9a99ed0 100644
--- a/components/optimization_guide/core/prediction_model.cc
+++ b/components/optimization_guide/core/prediction_model.cc
@@ -26,24 +26,27 @@
   if (!prediction_model.model_info().has_version())
     return nullptr;
 
-  // Enforce that only one ModelType is specified for the PredictionModel.
-  if (prediction_model.model_info().supported_model_types_size() != 1) {
+  // Enforce that only one ModelEngineVersion is specified for the
+  // PredictionModel.
+  if (prediction_model.model_info().supported_model_engine_versions_size() !=
+      1) {
     return nullptr;
   }
 
   // Check that the client supports this type of model and is not an unknown
   // type.
-  if (!proto::ModelType_IsValid(
-          prediction_model.model_info().supported_model_types(0)) ||
-      prediction_model.model_info().supported_model_types(0) ==
-          proto::ModelType::MODEL_TYPE_UNKNOWN) {
+  if (!proto::ModelEngineVersion_IsValid(
+          prediction_model.model_info().supported_model_engine_versions(0)) ||
+      prediction_model.model_info().supported_model_engine_versions(0) ==
+          proto::ModelEngineVersion::MODEL_ENGINE_VERSION_UNKNOWN) {
     return nullptr;
   }
 
   std::unique_ptr<PredictionModel> model;
-  // The Decision Tree model type is currently the only supported model type.
-  if (prediction_model.model_info().supported_model_types(0) !=
-      proto::ModelType::MODEL_TYPE_DECISION_TREE) {
+  // The Decision Tree model engine version is currently the only supported
+  // model engine version.
+  if (prediction_model.model_info().supported_model_engine_versions(0) !=
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE) {
     return nullptr;
   }
   model = std::make_unique<DecisionTreePredictionModel>(prediction_model);
diff --git a/components/optimization_guide/core/prediction_model.h b/components/optimization_guide/core/prediction_model.h
index c4c3c024..bf81903 100644
--- a/components/optimization_guide/core/prediction_model.h
+++ b/components/optimization_guide/core/prediction_model.h
@@ -25,9 +25,11 @@
 
   virtual ~PredictionModel();
 
-  // Creates an Prediction model of the correct ModelType specified in
+  // Creates an Prediction model of the correct model type specified in
   // |prediction_model|. The validation overhead of this factory can be high and
   // should should be called in the background.
+  //
+  // TODO(crbug/1245793): Remove this function.
   static std::unique_ptr<PredictionModel> Create(
       const proto::PredictionModel& prediction_model);
 
diff --git a/components/optimization_guide/core/prediction_model_unittest.cc b/components/optimization_guide/core/prediction_model_unittest.cc
index c928abdd..436a445c 100644
--- a/components/optimization_guide/core/prediction_model_unittest.cc
+++ b/components/optimization_guide/core/prediction_model_unittest.cc
@@ -50,8 +50,8 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
   model_info->add_supported_host_model_features("agg1");
 
   std::unique_ptr<PredictionModel> model =
@@ -82,7 +82,7 @@
   EXPECT_FALSE(model);
 }
 
-TEST(PredictionModelTest, NoModelType) {
+TEST(PredictionModelTest, NoModelEngineVersion) {
   proto::PredictionModel prediction_model;
 
   proto::DecisionTree* decision_tree_model =
@@ -97,7 +97,7 @@
   EXPECT_FALSE(model);
 }
 
-TEST(PredictionModelTest, UnknownModelType) {
+TEST(PredictionModelTest, UnknownModelEngineVersion) {
   proto::PredictionModel prediction_model;
 
   proto::DecisionTree* decision_tree_model =
@@ -106,14 +106,15 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(proto::ModelType::MODEL_TYPE_UNKNOWN);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_UNKNOWN);
 
   std::unique_ptr<PredictionModel> model =
       PredictionModel::Create(prediction_model);
   EXPECT_FALSE(model);
 }
 
-TEST(PredictionModelTest, MultipleModelTypes) {
+TEST(PredictionModelTest, MultipleModelEngineVersions) {
   proto::PredictionModel prediction_model;
 
   proto::DecisionTree* decision_tree_model =
@@ -122,9 +123,10 @@
 
   proto::ModelInfo* model_info = prediction_model.mutable_model_info();
   model_info->set_version(1);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
-  model_info->add_supported_model_types(proto::ModelType::MODEL_TYPE_UNKNOWN);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_UNKNOWN);
 
   std::unique_ptr<PredictionModel> model =
       PredictionModel::Create(prediction_model);
diff --git a/components/optimization_guide/core/store_update_data_unittest.cc b/components/optimization_guide/core/store_update_data_unittest.cc
index 78503369..edac180 100644
--- a/components/optimization_guide/core/store_update_data_unittest.cc
+++ b/components/optimization_guide/core/store_update_data_unittest.cc
@@ -120,8 +120,8 @@
   model_info->set_version(1);
   model_info->set_optimization_target(
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-  model_info->add_supported_model_types(
-      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_engine_versions(
+      proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
 
   base::Time expected_expiry_time =
       base::Time::Now() + features::StoredModelsInactiveDuration();
diff --git a/components/optimization_guide/proto/models.proto b/components/optimization_guide/proto/models.proto
index 6a9384f..ea953cc 100644
--- a/components/optimization_guide/proto/models.proto
+++ b/components/optimization_guide/proto/models.proto
@@ -208,8 +208,9 @@
   optional OptimizationTarget optimization_target = 1;
   // The version of the model, which is specific to the optimization target.
   optional int64 version = 2;
-  // The set of model types the requesting client can use to make predictions.
-  repeated ModelType supported_model_types = 4;
+  // The set of model engine versions the requesting client can use to do model
+  // inference.
+  repeated ModelEngineVersion supported_model_engine_versions = 4;
   // The set of host model features that are referenced by the model.
   //
   // Note that this should only be populated if part of the response.
@@ -271,29 +272,29 @@
   OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT = 16;
 }
 
-// The types of models that can be evaluated.
+// The model engine versions that can be used to do model inference.
 //
 // Please only update these enums when a new major version of TFLite rolls.
 //
 // For example: v1.2.3
 //                 ^
 //                 Change when this number increments.
-enum ModelType {
-  MODEL_TYPE_UNKNOWN = 0;
+enum ModelEngineVersion {
+  MODEL_ENGINE_VERSION_UNKNOWN = 0;
   // A decision tree.
-  MODEL_TYPE_DECISION_TREE = 1;
+  MODEL_ENGINE_VERSION_DECISION_TREE = 1;
   // A model using only operations that are supported by TensorflowLite 2.3.0.
-  MODEL_TYPE_TFLITE_2_3_0 = 2;
+  MODEL_ENGINE_VERSION_TFLITE_2_3_0 = 2;
   // A model using only operations that are supported by TensorflowLite 2.3.0
   // with updated FULLY_CONNECTED and BATCH_MUL versions for quantized models.
-  MODEL_TYPE_TFLITE_2_3_0_1 = 3;
+  MODEL_ENGINE_VERSION_TFLITE_2_3_0_1 = 3;
   // TensorflowLite version 2.4.2, and a bit more up to internal rev number
   // 381280669.
-  MODEL_TYPE_TFLITE_2_4 = 4;
+  MODEL_ENGINE_VERSION_TFLITE_2_4 = 4;
   // TensorflowLite version 2.7.*. This is where regular ~HEAD rolls started.
-  MODEL_TYPE_TFLITE_2_7 = 5;
+  MODEL_ENGINE_VERSION_TFLITE_2_7 = 5;
   // A model using only operations that are supported by TensorflowLite 2.8.0.
-  MODEL_TYPE_TFLITE_2_8 = 6;
+  MODEL_ENGINE_VERSION_TFLITE_2_8 = 6;
 }
 
 // A set of model features and the host that it applies to.
diff --git a/components/page_info/android/BUILD.gn b/components/page_info/android/BUILD.gn
index a2efa5d..ed3d20d 100644
--- a/components/page_info/android/BUILD.gn
+++ b/components/page_info/android/BUILD.gn
@@ -87,7 +87,7 @@
     "//base:base_java",
     "//components/browser_ui/settings/android:java",
     "//components/browser_ui/site_settings/android:java",
-    "//components/browser_ui/styles/android:java_resources",
+    "//components/browser_ui/styles/android:java",
     "//components/browser_ui/widget/android:java",
     "//components/content_settings/android:content_settings_enums_java",
     "//components/content_settings/android:java",
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoConnectionController.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoConnectionController.java
index 58cfe29..38c9351 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoConnectionController.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoConnectionController.java
@@ -14,7 +14,7 @@
 
 import androidx.annotation.ColorRes;
 
-import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.omnibox.SecurityStatusIcon;
 import org.chromium.components.security_state.ConnectionSecurityLevel;
 import org.chromium.components.security_state.SecurityStateModel;
@@ -128,9 +128,8 @@
             messageBuilder.append(" ");
             SpannableString detailsText =
                     new SpannableString(mRowView.getContext().getString(R.string.details_link));
-            final ForegroundColorSpan blueSpan =
-                    new ForegroundColorSpan(ApiCompatibilityUtils.getColor(
-                            mRowView.getContext().getResources(), R.color.default_text_color_link));
+            final ForegroundColorSpan blueSpan = new ForegroundColorSpan(
+                    SemanticColorUtils.getDefaultTextColorLink(mRowView.getContext()));
             detailsText.setSpan(
                     blueSpan, 0, detailsText.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
             messageBuilder.append(detailsText);
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index 9a532f1..aa56bba945 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -65,13 +65,6 @@
     std::move(completion_callback).Run(!is_change_empty);
 }
 
-LoginsResult GetLoginsOrEmptyListOnFailure(LoginsResultOrError result) {
-  if (absl::holds_alternative<PasswordStoreBackendError>(result)) {
-    return {};
-  }
-  return std::move(absl::get<LoginsResult>(result));
-}
-
 }  // namespace
 
 PasswordStore::PasswordStore(std::unique_ptr<PasswordStoreBackend> backend) {
diff --git a/components/password_manager/core/browser/password_store_util.cc b/components/password_manager/core/browser/password_store_util.cc
index 186f563..59526a2 100644
--- a/components/password_manager/core/browser/password_store_util.cc
+++ b/components/password_manager/core/browser/password_store_util.cc
@@ -16,4 +16,11 @@
   return joined_changes;
 }
 
+LoginsResult GetLoginsOrEmptyListOnFailure(LoginsResultOrError result) {
+  if (absl::holds_alternative<PasswordStoreBackendError>(result)) {
+    return {};
+  }
+  return std::move(absl::get<LoginsResult>(result));
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_store_util.h b/components/password_manager/core/browser/password_store_util.h
index 11a44f5..a070d0b6 100644
--- a/components/password_manager/core/browser/password_store_util.h
+++ b/components/password_manager/core/browser/password_store_util.h
@@ -7,6 +7,7 @@
 
 #include <vector>
 
+#include "components/password_manager/core/browser/password_store_backend.h"
 #include "components/password_manager/core/browser/password_store_change.h"
 
 namespace password_manager {
@@ -16,6 +17,10 @@
 PasswordStoreChangeList JoinPasswordStoreChanges(
     std::vector<PasswordStoreChangeList> changes);
 
+// Returns logins if |result| holds them, or an empty list if |result|
+// holds an error.
+LoginsResult GetLoginsOrEmptyListOnFailure(LoginsResultOrError result);
+
 }  // namespace password_manager
 
 #endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_STORE_UTIL_H_
\ No newline at end of file
diff --git a/components/payments/content/payment_request.h b/components/payments/content/payment_request.h
index e6137db..5162545 100644
--- a/components/payments/content/payment_request.h
+++ b/components/payments/content/payment_request.h
@@ -128,10 +128,6 @@
   // object and close any related connections.
   void OnUserCancelled();
 
-  // Called when the main frame attached to this PaymentRequest is navigating
-  // to another document, but before the PaymentRequest is destroyed.
-  void DidStartMainFrameNavigationToDifferentDocument(bool is_user_initiated);
-
   // Called when the PaymentRequest is about to be destroyed. This reports
   // the reason for destruction.
   void WillBeDestroyed(content::DocumentServiceDestructionReason reason) final;
diff --git a/components/payments/core/error_strings.cc b/components/payments/core/error_strings.cc
index 8b531b2..aaccb97 100644
--- a/components/payments/core/error_strings.cc
+++ b/components/payments/core/error_strings.cc
@@ -13,7 +13,7 @@
 const char kAnotherUiShowing[] = "Another PaymentRequest UI is already showing in a different tab or window.";
 const char kAppStoreMethodOnlySupportedInTwa[] = "Payment method https://play.google.com/billing is only supported in Trusted Web Activity.";
 const char kAttemptedInitializationTwice[] = "Attempted initialization twice.";
-const char kCannotShowInBackgroundTab[] = "Cannot show PaymentRequest UI in a background tab.";
+const char kCannotShowInBackgroundTab[] = "Cannot show PaymentRequest UI in a preview page or a background tab.";
 const char kCannotShowTwice[] = "Attempted show twice.";
 const char kCannotShowWithoutInit[] = "Attempted show without initialization.";
 const char kCannotUpdateWithoutInit[] = "Attempted updateWith without initialization.";
diff --git a/components/policy/core/common/configuration_policy_provider.cc b/components/policy/core/common/configuration_policy_provider.cc
index 4396245..59848f5 100644
--- a/components/policy/core/common/configuration_policy_provider.cc
+++ b/components/policy/core/common/configuration_policy_provider.cc
@@ -79,4 +79,11 @@
 
 void ConfigurationPolicyProvider::OnSchemaRegistryReady() {}
 
+#if defined(OS_ANDROID)
+void ConfigurationPolicyProvider::ShutdownForTesting() {
+  observer_list_.Clear();
+  Shutdown();
+}
+#endif  // defined(OS_ANDROID)
+
 }  // namespace policy
diff --git a/components/policy/core/common/configuration_policy_provider.h b/components/policy/core/common/configuration_policy_provider.h
index 033e697..7147af2 100644
--- a/components/policy/core/common/configuration_policy_provider.h
+++ b/components/policy/core/common/configuration_policy_provider.h
@@ -10,6 +10,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/observer_list.h"
+#include "build/build_config.h"
 #include "components/policy/core/common/policy_bundle.h"
 #include "components/policy/core/common/policy_namespace.h"
 #include "components/policy/core/common/schema_registry.h"
@@ -84,6 +85,10 @@
   void OnSchemaRegistryUpdated(bool has_new_schemas) override;
   void OnSchemaRegistryReady() override;
 
+#if defined(OS_ANDROID)
+  void ShutdownForTesting();
+#endif  // defined(OS_ANDROID)
+
  protected:
   // Subclasses must invoke this to update the policies currently served by
   // this provider. UpdatePolicy() takes ownership of |policies|.
diff --git a/components/policy/core/common/mock_configuration_policy_provider.cc b/components/policy/core/common/mock_configuration_policy_provider.cc
index c660304..faba677 100644
--- a/components/policy/core/common/mock_configuration_policy_provider.cc
+++ b/components/policy/core/common/mock_configuration_policy_provider.cc
@@ -19,7 +19,11 @@
 
 MockConfigurationPolicyProvider::MockConfigurationPolicyProvider() {}
 
-MockConfigurationPolicyProvider::~MockConfigurationPolicyProvider() {}
+MockConfigurationPolicyProvider::~MockConfigurationPolicyProvider() {
+#if defined(OS_ANDROID)
+  ShutdownForTesting();
+#endif  // defined(OS_ANDROID)
+}
 
 void MockConfigurationPolicyProvider::UpdateChromePolicy(
     const PolicyMap& policy) {
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index d2800611..c56266d5 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -1310,6 +1310,15 @@
       ],
     },
     {
+      'name': 'Projector',
+      'type': 'group',
+      'caption': '''Projector''',
+      'desc': '''Controls policies for Projector.''',
+      'policies': [
+        'ProjectorEnabled',
+      ]
+    },
+    {
       'name': 'HomepageLocation',
       'owners': ['file://components/policy/resources/OWNERS', 'rsorokin@chromium.org'],
       'type': 'string',
@@ -29114,6 +29123,37 @@
 
       This policy should be removed after kEnableLazyLoginWebUILoading is fully rolled out.''',
     },
+    {
+      'name': 'ProjectorEnabled',
+      'owners': ['llin@google.com', 'cros-projector@google.com'],
+      'type': 'main',
+      'schema': { 'type': 'boolean' },
+      'supported_on': ['chrome_os:99-'],
+      'tags' : ['google-sharing'],
+      'features': {
+        'dynamic_refresh': True,
+        'per_profile': True,
+      },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable Projector',
+        },
+        {
+          'value': False,
+          'caption': 'Disable Projector',
+        },
+      ],
+      'example_value': True,
+      'default': False,
+      'id': 935,
+      'caption': '''Enable Projector''',
+      'desc': '''This policy gives Projector permission to create and transcribe screen recording and upload to Drive.
+
+      If the policy is enabled, Projector will be enabled.
+      If the policy is disabled, Projector will be disabled.
+      If the policy is not set, Projector will by default disabled.''',
+    }
   ],
   'messages': {
     # Messages that are not associated to any policies.
@@ -30088,6 +30128,6 @@
   'placeholders': [],
   'deleted_policy_ids': [114, 115, 204, 205, 206, 341, 412, 476, 544, 546, 562, 569, 578, 583, 585, 586, 587, 588, 589, 590, 591, 600, 668, 669, 872],
   'deleted_atomic_policy_group_ids': [19],
-  'highest_id_currently_used': 934,
+  'highest_id_currently_used': 935,
   'highest_atomic_group_id_currently_used': 41
 }
diff --git a/components/variations/BUILD.gn b/components/variations/BUILD.gn
index 406e48da..9a999fd 100644
--- a/components/variations/BUILD.gn
+++ b/components/variations/BUILD.gn
@@ -231,7 +231,9 @@
   # build returns the crash keys in the unit-test, not the "variations"
   # target. The test verifies the keys in the "variations" target, not the
   # test. As such, it fails in component builds.
-  if (!is_component_build) {
+  # TODO(crbug.com/1186718): Enable this unittest for is_fuchsia when crash keys
+  # are supported on Fuchsia.
+  if (!is_component_build && !is_fuchsia) {
     sources += [ "variations_crash_keys_unittest.cc" ]
   }
 
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc
index 1ba0c02..c2823172 100644
--- a/components/viz/service/display/display.cc
+++ b/components/viz/service/display/display.cc
@@ -1162,16 +1162,18 @@
 
 base::TimeDelta Display::GetEstimatedDisplayDrawTime(base::TimeDelta interval,
                                                      double percentile) const {
-  if (draw_time_without_scheduling_waits_.sample_count() >= 60) {
+  base::TimeDelta default_estimate =
+      BeginFrameArgs::DefaultEstimatedDisplayDrawTime(interval);
+  if (draw_time_without_scheduling_waits_.sample_count() >= 60 &&
+      default_estimate > kMinEstimatedDisplayDrawTime) {
     // We do not want the deadline adjustmens to exceed a default of 1/3 VSync,
     // as we would not give other processes enough time to produce content. So
     // this would make high latency situations worse.
     return base::clamp(
         draw_time_without_scheduling_waits_.Percentile(percentile),
-        kMinEstimatedDisplayDrawTime,
-        BeginFrameArgs::DefaultEstimatedDisplayDrawTime(interval));
+        kMinEstimatedDisplayDrawTime, default_estimate);
   }
-  return BeginFrameArgs::DefaultEstimatedDisplayDrawTime(interval);
+  return default_estimate;
 }
 
 void Display::OnObservingBeginFrameSourceChanged(bool observing) {
diff --git a/components/viz/service/display/overlay_processor_ozone.cc b/components/viz/service/display/overlay_processor_ozone.cc
index 92d2629..c4c4aff 100644
--- a/components/viz/service/display/overlay_processor_ozone.cc
+++ b/components/viz/service/display/overlay_processor_ozone.cc
@@ -143,15 +143,6 @@
 void OverlayProcessorOzone::CheckOverlaySupport(
     const OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane,
     OverlayCandidateList* surfaces) {
-  // This number is depended on what type of strategies we have. Currently we
-  // only overlay one video.
-#if DCHECK_IS_ON()
-  // TODO(petermcneeley) : Reconsider this check in light of delegated
-  // compositing and multiple overlay work.
-  if (!features::IsDelegatedCompositingEnabled()) {
-    DCHECK_EQ(1U, surfaces->size());
-  }
-#endif
   auto full_size = surfaces->size();
   if (primary_plane)
     full_size += 1;
diff --git a/components/viz/service/display/overlay_processor_using_strategy.cc b/components/viz/service/display/overlay_processor_using_strategy.cc
index 3904039..ea104e90 100644
--- a/components/viz/service/display/overlay_processor_using_strategy.cc
+++ b/components/viz/service/display/overlay_processor_using_strategy.cc
@@ -255,10 +255,12 @@
     SurfaceDamageRectList* surface_damage_rect_list,
     const QuadList* quad_list,
     gfx::Rect* damage_rect) {
-  // TODO(petermcneeley): This code only supports one overlay candidate. To
-  // support multiple overlays one would need to track the difference set of
+  // TODO(khaslett): This code only supports one overlay candidate, but we'll
+  // check against `max_overlays_considered_` so we can still try the
+  // UseMultipleOverlays feature without hitting DCHECKS.
+  // To support multiple overlays one would need to track the difference set of
   // overlays between frames.
-  DCHECK_LE(candidates->size(), 1U);
+  DCHECK_LE(candidates->size(), static_cast<size_t>(max_overlays_considered_));
 
   gfx::Rect this_frame_overlay_rect;
   bool has_mask_filter = false;
diff --git a/components/viz/test/test_context_provider.cc b/components/viz/test/test_context_provider.cc
index 5f7e07b..9cc71de 100644
--- a/components/viz/test/test_context_provider.cc
+++ b/components/viz/test/test_context_provider.cc
@@ -469,7 +469,7 @@
                                     &max_glyph_cache_texture_bytes);
   gr_context_ = std::make_unique<skia_bindings::GrContextForGLES2Interface>(
       context_gl_.get(), support_.get(), context_gl_->test_capabilities(),
-      max_resource_cache_bytes, max_glyph_cache_texture_bytes);
+      max_resource_cache_bytes, max_glyph_cache_texture_bytes, true);
   cache_controller_->SetGrContext(gr_context_->get());
 
   // If GlContext is already lost, also abandon the new GrContext.
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index 34fbe09..bcabc00 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -1845,6 +1845,7 @@
 
   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
   BrowserAccessibility* focusedChild = _owner->manager()->GetFocus();
+
   // "IsDescendantOf" also returns true when the two objects are equivalent.
   if (focusedChild && focusedChild != _owner &&
       focusedChild->IsDescendantOf(_owner)) {
@@ -1852,7 +1853,8 @@
     // children because there could only be at most one selected child. The
     // selected child should also be equivalent to the focused child, because
     // selection is tethered to the focus.
-    if (!GetState(_owner, ax::mojom::State::kMultiselectable)) {
+    if (!GetState(_owner, ax::mojom::State::kMultiselectable) &&
+        focusedChild->GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)) {
       [ret addObject:ToBrowserAccessibilityCocoa(focusedChild)];
       return ret;
     }
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.cc b/content/browser/attribution_reporting/attribution_storage_sql.cc
index ab01c1e1..7cb5f53 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql.cc
@@ -470,7 +470,7 @@
                              /*priority=*/0,
                              /*external_report_id=*/
                              delegate_->NewReportID(),
-                             /*conversion_id=*/absl::nullopt);
+                             /*report_id=*/absl::nullopt);
 
     if (!StoreReport(report, source_id))
       return {};
@@ -666,7 +666,7 @@
                            /*conversion_time=*/current_time,
                            /*report_time=*/report_time, trigger.priority(),
                            /*external_report_id=*/delegate_->NewReportID(),
-                           /*conversion_id=*/absl::nullopt);
+                           /*report_id=*/absl::nullopt);
 
   switch (
       rate_limit_table_.AttributionAllowed(db_.get(), report, current_time)) {
diff --git a/content/browser/devtools/protocol/devtools_network_resource_loader.cc b/content/browser/devtools/protocol/devtools_network_resource_loader.cc
index e995ba2..4cf56d19 100644
--- a/content/browser/devtools/protocol/devtools_network_resource_loader.cc
+++ b/content/browser/devtools/protocol/devtools_network_resource_loader.cc
@@ -45,7 +45,6 @@
     Caching caching,
     Credentials include_credentials,
     CompletionCallback completion_callback) {
-  DCHECK(gurl.SchemeIsHTTPOrHTTPS());
   network::ResourceRequest resource_request;
   resource_request.url = std::move(gurl);
   resource_request.request_initiator = origin;
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index aaa61e2..b0d8b48 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -1008,7 +1008,8 @@
     const std::string& host_id,
     const base::UnguessableToken& devtools_token,
     DevToolsIOContext* io_context,
-    base::RepeatingClosure update_loader_factories_callback)
+    base::RepeatingClosure update_loader_factories_callback,
+    bool allow_file_access)
     : DevToolsDomainHandler(Network::Metainfo::domainName),
       host_id_(host_id),
       devtools_token_(devtools_token),
@@ -1023,7 +1024,8 @@
       bypass_service_worker_(false),
       cache_disabled_(false),
       update_loader_factories_callback_(
-          std::move(update_loader_factories_callback)) {
+          std::move(update_loader_factories_callback)),
+      allow_file_access_(allow_file_access) {
   DCHECK(io_context_);
   static bool have_configured_service_worker_context = false;
   if (have_configured_service_worker_context)
@@ -2886,7 +2888,9 @@
 
 mojo::PendingRemote<network::mojom::URLLoaderFactory>
 CreateNetworkFactoryForDevTools(
+    base::StringPiece scheme,
     RenderProcessHost* host,
+    int routing_id,
     network::mojom::URLLoaderFactoryParamsPtr params) {
   if (!host || !params) {
     // Return an invalid remote by default.
@@ -2901,10 +2905,26 @@
   // See BUG(chromium:1076435) for more context.
   params->is_corb_enabled = false;
 
-  mojo::PendingRemote<network::mojom::URLLoaderFactory> remote;
-  host->CreateURLLoaderFactory(remote.InitWithNewPipeAndPassReceiver(),
-                               std::move(params));
-  return remote;
+  if (scheme == url::kHttpScheme || scheme == url::kHttpsScheme) {
+    mojo::PendingRemote<network::mojom::URLLoaderFactory> remote;
+    host->CreateURLLoaderFactory(remote.InitWithNewPipeAndPassReceiver(),
+                                 std::move(params));
+    return remote;
+  }
+
+  if (scheme != url::kFileScheme) {
+    ContentBrowserClient::NonNetworkURLLoaderFactoryMap factories;
+    GetContentClient()
+        ->browser()
+        ->RegisterNonNetworkSubresourceURLLoaderFactories(
+            host->GetID(), routing_id, &factories);
+    auto i = factories.find(std::string(scheme));
+    if (i == factories.end()) {
+      return {};
+    }
+    return std::move(i->second);
+  }
+  return {};
 }
 }  // namespace
 
@@ -2914,10 +2934,14 @@
     std::unique_ptr<protocol::Network::LoadNetworkResourceOptions> options,
     std::unique_ptr<LoadNetworkResourceCallback> callback) {
   GURL gurl(url);
-  const bool is_gurl_valid = gurl.is_valid() && gurl.SchemeIsHTTPOrHTTPS();
+  const bool is_gurl_valid = gurl.is_valid();
   if (!is_gurl_valid) {
-    callback->sendFailure(Response::InvalidParams(
-        "The url must be valid and have scheme http or https"));
+    callback->sendFailure(Response::InvalidParams("The url must be valid"));
+    return;
+  }
+
+  if (gurl.SchemeIs(url::kFileScheme) && !allow_file_access_) {
+    callback->sendFailure(Response::InvalidParams("Unsupported URL scheme"));
     return;
   }
 
@@ -2964,8 +2988,10 @@
         network::mojom::TrustTokenRedemptionPolicy::kForbid,
         "NetworkHandler::LoadNetworkResource");
 
-    auto factory =
-        CreateNetworkFactoryForDevTools(frame->GetProcess(), std::move(params));
+    auto factory = CreateNetworkFactoryForDevTools(
+        gurl.scheme(), frame->GetProcess(), frame->GetRoutingID(),
+        std::move(params));
+
     url_loader_factory.Bind(std::move(factory));
     auto loader = DevToolsNetworkResourceLoader::Create(
         std::move(url_loader_factory), std::move(gurl),
@@ -2980,7 +3006,8 @@
     // TODO(sigurds): Support dedicated workers.
     auto info = host->CreateNetworkFactoryParamsForDevTools();
     auto factory = CreateNetworkFactoryForDevTools(
-        host->GetProcessHost(), std::move(info.factory_params));
+        gurl.scheme(), host->GetProcessHost(), MSG_ROUTING_NONE,
+        std::move(info.factory_params));
     if (factory.is_valid()) {
       url_loader_factory.Bind(std::move(factory));
       auto loader = DevToolsNetworkResourceLoader::Create(
diff --git a/content/browser/devtools/protocol/network_handler.h b/content/browser/devtools/protocol/network_handler.h
index 4771d5e..c343166 100644
--- a/content/browser/devtools/protocol/network_handler.h
+++ b/content/browser/devtools/protocol/network_handler.h
@@ -70,7 +70,8 @@
   NetworkHandler(const std::string& host_id,
                  const base::UnguessableToken& devtools_token,
                  DevToolsIOContext* io_context,
-                 base::RepeatingClosure update_loader_factories_callback);
+                 base::RepeatingClosure update_loader_factories_callback,
+                 bool allow_file_access);
 
   NetworkHandler(const NetworkHandler&) = delete;
   NetworkHandler& operator=(const NetworkHandler&) = delete;
@@ -342,6 +343,7 @@
       loaders_;
   absl::optional<std::set<net::SourceStream::SourceType>>
       accepted_stream_types_;
+  const bool allow_file_access_;
   base::WeakPtrFactory<NetworkHandler> weak_factory_{this};
 };
 
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index 3c0b00cb..9aaf942 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -326,7 +326,8 @@
       GetIOContext(),
       base::BindRepeating(
           &RenderFrameDevToolsAgentHost::UpdateResourceLoaderFactories,
-          base::Unretained(this))));
+          base::Unretained(this)),
+      session->GetClient()->MayReadLocalFiles()));
   session->AddHandler(std::make_unique<protocol::FetchHandler>(
       GetIOContext(), base::BindRepeating(
                           [](RenderFrameDevToolsAgentHost* self,
diff --git a/content/browser/devtools/service_worker_devtools_agent_host.cc b/content/browser/devtools/service_worker_devtools_agent_host.cc
index f013a7f..c94b2773 100644
--- a/content/browser/devtools/service_worker_devtools_agent_host.cc
+++ b/content/browser/devtools/service_worker_devtools_agent_host.cc
@@ -229,7 +229,9 @@
   session->AddHandler(std::make_unique<protocol::IOHandler>(GetIOContext()));
   session->AddHandler(std::make_unique<protocol::InspectorHandler>());
   session->AddHandler(std::make_unique<protocol::NetworkHandler>(
-      GetId(), devtools_worker_token_, GetIOContext(), base::DoNothing()));
+      GetId(), devtools_worker_token_, GetIOContext(), base::DoNothing(),
+      session->GetClient()->MayReadLocalFiles()));
+
   session->AddHandler(std::make_unique<protocol::FetchHandler>(
       GetIOContext(),
       base::BindRepeating(
diff --git a/content/browser/devtools/shared_worker_devtools_agent_host.cc b/content/browser/devtools/shared_worker_devtools_agent_host.cc
index 0c5966e..539d032 100644
--- a/content/browser/devtools/shared_worker_devtools_agent_host.cc
+++ b/content/browser/devtools/shared_worker_devtools_agent_host.cc
@@ -91,7 +91,7 @@
   session->AddHandler(std::make_unique<protocol::InspectorHandler>());
   session->AddHandler(std::make_unique<protocol::NetworkHandler>(
       GetId(), devtools_worker_token_, GetIOContext(),
-      base::BindRepeating([] {})));
+      base::BindRepeating([] {}), session->GetClient()->MayReadLocalFiles()));
   // TODO(crbug.com/1143100): support pushing updated loader factories down to
   // renderer.
   session->AddHandler(std::make_unique<protocol::FetchHandler>(
diff --git a/content/browser/devtools/worker_devtools_agent_host.cc b/content/browser/devtools/worker_devtools_agent_host.cc
index 5daf2de..dec63fe8 100644
--- a/content/browser/devtools/worker_devtools_agent_host.cc
+++ b/content/browser/devtools/worker_devtools_agent_host.cc
@@ -136,7 +136,8 @@
       protocol::TargetHandler::AccessMode::kAutoAttachOnly, GetId(),
       auto_attacher_.get(), session->GetRootSession()));
   session->AddHandler(std::make_unique<protocol::NetworkHandler>(
-      GetId(), devtools_worker_token_, GetIOContext(), base::DoNothing()));
+      GetId(), devtools_worker_token_, GetIOContext(), base::DoNothing(),
+      session->GetClient()->MayReadLocalFiles()));
   return true;
 }
 
diff --git a/content/browser/renderer_host/frame_tree_node.cc b/content/browser/renderer_host/frame_tree_node.cc
index e86427b..01a84ac 100644
--- a/content/browser/renderer_host/frame_tree_node.cc
+++ b/content/browser/renderer_host/frame_tree_node.cc
@@ -309,20 +309,6 @@
   }
 }
 
-size_t FrameTreeNode::GetFrameTreeSize() const {
-  if (is_collapsed())
-    return 0;
-
-  size_t size = 0;
-  for (size_t i = 0; i < child_count(); i++) {
-    size += child_at(i)->GetFrameTreeSize();
-  }
-
-  // Account for this node.
-  size++;
-  return size;
-}
-
 void FrameTreeNode::SetOpener(FrameTreeNode* opener) {
   if (opener_) {
     opener_->RemoveObserver(opener_observer_.get());
diff --git a/content/browser/renderer_host/frame_tree_node.h b/content/browser/renderer_host/frame_tree_node.h
index 69d727fd..ad01d27f 100644
--- a/content/browser/renderer_host/frame_tree_node.h
+++ b/content/browser/renderer_host/frame_tree_node.h
@@ -149,10 +149,6 @@
   // Returns the type of the frame. Refer to frame_type.h for the details.
   FrameType GetFrameType() const;
 
-  // Gets the total number of descendants to this FrameTreeNode in addition to
-  // this node.
-  size_t GetFrameTreeSize() const;
-
   // Assigns a new opener for this node and, if |opener| is non-null, registers
   // an observer that will clear this node's opener if |opener| is ever
   // destroyed.
diff --git a/content/browser/renderer_host/media/service_video_capture_device_launcher.cc b/content/browser/renderer_host/media/service_video_capture_device_launcher.cc
index 19748325a..05f5a67 100644
--- a/content/browser/renderer_host/media/service_video_capture_device_launcher.cc
+++ b/content/browser/renderer_host/media/service_video_capture_device_launcher.cc
@@ -191,7 +191,7 @@
     mojo::Remote<video_capture::mojom::PushVideoStreamSubscription>
         subscription,
     base::OnceClosure connection_lost_cb,
-    video_capture::mojom::CreatePushSubscriptionResultCode result_code,
+    video_capture::mojom::CreatePushSubscriptionResultCodePtr result_code,
     const media::VideoCaptureParams& params) {
   DCHECK(sequence_checker_.CalledOnValidSequence());
   DCHECK(callbacks_);
@@ -201,11 +201,9 @@
   state_ = State::READY_TO_LAUNCH;
   Callbacks* callbacks = callbacks_;
   callbacks_ = nullptr;
-  switch (result_code) {
-    case video_capture::mojom::CreatePushSubscriptionResultCode::
-        kCreatedWithRequestedSettings:  // Fall through.
-    case video_capture::mojom::CreatePushSubscriptionResultCode::
-        kCreatedWithDifferentSettings:
+  switch (result_code->which()) {
+    case video_capture::mojom::CreatePushSubscriptionResultCode::Tag::
+        SUCCESS_CODE:
       if (abort_requested) {
         subscription.reset();
         source.reset();
@@ -218,12 +216,13 @@
           std::move(source), std::move(subscription),
           std::move(connection_lost_cb), callbacks, std::move(done_cb_));
       return;
-    case video_capture::mojom::CreatePushSubscriptionResultCode::kFailed:
-      ConcludeLaunchDeviceWithFailure(
-          abort_requested,
-          media::VideoCaptureError::
-              kServiceDeviceLauncherServiceRespondedWithDeviceNotFound,
-          std::move(service_connection_), callbacks, std::move(done_cb_));
+    case video_capture::mojom::CreatePushSubscriptionResultCode::Tag::
+        ERROR_CODE:
+      media::VideoCaptureError error = result_code->get_error_code();
+      DCHECK_NE(error, media::VideoCaptureError::kNone);
+      ConcludeLaunchDeviceWithFailure(abort_requested, error,
+                                      std::move(service_connection_), callbacks,
+                                      std::move(done_cb_));
       return;
   }
 }
diff --git a/content/browser/renderer_host/media/service_video_capture_device_launcher.h b/content/browser/renderer_host/media/service_video_capture_device_launcher.h
index da04a0184..10388fd7 100644
--- a/content/browser/renderer_host/media/service_video_capture_device_launcher.h
+++ b/content/browser/renderer_host/media/service_video_capture_device_launcher.h
@@ -53,7 +53,7 @@
       mojo::Remote<video_capture::mojom::PushVideoStreamSubscription>
           subscription,
       base::OnceClosure connection_lost_cb,
-      video_capture::mojom::CreatePushSubscriptionResultCode result_code,
+      video_capture::mojom::CreatePushSubscriptionResultCodePtr result_code,
       const media::VideoCaptureParams& params);
 
   void OnConnectionLostWhileWaitingForCallback();
diff --git a/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc b/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc
index 917cbd2..17c5855 100644
--- a/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc
+++ b/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc
@@ -68,8 +68,7 @@
         mojo::Receiver<video_capture::mojom::VideoSourceProvider>>(
         &mock_source_provider_, source_provider_.BindNewPipeAndPassReceiver());
     service_connection_ = base::MakeRefCounted<RefCountedVideoSourceProvider>(
-        std::move(source_provider_),
-        release_connection_cb_.Get());
+        std::move(source_provider_), release_connection_cb_.Get());
 
     launcher_ = std::make_unique<ServiceVideoCaptureDeviceLauncher>(
         connect_to_device_factory_cb_.Get());
@@ -114,7 +113,9 @@
                                           std::move(subscription));
               std::move(callback).Run(
                   video_capture::mojom::CreatePushSubscriptionResultCode::
-                      kCreatedWithRequestedSettings,
+                      NewSuccessCode(video_capture::mojom::
+                                         CreatePushSubscriptionSuccessCode::
+                                             kCreatedWithRequestedSettings),
                   requested_settings);
             }));
   }
@@ -122,7 +123,7 @@
   void TearDown() override {}
 
   void RunLaunchingDeviceIsAbortedTest(
-      video_capture::mojom::CreatePushSubscriptionResultCode
+      video_capture::mojom::CreatePushSubscriptionResultCodePtr
           service_result_code);
   void RunConnectionLostAfterSuccessfulStartTest(
       base::OnceClosure close_connection_cb);
@@ -181,25 +182,28 @@
 TEST_F(ServiceVideoCaptureDeviceLauncherTest,
        LaunchingDeviceIsAbortedBeforeServiceRespondsWithSuccess) {
   RunLaunchingDeviceIsAbortedTest(
-      video_capture::mojom::CreatePushSubscriptionResultCode::
-          kCreatedWithRequestedSettings);
+      video_capture::mojom::CreatePushSubscriptionResultCode::NewSuccessCode(
+          video_capture::mojom::CreatePushSubscriptionSuccessCode::
+              kCreatedWithRequestedSettings));
 }
 
 TEST_F(ServiceVideoCaptureDeviceLauncherTest,
        LaunchingDeviceIsAbortedBeforeServiceRespondsWithNotFound) {
   RunLaunchingDeviceIsAbortedTest(
-      video_capture::mojom::CreatePushSubscriptionResultCode::
-          kCreatedWithDifferentSettings);
+      video_capture::mojom::CreatePushSubscriptionResultCode::NewSuccessCode(
+          video_capture::mojom::CreatePushSubscriptionSuccessCode::
+              kCreatedWithDifferentSettings));
 }
 
 TEST_F(ServiceVideoCaptureDeviceLauncherTest,
        LaunchingDeviceIsAbortedBeforeServiceRespondsWithNotInitialized) {
   RunLaunchingDeviceIsAbortedTest(
-      video_capture::mojom::CreatePushSubscriptionResultCode::kFailed);
+      video_capture::mojom::CreatePushSubscriptionResultCode::NewErrorCode(
+          media::VideoCaptureError::kIntentionalErrorRaisedByUnitTest));
 }
 
 void ServiceVideoCaptureDeviceLauncherTest::RunLaunchingDeviceIsAbortedTest(
-    video_capture::mojom::CreatePushSubscriptionResultCode
+    video_capture::mojom::CreatePushSubscriptionResultCodePtr
         service_result_code) {
   base::RunLoop step_1_run_loop;
   base::RunLoop step_2_run_loop;
@@ -208,7 +212,7 @@
   EXPECT_CALL(mock_source_, DoCreatePushSubscription(_, _, _, _, _))
       .WillOnce(Invoke(
           [&create_push_subscription_success_answer_cb, &step_1_run_loop,
-           service_result_code](
+           &service_result_code](
               mojo::PendingRemote<video_capture::mojom::VideoFrameHandler>
                   subscriber,
               const media::VideoCaptureParams& requested_settings,
@@ -226,13 +230,13 @@
                        subscription,
                    video_capture::mojom::VideoSource::
                        CreatePushSubscriptionCallback callback,
-                   video_capture::mojom::CreatePushSubscriptionResultCode
+                   video_capture::mojom::CreatePushSubscriptionResultCodePtr
                        service_result_code) {
-                  std::move(callback).Run(service_result_code,
+                  std::move(callback).Run(std::move(service_result_code),
                                           requested_settings);
                 },
                 requested_settings, std::move(subscription),
-                std::move(callback), service_result_code);
+                std::move(callback), std::move(service_result_code));
             step_1_run_loop.Quit();
           }));
   EXPECT_CALL(mock_callbacks_, DoOnDeviceLaunched(_)).Times(0);
@@ -290,7 +294,9 @@
                            CreatePushSubscriptionCallback callback) {
                       std::move(callback).Run(
                           video_capture::mojom::
-                              CreatePushSubscriptionResultCode::kFailed,
+                              CreatePushSubscriptionResultCode::NewErrorCode(
+                                  media::VideoCaptureError::
+                                      kIntentionalErrorRaisedByUnitTest),
                           requested_settings);
                     },
                     std::move(subscriber), requested_settings,
@@ -300,8 +306,7 @@
   EXPECT_CALL(mock_callbacks_, OnDeviceLaunchAborted()).Times(0);
   EXPECT_CALL(mock_callbacks_,
               OnDeviceLaunchFailed(
-                  media::VideoCaptureError::
-                      kServiceDeviceLauncherServiceRespondedWithDeviceNotFound))
+                  media::VideoCaptureError::kIntentionalErrorRaisedByUnitTest))
       .Times(1);
   EXPECT_CALL(connection_lost_cb_, Run()).Times(0);
   EXPECT_CALL(done_cb_, Run()).WillOnce(InvokeWithoutArgs([&run_loop]() {
@@ -385,11 +390,12 @@
   // |create_subscription_cb| will be dropped when we invoke it below.
   source_receiver_.reset();
   // We have to invoke the callback, because not doing so triggers a DCHECK.
-  const video_capture::mojom::CreatePushSubscriptionResultCode
-      arbitrary_result_code = video_capture::mojom::
-          CreatePushSubscriptionResultCode::kCreatedWithRequestedSettings;
   std::move(create_subscription_cb)
-      .Run(arbitrary_result_code, kArbitraryParams);
+      .Run(video_capture::mojom::CreatePushSubscriptionResultCode::
+               NewSuccessCode(
+                   video_capture::mojom::CreatePushSubscriptionSuccessCode::
+                       kCreatedWithRequestedSettings),
+           kArbitraryParams);
 }
 
 TEST_F(ServiceVideoCaptureDeviceLauncherTest,
diff --git a/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc b/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc
index afbf3c5..7f8a6f5a 100644
--- a/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc
+++ b/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc
@@ -125,7 +125,9 @@
                                           std::move(subscription));
               std::move(callback).Run(
                   video_capture::mojom::CreatePushSubscriptionResultCode::
-                      kCreatedWithRequestedSettings,
+                      NewSuccessCode(video_capture::mojom::
+                                         CreatePushSubscriptionSuccessCode::
+                                             kCreatedWithRequestedSettings),
                   requested_settings);
             }));
   }
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 058fec6..110a43f 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -648,9 +648,7 @@
   loop.Run();
 }
 
-//
 // SitePerProcessBrowserTest
-//
 
 SitePerProcessBrowserTest::SitePerProcessBrowserTest() {
   InitAndEnableRenderDocumentFeature(&feature_list_, GetParam());
@@ -662,15 +660,13 @@
   return url::Origin::Create(url).Serialize();
 }
 
-//
 // SitePerProcessHighDPIBrowserTest
-//
 
 class SitePerProcessHighDPIBrowserTest : public SitePerProcessBrowserTest {
  public:
   const double kDeviceScaleFactor = 2.0;
 
-  SitePerProcessHighDPIBrowserTest() {}
+  SitePerProcessHighDPIBrowserTest() = default;
 
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
@@ -683,35 +679,28 @@
 
 // SitePerProcessIgnoreCertErrorsBrowserTest
 
-class SitePerProcessIgnoreCertErrorsBrowserTest
-    : public SitePerProcessBrowserTest {
- public:
-  SitePerProcessIgnoreCertErrorsBrowserTest() {}
+void SitePerProcessIgnoreCertErrorsBrowserTest::SetUpOnMainThread() {
+  SitePerProcessBrowserTest::SetUpOnMainThread();
+  mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
+}
 
- protected:
-  void SetUpOnMainThread() override {
-    SitePerProcessBrowserTest::SetUpOnMainThread();
-    mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
-  }
+void SitePerProcessIgnoreCertErrorsBrowserTest::SetUpCommandLine(
+    base::CommandLine* command_line) {
+  SitePerProcessBrowserTest::SetUpCommandLine(command_line);
+  mock_cert_verifier_.SetUpCommandLine(command_line);
+}
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    SitePerProcessBrowserTest::SetUpCommandLine(command_line);
-    mock_cert_verifier_.SetUpCommandLine(command_line);
-  }
+void SitePerProcessIgnoreCertErrorsBrowserTest::
+    SetUpInProcessBrowserTestFixture() {
+  SitePerProcessBrowserTest::SetUpInProcessBrowserTestFixture();
+  mock_cert_verifier_.SetUpInProcessBrowserTestFixture();
+}
 
-  void SetUpInProcessBrowserTestFixture() override {
-    SitePerProcessBrowserTest::SetUpInProcessBrowserTestFixture();
-    mock_cert_verifier_.SetUpInProcessBrowserTestFixture();
-  }
-
-  void TearDownInProcessBrowserTestFixture() override {
-    SitePerProcessBrowserTest::TearDownInProcessBrowserTestFixture();
-    mock_cert_verifier_.TearDownInProcessBrowserTestFixture();
-  }
-
- private:
-  content::ContentMockCertVerifier mock_cert_verifier_;
-};
+void SitePerProcessIgnoreCertErrorsBrowserTest::
+    TearDownInProcessBrowserTestFixture() {
+  SitePerProcessBrowserTest::TearDownInProcessBrowserTestFixture();
+  mock_cert_verifier_.TearDownInProcessBrowserTestFixture();
+}
 
 // SitePerProcessAutoplayBrowserTest
 
@@ -737,27 +726,6 @@
   }
 };
 
-// SitePerProcessWebBundleBrowserTest.
-
-class SitePerProcessWebBundleBrowserTest
-    : public SitePerProcessIgnoreCertErrorsBrowserTest {
- public:
-  SitePerProcessWebBundleBrowserTest() {
-    feature_list_.InitAndEnableFeature(features::kSubresourceWebBundles);
-  }
-  void SetUpOnMainThread() override {
-    SitePerProcessIgnoreCertErrorsBrowserTest::SetUpOnMainThread();
-    https_server_.ServeFilesFromSourceDirectory(GetTestDataFilePath());
-    ASSERT_TRUE(https_server_.Start());
-  }
-  net::EmbeddedTestServer* https_server() { return &https_server_; }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-  net::EmbeddedTestServer https_server_{
-      net::EmbeddedTestServer::Type::TYPE_HTTPS};
-};
-
 IN_PROC_BROWSER_TEST_P(SitePerProcessHighDPIBrowserTest,
                        SubframeLoadsWithCorrectDeviceScaleFactor) {
   GURL main_url(embedded_test_server()->GetURL(
@@ -5826,181 +5794,6 @@
             EvalJs(foo_root, "self.origin;"));
 }
 
-// A subclass of SitePerProcessIgnoreCertErrorsBrowsertest that disables mixed
-// content autoupgrades.
-// TODO(carlosil): Since the flag will be cleaned up eventually, this should be
-// changed to proper plumbing that adds the relevant urls to the allowlist.
-class SitePerProcessIgnoreCertErrorsAllowMixedContentBrowserTest
-    : public SitePerProcessIgnoreCertErrorsBrowserTest {
- public:
-  SitePerProcessIgnoreCertErrorsAllowMixedContentBrowserTest() {
-    feature_list.InitAndDisableFeature(
-        blink::features::kMixedContentAutoupgrade);
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list;
-};
-
-// Tests that the WebContents is notified when passive mixed content is
-// displayed in an OOPIF. The test ignores cert errors so that an HTTPS
-// iframe can be loaded from a site other than localhost (the
-// EmbeddedTestServer serves a certificate that is valid for localhost).
-// This test crashes on Windows under Dr. Memory, see https://crbug.com/600942.
-#if defined(OS_WIN)
-#define MAYBE_PassiveMixedContentInIframe DISABLED_PassiveMixedContentInIframe
-#else
-#define MAYBE_PassiveMixedContentInIframe PassiveMixedContentInIframe
-#endif
-IN_PROC_BROWSER_TEST_P(
-    SitePerProcessIgnoreCertErrorsAllowMixedContentBrowserTest,
-    MAYBE_PassiveMixedContentInIframe) {
-  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
-  https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
-  SetupCrossSiteRedirector(&https_server);
-  ASSERT_TRUE(https_server.Start());
-
-  WebContentsImpl* web_contents =
-      static_cast<WebContentsImpl*>(shell()->web_contents());
-
-  GURL iframe_url(
-      https_server.GetURL("/mixed-content/basic-passive-in-iframe.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), iframe_url));
-  NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
-  EXPECT_TRUE(!!(entry->GetSSL().content_status &
-                 SSLStatus::DISPLAYED_INSECURE_CONTENT));
-
-  // When the subframe navigates, the WebContents should still be marked
-  // as having displayed insecure content.
-  GURL navigate_url(https_server.GetURL("/title1.html"));
-  FrameTreeNode* root = web_contents->GetPrimaryFrameTree().root();
-  EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), navigate_url));
-  entry = web_contents->GetController().GetVisibleEntry();
-  EXPECT_TRUE(!!(entry->GetSSL().content_status &
-                 SSLStatus::DISPLAYED_INSECURE_CONTENT));
-
-  // When the main frame navigates, it should no longer be marked as
-  // displaying insecure content.
-  EXPECT_TRUE(
-      NavigateToURL(shell(), https_server.GetURL("b.com", "/title1.html")));
-  entry = web_contents->GetController().GetVisibleEntry();
-  EXPECT_FALSE(!!(entry->GetSSL().content_status &
-                  SSLStatus::DISPLAYED_INSECURE_CONTENT));
-}
-
-// Tests that, when a parent frame is set to strictly block mixed
-// content via Content Security Policy, child OOPIFs cannot display
-// mixed content.
-IN_PROC_BROWSER_TEST_P(SitePerProcessIgnoreCertErrorsBrowserTest,
-                       PassiveMixedContentInIframeWithStrictBlocking) {
-  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
-  https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
-  SetupCrossSiteRedirector(&https_server);
-  ASSERT_TRUE(https_server.Start());
-
-  WebContentsImpl* web_contents =
-      static_cast<WebContentsImpl*>(shell()->web_contents());
-
-  GURL iframe_url_with_strict_blocking(https_server.GetURL(
-      "/mixed-content/basic-passive-in-iframe-with-strict-blocking.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), iframe_url_with_strict_blocking));
-  NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
-  EXPECT_FALSE(!!(entry->GetSSL().content_status &
-                  SSLStatus::DISPLAYED_INSECURE_CONTENT));
-
-  FrameTreeNode* root = web_contents->GetPrimaryFrameTree().root();
-  EXPECT_EQ(blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent,
-            root->current_replication_state().insecure_request_policy);
-  EXPECT_EQ(
-      blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent,
-      root->child_at(0)->current_replication_state().insecure_request_policy);
-
-  // When the subframe navigates, it should still be marked as enforcing
-  // strict mixed content.
-  GURL navigate_url(https_server.GetURL("/title1.html"));
-  EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), navigate_url));
-  EXPECT_EQ(blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent,
-            root->current_replication_state().insecure_request_policy);
-  EXPECT_EQ(
-      blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent,
-      root->child_at(0)->current_replication_state().insecure_request_policy);
-
-  // When the main frame navigates, it should no longer be marked as
-  // enforcing strict mixed content.
-  EXPECT_TRUE(
-      NavigateToURL(shell(), https_server.GetURL("b.com", "/title1.html")));
-  EXPECT_EQ(blink::mojom::InsecureRequestPolicy::kLeaveInsecureRequestsAlone,
-            root->current_replication_state().insecure_request_policy);
-}
-
-// Tests that, when a parent frame is set to upgrade insecure requests
-// via Content Security Policy, child OOPIFs will upgrade as well.
-IN_PROC_BROWSER_TEST_P(SitePerProcessIgnoreCertErrorsBrowserTest,
-                       PassiveMixedContentInIframeWithUpgrade) {
-  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
-  https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
-  SetupCrossSiteRedirector(&https_server);
-  ASSERT_TRUE(https_server.Start());
-
-  WebContentsImpl* web_contents =
-      static_cast<WebContentsImpl*>(shell()->web_contents());
-
-  GURL iframe_url_with_upgrade(https_server.GetURL(
-      "/mixed-content/basic-passive-in-iframe-with-upgrade.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), iframe_url_with_upgrade));
-  NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
-  EXPECT_FALSE(!!(entry->GetSSL().content_status &
-                  SSLStatus::DISPLAYED_INSECURE_CONTENT));
-
-  FrameTreeNode* root = web_contents->GetPrimaryFrameTree().root();
-  EXPECT_EQ(blink::mojom::InsecureRequestPolicy::kUpgradeInsecureRequests,
-            root->current_replication_state().insecure_request_policy);
-  EXPECT_EQ(
-      blink::mojom::InsecureRequestPolicy::kUpgradeInsecureRequests,
-      root->child_at(0)->current_replication_state().insecure_request_policy);
-
-  // When the subframe navigates, it should still be marked as upgrading
-  // insecure requests.
-  GURL navigate_url(https_server.GetURL("/title1.html"));
-  EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), navigate_url));
-  EXPECT_EQ(blink::mojom::InsecureRequestPolicy::kUpgradeInsecureRequests,
-            root->current_replication_state().insecure_request_policy);
-  EXPECT_EQ(
-      blink::mojom::InsecureRequestPolicy::kUpgradeInsecureRequests,
-      root->child_at(0)->current_replication_state().insecure_request_policy);
-
-  // When the main frame navigates, it should no longer be marked as
-  // upgrading insecure requests.
-  EXPECT_TRUE(
-      NavigateToURL(shell(), https_server.GetURL("b.com", "/title1.html")));
-  EXPECT_EQ(blink::mojom::InsecureRequestPolicy::kLeaveInsecureRequestsAlone,
-            root->current_replication_state().insecure_request_policy);
-}
-
-// Tests that active mixed content is blocked in an OOPIF. The test
-// ignores cert errors so that an HTTPS iframe can be loaded from a site
-// other than localhost (the EmbeddedTestServer serves a certificate
-// that is valid for localhost).
-IN_PROC_BROWSER_TEST_P(SitePerProcessIgnoreCertErrorsBrowserTest,
-                       ActiveMixedContentInIframe) {
-  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
-  https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
-  SetupCrossSiteRedirector(&https_server);
-  ASSERT_TRUE(https_server.Start());
-
-  GURL iframe_url(
-      https_server.GetURL("/mixed-content/basic-active-in-iframe.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), iframe_url));
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  ASSERT_EQ(1U, root->child_count());
-  FrameTreeNode* mixed_child = root->child_at(0)->child_at(0);
-  ASSERT_TRUE(mixed_child);
-  // The child iframe attempted to create a mixed iframe; this should
-  // have been blocked, so the mixed iframe should still be on the initial empty
-  // document.
-  EXPECT_TRUE(mixed_child->is_on_initial_empty_document());
-}
-
 // Test that subresources with certificate errors get reported to the
 // browser. That is, if https://example.test frames https://a.com which
 // loads an image with certificate errors, the browser should be
@@ -15234,176 +15027,6 @@
   }
 }
 
-// Check that a uuid-in-package: subframe instantiated from a same-origin
-// WebBundle reuses its parent's process.
-IN_PROC_BROWSER_TEST_P(SitePerProcessWebBundleBrowserTest, SameSiteBundle) {
-  GURL bundle_url(
-      https_server()->GetURL("foo.test", "/web_bundle/uuid-in-package.wbn"));
-  GURL frame_url("uuid-in-package:429fcc4e-0696-4bad-b099-ee9175f023ae");
-  GURL main_url(https_server()->GetURL(
-      "foo.test", "/web_bundle/frame_parent.html?wbn=" + bundle_url.spec() +
-                      "&frame=" + frame_url.spec()));
-  std::u16string expected_title(u"OK");
-  TitleWatcher title_watcher(shell()->web_contents(), expected_title);
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  ASSERT_EQ(1U, root->child_count());
-  FrameTreeNode* child_node = root->child_at(0);
-  EXPECT_EQ(child_node->current_url(), frame_url);
-  EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
-            child_node->current_frame_host()->GetSiteInstance());
-}
-
-// Check that a uuid-in-package: subframe instantiated from a WebBundle gets a
-// process for the Bundle's origin.
-IN_PROC_BROWSER_TEST_P(SitePerProcessWebBundleBrowserTest, CrossSiteBundle) {
-  GURL bundle_url(
-      https_server()->GetURL("bar.test", "/web_bundle/uuid-in-package.wbn"));
-  GURL frame_url("uuid-in-package:429fcc4e-0696-4bad-b099-ee9175f023ae");
-  GURL main_url(https_server()->GetURL(
-      "foo.test", "/web_bundle/frame_parent.html?wbn=" + bundle_url.spec() +
-                      "&frame=" + frame_url.spec()));
-  std::u16string expected_title(u"OK");
-  TitleWatcher title_watcher(shell()->web_contents(), expected_title);
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  ASSERT_EQ(1U, root->child_count());
-  FrameTreeNode* child_node = root->child_at(0);
-  EXPECT_EQ(child_node->current_url(), frame_url);
-  url::Origin last_committed_origin =
-      child_node->current_frame_host()->GetLastCommittedOrigin();
-  EXPECT_TRUE(last_committed_origin.opaque());
-  const url::SchemeHostPort& tuple =
-      last_committed_origin.GetTupleOrPrecursorTupleIfOpaque();
-  EXPECT_EQ("bar.test", tuple.host());
-
-  // An iframe nested in the uuid-in-package: iframe gets a non-opaque origin.
-  GURL c_url = https_server()->GetURL("c.test", "/title1.html");
-  TestNavigationObserver observer(c_url);
-  observer.WatchExistingWebContents();
-
-  // Create the subframe now.
-  std::string create_frame_script = base::StringPrintf(
-      "var new_iframe = document.createElement('iframe');"
-      "new_iframe.src = '%s';"
-      "document.body.appendChild(new_iframe);",
-      c_url.spec().c_str());
-  EXPECT_TRUE(ExecJs(child_node, create_frame_script));
-
-  observer.WaitForNavigationFinished();
-  EXPECT_TRUE(observer.last_navigation_succeeded());
-
-  ASSERT_EQ(1U, child_node->child_count());
-  FrameTreeNode* grandchild_node = child_node->child_at(0);
-  url::Origin grandchild_committed_origin =
-      grandchild_node->current_frame_host()->GetLastCommittedOrigin();
-  EXPECT_FALSE(grandchild_committed_origin.opaque());
-  const url::SchemeHostPort& c_tuple =
-      grandchild_committed_origin.GetTupleOrPrecursorTupleIfOpaque();
-  EXPECT_EQ("c.test", c_tuple.host());
-  EXPECT_FALSE(
-      last_committed_origin.IsSameOriginWith(grandchild_committed_origin));
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B C\n"
-      "   +--Site B ------- proxies for A C\n"
-      "        +--Site C -- proxies for A B\n"
-      "Where A = https://foo.test/\n"
-      "      B = https://bar.test/\n"
-      "      C = https://c.test/",
-      DepictFrameTree(root));
-}
-
-// Check that a urn:uuid subframe instantiated from a same-origin WebBundle
-// reuses its parent's process.
-IN_PROC_BROWSER_TEST_P(SitePerProcessWebBundleBrowserTest,
-                       SameSiteUrnUuidBundle) {
-  GURL bundle_url(
-      https_server()->GetURL("foo.test", "/web_bundle/urn-uuid.wbn"));
-  GURL frame_url("urn:uuid:429fcc4e-0696-4bad-b099-ee9175f023ae");
-  GURL main_url(https_server()->GetURL(
-      "foo.test", "/web_bundle/frame_parent.html?wbn=" + bundle_url.spec() +
-                      "&frame=" + frame_url.spec()));
-  std::u16string expected_title(u"OK");
-  TitleWatcher title_watcher(shell()->web_contents(), expected_title);
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  ASSERT_EQ(1U, root->child_count());
-  FrameTreeNode* child_node = root->child_at(0);
-  EXPECT_EQ(child_node->current_url(), frame_url);
-  EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
-            child_node->current_frame_host()->GetSiteInstance());
-}
-
-// Check that a urn:uuid subframe instantiated from a WebBundle gets a process
-// for the Bundle's origin.
-IN_PROC_BROWSER_TEST_P(SitePerProcessWebBundleBrowserTest,
-                       CrossSiteUrnUuidBundle) {
-  GURL bundle_url(
-      https_server()->GetURL("bar.test", "/web_bundle/urn-uuid.wbn"));
-  GURL frame_url("urn:uuid:429fcc4e-0696-4bad-b099-ee9175f023ae");
-  GURL main_url(https_server()->GetURL(
-      "foo.test", "/web_bundle/frame_parent.html?wbn=" + bundle_url.spec() +
-                      "&frame=" + frame_url.spec()));
-  std::u16string expected_title(u"OK");
-  TitleWatcher title_watcher(shell()->web_contents(), expected_title);
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  ASSERT_EQ(1U, root->child_count());
-  FrameTreeNode* child_node = root->child_at(0);
-  EXPECT_EQ(child_node->current_url(), frame_url);
-  url::Origin last_committed_origin =
-      child_node->current_frame_host()->GetLastCommittedOrigin();
-  EXPECT_TRUE(last_committed_origin.opaque());
-  const url::SchemeHostPort& tuple =
-      last_committed_origin.GetTupleOrPrecursorTupleIfOpaque();
-  EXPECT_EQ("bar.test", tuple.host());
-
-  // An iframe nested in the urn:uuid iframe gets a non-opaque origin.
-  GURL c_url = https_server()->GetURL("c.test", "/title1.html");
-  TestNavigationObserver observer(c_url);
-  observer.WatchExistingWebContents();
-
-  // Create the subframe now.
-  std::string create_frame_script = base::StringPrintf(
-      "var new_iframe = document.createElement('iframe');"
-      "new_iframe.src = '%s';"
-      "document.body.appendChild(new_iframe);",
-      c_url.spec().c_str());
-  EXPECT_TRUE(ExecJs(child_node, create_frame_script));
-
-  observer.WaitForNavigationFinished();
-  EXPECT_TRUE(observer.last_navigation_succeeded());
-
-  ASSERT_EQ(1U, child_node->child_count());
-  FrameTreeNode* grandchild_node = child_node->child_at(0);
-  url::Origin grandchild_committed_origin =
-      grandchild_node->current_frame_host()->GetLastCommittedOrigin();
-  EXPECT_FALSE(grandchild_committed_origin.opaque());
-  const url::SchemeHostPort& c_tuple =
-      grandchild_committed_origin.GetTupleOrPrecursorTupleIfOpaque();
-  EXPECT_EQ("c.test", c_tuple.host());
-  EXPECT_FALSE(
-      last_committed_origin.IsSameOriginWith(grandchild_committed_origin));
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B C\n"
-      "   +--Site B ------- proxies for A C\n"
-      "        +--Site C -- proxies for A B\n"
-      "Where A = https://foo.test/\n"
-      "      B = https://bar.test/\n"
-      "      C = https://c.test/",
-      DepictFrameTree(root));
-}
-
 // Tests that verify the feature disabling process reuse.
 class DisableProcessReusePolicyTest : public SitePerProcessBrowserTest {
  public:
@@ -15487,13 +15110,6 @@
 INSTANTIATE_TEST_SUITE_P(All,
                          DisableProcessReusePolicyTest,
                          testing::ValuesIn(RenderDocumentFeatureLevelValues()));
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    SitePerProcessIgnoreCertErrorsAllowMixedContentBrowserTest,
-    testing::ValuesIn(RenderDocumentFeatureLevelValues()));
-INSTANTIATE_TEST_SUITE_P(All,
-                         SitePerProcessWebBundleBrowserTest,
-                         testing::ValuesIn(RenderDocumentFeatureLevelValues()));
 #if defined(OS_ANDROID)
 INSTANTIATE_TEST_SUITE_P(All,
                          TouchSelectionControllerClientAndroidSiteIsolationTest,
diff --git a/content/browser/site_per_process_browsertest.h b/content/browser/site_per_process_browsertest.h
index 80847dd0..e66f75c7d 100644
--- a/content/browser/site_per_process_browsertest.h
+++ b/content/browser/site_per_process_browsertest.h
@@ -10,6 +10,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_mock_cert_verifier.h"
 #include "content/shell/browser/shell.h"
 #include "content/test/content_browser_test_utils_internal.h"
 #include "url/gurl.h"
@@ -63,6 +64,21 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
+class SitePerProcessIgnoreCertErrorsBrowserTest
+    : public SitePerProcessBrowserTest {
+ public:
+  SitePerProcessIgnoreCertErrorsBrowserTest() = default;
+
+ protected:
+  void SetUpOnMainThread() override;
+  void SetUpCommandLine(base::CommandLine* command_line) override;
+  void SetUpInProcessBrowserTestFixture() override;
+  void TearDownInProcessBrowserTestFixture() override;
+
+ private:
+  content::ContentMockCertVerifier mock_cert_verifier_;
+};
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_SITE_PER_PROCESS_BROWSERTEST_H_
diff --git a/content/browser/site_per_process_mixed_content_browsertest.cc b/content/browser/site_per_process_mixed_content_browsertest.cc
new file mode 100644
index 0000000..7267a32
--- /dev/null
+++ b/content/browser/site_per_process_mixed_content_browsertest.cc
@@ -0,0 +1,196 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/site_per_process_browsertest.h"
+
+#include "build/build_config.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/content_mock_cert_verifier.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "content/test/render_document_feature.h"
+
+namespace content {
+
+// A subclass of SitePerProcessIgnoreCertErrorsBrowsertest that disables mixed
+// content autoupgrades.
+// TODO(carlosil): Since the flag will be cleaned up eventually, this should be
+// changed to proper plumbing that adds the relevant urls to the allowlist.
+class SitePerProcessIgnoreCertErrorsAllowMixedContentBrowserTest
+    : public SitePerProcessIgnoreCertErrorsBrowserTest {
+ public:
+  SitePerProcessIgnoreCertErrorsAllowMixedContentBrowserTest() {
+    feature_list.InitAndDisableFeature(
+        blink::features::kMixedContentAutoupgrade);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list;
+};
+
+// Tests that, when a parent frame is set to strictly block mixed
+// content via Content Security Policy, child OOPIFs cannot display
+// mixed content.
+IN_PROC_BROWSER_TEST_P(SitePerProcessIgnoreCertErrorsBrowserTest,
+                       PassiveMixedContentInIframeWithStrictBlocking) {
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
+  SetupCrossSiteRedirector(&https_server);
+  ASSERT_TRUE(https_server.Start());
+
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+
+  GURL iframe_url_with_strict_blocking(https_server.GetURL(
+      "/mixed-content/basic-passive-in-iframe-with-strict-blocking.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), iframe_url_with_strict_blocking));
+  NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
+  EXPECT_FALSE(!!(entry->GetSSL().content_status &
+                  SSLStatus::DISPLAYED_INSECURE_CONTENT));
+
+  FrameTreeNode* root = web_contents->GetPrimaryFrameTree().root();
+  EXPECT_EQ(blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent,
+            root->current_replication_state().insecure_request_policy);
+  EXPECT_EQ(
+      blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent,
+      root->child_at(0)->current_replication_state().insecure_request_policy);
+
+  // When the subframe navigates, it should still be marked as enforcing
+  // strict mixed content.
+  GURL navigate_url(https_server.GetURL("/title1.html"));
+  EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), navigate_url));
+  EXPECT_EQ(blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent,
+            root->current_replication_state().insecure_request_policy);
+  EXPECT_EQ(
+      blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent,
+      root->child_at(0)->current_replication_state().insecure_request_policy);
+
+  // When the main frame navigates, it should no longer be marked as
+  // enforcing strict mixed content.
+  EXPECT_TRUE(
+      NavigateToURL(shell(), https_server.GetURL("b.com", "/title1.html")));
+  EXPECT_EQ(blink::mojom::InsecureRequestPolicy::kLeaveInsecureRequestsAlone,
+            root->current_replication_state().insecure_request_policy);
+}
+
+// Tests that, when a parent frame is set to upgrade insecure requests
+// via Content Security Policy, child OOPIFs will upgrade as well.
+IN_PROC_BROWSER_TEST_P(SitePerProcessIgnoreCertErrorsBrowserTest,
+                       PassiveMixedContentInIframeWithUpgrade) {
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
+  SetupCrossSiteRedirector(&https_server);
+  ASSERT_TRUE(https_server.Start());
+
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+
+  GURL iframe_url_with_upgrade(https_server.GetURL(
+      "/mixed-content/basic-passive-in-iframe-with-upgrade.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), iframe_url_with_upgrade));
+  NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
+  EXPECT_FALSE(!!(entry->GetSSL().content_status &
+                  SSLStatus::DISPLAYED_INSECURE_CONTENT));
+
+  FrameTreeNode* root = web_contents->GetPrimaryFrameTree().root();
+  EXPECT_EQ(blink::mojom::InsecureRequestPolicy::kUpgradeInsecureRequests,
+            root->current_replication_state().insecure_request_policy);
+  EXPECT_EQ(
+      blink::mojom::InsecureRequestPolicy::kUpgradeInsecureRequests,
+      root->child_at(0)->current_replication_state().insecure_request_policy);
+
+  // When the subframe navigates, it should still be marked as upgrading
+  // insecure requests.
+  GURL navigate_url(https_server.GetURL("/title1.html"));
+  EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), navigate_url));
+  EXPECT_EQ(blink::mojom::InsecureRequestPolicy::kUpgradeInsecureRequests,
+            root->current_replication_state().insecure_request_policy);
+  EXPECT_EQ(
+      blink::mojom::InsecureRequestPolicy::kUpgradeInsecureRequests,
+      root->child_at(0)->current_replication_state().insecure_request_policy);
+
+  // When the main frame navigates, it should no longer be marked as
+  // upgrading insecure requests.
+  EXPECT_TRUE(
+      NavigateToURL(shell(), https_server.GetURL("b.com", "/title1.html")));
+  EXPECT_EQ(blink::mojom::InsecureRequestPolicy::kLeaveInsecureRequestsAlone,
+            root->current_replication_state().insecure_request_policy);
+}
+
+// Tests that active mixed content is blocked in an OOPIF. The test
+// ignores cert errors so that an HTTPS iframe can be loaded from a site
+// other than localhost (the EmbeddedTestServer serves a certificate
+// that is valid for localhost).
+IN_PROC_BROWSER_TEST_P(SitePerProcessIgnoreCertErrorsBrowserTest,
+                       ActiveMixedContentInIframe) {
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
+  SetupCrossSiteRedirector(&https_server);
+  ASSERT_TRUE(https_server.Start());
+
+  GURL iframe_url(
+      https_server.GetURL("/mixed-content/basic-active-in-iframe.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), iframe_url));
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  ASSERT_EQ(1U, root->child_count());
+  FrameTreeNode* mixed_child = root->child_at(0)->child_at(0);
+  ASSERT_TRUE(mixed_child);
+  // The child iframe attempted to create a mixed iframe; this should
+  // have been blocked, so the mixed iframe should still be on the initial empty
+  // document.
+  EXPECT_TRUE(mixed_child->is_on_initial_empty_document());
+}
+
+// Tests that the WebContents is notified when passive mixed content is
+// displayed in an OOPIF. The test ignores cert errors so that an HTTPS
+// iframe can be loaded from a site other than localhost (the
+// EmbeddedTestServer serves a certificate that is valid for localhost).
+// This test crashes on Windows under Dr. Memory, see https://crbug.com/600942.
+#if defined(OS_WIN)
+#define MAYBE_PassiveMixedContentInIframe DISABLED_PassiveMixedContentInIframe
+#else
+#define MAYBE_PassiveMixedContentInIframe PassiveMixedContentInIframe
+#endif
+IN_PROC_BROWSER_TEST_P(
+    SitePerProcessIgnoreCertErrorsAllowMixedContentBrowserTest,
+    MAYBE_PassiveMixedContentInIframe) {
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
+  SetupCrossSiteRedirector(&https_server);
+  ASSERT_TRUE(https_server.Start());
+
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+
+  GURL iframe_url(
+      https_server.GetURL("/mixed-content/basic-passive-in-iframe.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), iframe_url));
+  NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
+  EXPECT_TRUE(!!(entry->GetSSL().content_status &
+                 SSLStatus::DISPLAYED_INSECURE_CONTENT));
+
+  // When the subframe navigates, the WebContents should still be marked
+  // as having displayed insecure content.
+  GURL navigate_url(https_server.GetURL("/title1.html"));
+  FrameTreeNode* root = web_contents->GetPrimaryFrameTree().root();
+  EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), navigate_url));
+  entry = web_contents->GetController().GetVisibleEntry();
+  EXPECT_TRUE(!!(entry->GetSSL().content_status &
+                 SSLStatus::DISPLAYED_INSECURE_CONTENT));
+
+  // When the main frame navigates, it should no longer be marked as
+  // displaying insecure content.
+  EXPECT_TRUE(
+      NavigateToURL(shell(), https_server.GetURL("b.com", "/title1.html")));
+  entry = web_contents->GetController().GetVisibleEntry();
+  EXPECT_FALSE(!!(entry->GetSSL().content_status &
+                  SSLStatus::DISPLAYED_INSECURE_CONTENT));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    SitePerProcessIgnoreCertErrorsAllowMixedContentBrowserTest,
+    testing::ValuesIn(RenderDocumentFeatureLevelValues()));
+
+}  // namespace content
diff --git a/content/browser/site_per_process_web_bundle_browsertest.cc b/content/browser/site_per_process_web_bundle_browsertest.cc
new file mode 100644
index 0000000..5b6b5f51
--- /dev/null
+++ b/content/browser/site_per_process_web_bundle_browsertest.cc
@@ -0,0 +1,208 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/site_per_process_browsertest.h"
+
+#include "content/public/test/browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/content_mock_cert_verifier.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "content/test/render_document_feature.h"
+
+namespace content {
+
+class SitePerProcessWebBundleBrowserTest
+    : public SitePerProcessIgnoreCertErrorsBrowserTest {
+ public:
+  SitePerProcessWebBundleBrowserTest() {
+    feature_list_.InitAndEnableFeature(features::kSubresourceWebBundles);
+  }
+  void SetUpOnMainThread() override {
+    SitePerProcessIgnoreCertErrorsBrowserTest::SetUpOnMainThread();
+    https_server_.ServeFilesFromSourceDirectory(GetTestDataFilePath());
+    ASSERT_TRUE(https_server_.Start());
+  }
+  net::EmbeddedTestServer* https_server() { return &https_server_; }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+  net::EmbeddedTestServer https_server_{
+      net::EmbeddedTestServer::Type::TYPE_HTTPS};
+};
+
+// Check that a uuid-in-package: subframe instantiated from a same-origin
+// WebBundle reuses its parent's process.
+IN_PROC_BROWSER_TEST_P(SitePerProcessWebBundleBrowserTest, SameSiteBundle) {
+  GURL bundle_url(
+      https_server()->GetURL("foo.test", "/web_bundle/uuid-in-package.wbn"));
+  GURL frame_url("uuid-in-package:429fcc4e-0696-4bad-b099-ee9175f023ae");
+  GURL main_url(https_server()->GetURL(
+      "foo.test", "/web_bundle/frame_parent.html?wbn=" + bundle_url.spec() +
+                      "&frame=" + frame_url.spec()));
+  std::u16string expected_title(u"OK");
+  TitleWatcher title_watcher(shell()->web_contents(), expected_title);
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  ASSERT_EQ(1U, root->child_count());
+  FrameTreeNode* child_node = root->child_at(0);
+  EXPECT_EQ(child_node->current_url(), frame_url);
+  EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
+            child_node->current_frame_host()->GetSiteInstance());
+}
+
+// Check that a uuid-in-package: subframe instantiated from a WebBundle gets a
+// process for the Bundle's origin.
+IN_PROC_BROWSER_TEST_P(SitePerProcessWebBundleBrowserTest, CrossSiteBundle) {
+  GURL bundle_url(
+      https_server()->GetURL("bar.test", "/web_bundle/uuid-in-package.wbn"));
+  GURL frame_url("uuid-in-package:429fcc4e-0696-4bad-b099-ee9175f023ae");
+  GURL main_url(https_server()->GetURL(
+      "foo.test", "/web_bundle/frame_parent.html?wbn=" + bundle_url.spec() +
+                      "&frame=" + frame_url.spec()));
+  std::u16string expected_title(u"OK");
+  TitleWatcher title_watcher(shell()->web_contents(), expected_title);
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  ASSERT_EQ(1U, root->child_count());
+  FrameTreeNode* child_node = root->child_at(0);
+  EXPECT_EQ(child_node->current_url(), frame_url);
+  url::Origin last_committed_origin =
+      child_node->current_frame_host()->GetLastCommittedOrigin();
+  EXPECT_TRUE(last_committed_origin.opaque());
+  const url::SchemeHostPort& tuple =
+      last_committed_origin.GetTupleOrPrecursorTupleIfOpaque();
+  EXPECT_EQ("bar.test", tuple.host());
+
+  // An iframe nested in the uuid-in-package: iframe gets a non-opaque origin.
+  GURL c_url = https_server()->GetURL("c.test", "/title1.html");
+  TestNavigationObserver observer(c_url);
+  observer.WatchExistingWebContents();
+
+  // Create the subframe now.
+  std::string create_frame_script = base::StringPrintf(
+      "var new_iframe = document.createElement('iframe');"
+      "new_iframe.src = '%s';"
+      "document.body.appendChild(new_iframe);",
+      c_url.spec().c_str());
+  EXPECT_TRUE(ExecJs(child_node, create_frame_script));
+
+  observer.WaitForNavigationFinished();
+  EXPECT_TRUE(observer.last_navigation_succeeded());
+
+  ASSERT_EQ(1U, child_node->child_count());
+  FrameTreeNode* grandchild_node = child_node->child_at(0);
+  url::Origin grandchild_committed_origin =
+      grandchild_node->current_frame_host()->GetLastCommittedOrigin();
+  EXPECT_FALSE(grandchild_committed_origin.opaque());
+  const url::SchemeHostPort& c_tuple =
+      grandchild_committed_origin.GetTupleOrPrecursorTupleIfOpaque();
+  EXPECT_EQ("c.test", c_tuple.host());
+  EXPECT_FALSE(
+      last_committed_origin.IsSameOriginWith(grandchild_committed_origin));
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B C\n"
+      "   +--Site B ------- proxies for A C\n"
+      "        +--Site C -- proxies for A B\n"
+      "Where A = https://foo.test/\n"
+      "      B = https://bar.test/\n"
+      "      C = https://c.test/",
+      DepictFrameTree(root));
+}
+
+// Check that a urn:uuid subframe instantiated from a same-origin WebBundle
+// reuses its parent's process.
+IN_PROC_BROWSER_TEST_P(SitePerProcessWebBundleBrowserTest,
+                       SameSiteUrnUuidBundle) {
+  GURL bundle_url(
+      https_server()->GetURL("foo.test", "/web_bundle/urn-uuid.wbn"));
+  GURL frame_url("urn:uuid:429fcc4e-0696-4bad-b099-ee9175f023ae");
+  GURL main_url(https_server()->GetURL(
+      "foo.test", "/web_bundle/frame_parent.html?wbn=" + bundle_url.spec() +
+                      "&frame=" + frame_url.spec()));
+  std::u16string expected_title(u"OK");
+  TitleWatcher title_watcher(shell()->web_contents(), expected_title);
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  ASSERT_EQ(1U, root->child_count());
+  FrameTreeNode* child_node = root->child_at(0);
+  EXPECT_EQ(child_node->current_url(), frame_url);
+  EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
+            child_node->current_frame_host()->GetSiteInstance());
+}
+
+// Check that a urn:uuid subframe instantiated from a WebBundle gets a process
+// for the Bundle's origin.
+IN_PROC_BROWSER_TEST_P(SitePerProcessWebBundleBrowserTest,
+                       CrossSiteUrnUuidBundle) {
+  GURL bundle_url(
+      https_server()->GetURL("bar.test", "/web_bundle/urn-uuid.wbn"));
+  GURL frame_url("urn:uuid:429fcc4e-0696-4bad-b099-ee9175f023ae");
+  GURL main_url(https_server()->GetURL(
+      "foo.test", "/web_bundle/frame_parent.html?wbn=" + bundle_url.spec() +
+                      "&frame=" + frame_url.spec()));
+  std::u16string expected_title(u"OK");
+  TitleWatcher title_watcher(shell()->web_contents(), expected_title);
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  ASSERT_EQ(1U, root->child_count());
+  FrameTreeNode* child_node = root->child_at(0);
+  EXPECT_EQ(child_node->current_url(), frame_url);
+  url::Origin last_committed_origin =
+      child_node->current_frame_host()->GetLastCommittedOrigin();
+  EXPECT_TRUE(last_committed_origin.opaque());
+  const url::SchemeHostPort& tuple =
+      last_committed_origin.GetTupleOrPrecursorTupleIfOpaque();
+  EXPECT_EQ("bar.test", tuple.host());
+
+  // An iframe nested in the urn:uuid iframe gets a non-opaque origin.
+  GURL c_url = https_server()->GetURL("c.test", "/title1.html");
+  TestNavigationObserver observer(c_url);
+  observer.WatchExistingWebContents();
+
+  // Create the subframe now.
+  std::string create_frame_script = base::StringPrintf(
+      "var new_iframe = document.createElement('iframe');"
+      "new_iframe.src = '%s';"
+      "document.body.appendChild(new_iframe);",
+      c_url.spec().c_str());
+  EXPECT_TRUE(ExecJs(child_node, create_frame_script));
+
+  observer.WaitForNavigationFinished();
+  EXPECT_TRUE(observer.last_navigation_succeeded());
+
+  ASSERT_EQ(1U, child_node->child_count());
+  FrameTreeNode* grandchild_node = child_node->child_at(0);
+  url::Origin grandchild_committed_origin =
+      grandchild_node->current_frame_host()->GetLastCommittedOrigin();
+  EXPECT_FALSE(grandchild_committed_origin.opaque());
+  const url::SchemeHostPort& c_tuple =
+      grandchild_committed_origin.GetTupleOrPrecursorTupleIfOpaque();
+  EXPECT_EQ("c.test", c_tuple.host());
+  EXPECT_FALSE(
+      last_committed_origin.IsSameOriginWith(grandchild_committed_origin));
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B C\n"
+      "   +--Site B ------- proxies for A C\n"
+      "        +--Site C -- proxies for A B\n"
+      "Where A = https://foo.test/\n"
+      "      B = https://bar.test/\n"
+      "      C = https://c.test/",
+      DepictFrameTree(root));
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         SitePerProcessWebBundleBrowserTest,
+                         testing::ValuesIn(RenderDocumentFeatureLevelValues()));
+
+}  // namespace content
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index c4351be..41223de 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -530,6 +530,22 @@
       native_theme_observation_{this};
 };
 
+size_t GetFrameTreeSize(FrameTree* frame_tree) {
+  size_t tree_size = 0;
+  FrameTree::NodeRange node_range = frame_tree->NodesIncludingInnerTreeNodes();
+  FrameTree::NodeIterator node_iter = node_range.begin();
+  while (node_iter != node_range.end()) {
+    // Skip over collapsed frame trees.
+    if ((*node_iter)->is_collapsed()) {
+      node_iter.AdvanceSkippingChildren();
+    } else {
+      ++tree_size;
+      ++node_iter;
+    }
+  }
+  return tree_size;
+}
+
 }  // namespace
 
 CreatedWindow::CreatedWindow() = default;
@@ -5550,8 +5566,7 @@
     // Navigation has completed in main frame. Reset |max_loaded_frame_count_|.
     // |max_loaded_frame_count_| is not necessarily 1 if the navigation was
     // served from BackForwardCache.
-    max_loaded_frame_count_ =
-        GetMainFrame()->frame_tree_node()->GetFrameTreeSize();
+    max_loaded_frame_count_ = GetFrameTreeSize(&primary_frame_tree_);
   }
 
   if (web_preferences_) {
@@ -6046,11 +6061,11 @@
     observers_.NotifyObservers(&WebContentsObserver::DidFinishLoad,
                                render_frame_host, validated_url);
   }
-  size_t tree_size = primary_frame_tree_.root()->GetFrameTreeSize();
+  size_t tree_size = GetFrameTreeSize(&primary_frame_tree_);
   if (max_loaded_frame_count_ < tree_size)
     max_loaded_frame_count_ = tree_size;
 
-  if (!render_frame_host->GetParent())
+  if (!render_frame_host->GetParentOrOuterDocument())
     UMA_HISTOGRAM_COUNTS_1000("Navigation.MainFrame.FrameCount", tree_size);
 }
 
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index d8f76595..f6889354 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -2532,6 +2532,16 @@
   NoEntryUserAgentInjector injector(shell()->web_contents(),
                                     user_agent_override);
 
+  // This tests executes two JS statements. The second statement (reload())
+  // results in a particular NavigationEntry being created. This only works
+  // if there is an IPC to the renderer, which was historically always called,
+  // but now only called if a before-unload handler is present. Force the extra
+  // IPC by making RenderFrameHost believe there is a before-unload handler.
+  static_cast<RenderFrameHostImpl*>(shell()->web_contents()->GetMainFrame())
+      ->SuddenTerminationDisablerChanged(
+          true,
+          blink::mojom::SuddenTerminationDisablerType::kBeforeUnloadHandler);
+
   // This triggers creating a NavigationRequest without a NavigationEntry. More
   // specifically back() triggers creating a pending entry, and because back()
   // does not complete, the reload() call results in a NavigationRequest with no
diff --git a/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc
index 8989b4a..7fa7a89 100644
--- a/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc
+++ b/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc
@@ -192,10 +192,9 @@
   }
 
   void OnCreatePushSubscriptionCallback(
-      video_capture::mojom::CreatePushSubscriptionResultCode result_code,
+      video_capture::mojom::CreatePushSubscriptionResultCodePtr result_code,
       const media::VideoCaptureParams& params) {
-    ASSERT_NE(video_capture::mojom::CreatePushSubscriptionResultCode::kFailed,
-              result_code);
+    ASSERT_TRUE(result_code->is_success_code());
     subscription_->Activate();
   }
 
diff --git a/content/browser/websockets/websocket_handshake_request_info_impl.cc b/content/browser/websockets/websocket_handshake_request_info_impl.cc
index 8422ba0..8b1f6cc 100644
--- a/content/browser/websockets/websocket_handshake_request_info_impl.cc
+++ b/content/browser/websockets/websocket_handshake_request_info_impl.cc
@@ -13,7 +13,7 @@
 
 constexpr int g_tag = 0;
 
-}  // namesapce
+}  // namespace
 
 WebSocketHandshakeRequestInfoImpl::WebSocketHandshakeRequestInfoImpl(
     int child_id,
diff --git a/content/services/auction_worklet/trusted_signals_request_manager.cc b/content/services/auction_worklet/trusted_signals_request_manager.cc
index e2d9d04..dac2b8d4 100644
--- a/content/services/auction_worklet/trusted_signals_request_manager.cc
+++ b/content/services/auction_worklet/trusted_signals_request_manager.cc
@@ -12,6 +12,7 @@
 #include "base/callback.h"
 #include "base/check.h"
 #include "base/containers/unique_ptr_adapters.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
@@ -194,7 +195,8 @@
   // Cancel and delete the corresponding BatchedTrustedSignalsRequest if it's
   // no longer associated with any live requests.
   if (request->batched_request_->requests.empty())
-    batched_requests_.erase(batched_requests_.find(request->batched_request_));
+    batched_requests_.erase(
+        batched_requests_.find(request->batched_request_.get()));
 }
 
 }  // namespace auction_worklet
diff --git a/content/services/auction_worklet/trusted_signals_request_manager.h b/content/services/auction_worklet/trusted_signals_request_manager.h
index eb70c1a..9faa687 100644
--- a/content/services/auction_worklet/trusted_signals_request_manager.h
+++ b/content/services/auction_worklet/trusted_signals_request_manager.h
@@ -12,6 +12,7 @@
 
 #include "base/callback.h"
 #include "base/containers/unique_ptr_adapters.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
@@ -138,11 +139,12 @@
 
     // The TrustedSignalsRequestManager that created `this`. Cleared on
     // completion.
-    TrustedSignalsRequestManager* trusted_signals_request_manager_ = nullptr;
+    raw_ptr<TrustedSignalsRequestManager> trusted_signals_request_manager_ =
+        nullptr;
 
     // If this request is currently assigned to a batched request, points to
     // that request. nullptr otherwise.
-    BatchedTrustedSignalsRequest* batched_request_ = nullptr;
+    raw_ptr<BatchedTrustedSignalsRequest> batched_request_ = nullptr;
   };
 
   // Manages a single TrustedSignals object, which is associated with one or
@@ -173,7 +175,7 @@
   void OnRequestDestroyed(RequestImpl* request);
 
   const Type type_;
-  network::mojom::URLLoaderFactory* const url_loader_factory_;
+  const raw_ptr<network::mojom::URLLoaderFactory> url_loader_factory_;
   const url::Origin top_level_origin_;
   const GURL trusted_signals_url_;
   const scoped_refptr<AuctionV8Helper> v8_helper_;
diff --git a/content/shell/browser/shell_javascript_dialog_win.cc b/content/shell/browser/shell_javascript_dialog_win.cc
index 5a2a27d..cef6ce9 100644
--- a/content/shell/browser/shell_javascript_dialog_win.cc
+++ b/content/shell/browser/shell_javascript_dialog_win.cc
@@ -107,16 +107,21 @@
   ShowWindow(dialog_win_, SW_SHOWNORMAL);
 }
 
-ShellJavaScriptDialog::~ShellJavaScriptDialog() {
-  Cancel();
-}
+ShellJavaScriptDialog::~ShellJavaScriptDialog() = default;
 
 void ShellJavaScriptDialog::Cancel() {
-  if (dialog_win_)
+  if (dialog_win_) {
+    // DestroyWindow() will delete `this` as the WM_DESTROY event handler
+    // deletes `this` through the `manager_`.
     DestroyWindow(dialog_win_);
-  dialog_win_ = 0;
-  if (callback_)
+  } else {
+    // If the window failed to be created then we emulate WM_DESTROY, since
+    // tests don't succeed in making dialogs always (e.g.
+    // BackForwardCacheBrowserTest.CanUseCacheWhenPageAlertsInTimeoutLoop).
     std::move(callback_).Run(false, std::u16string());
+    // DialogClosed() will delete `this`.
+    manager_->DialogClosed(this);
+  }
 }
 
 }  // namespace content
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 1909581..dbd6c99 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1287,9 +1287,11 @@
     "../browser/site_per_process_browsertest.cc",
     "../browser/site_per_process_browsertest.h",
     "../browser/site_per_process_hit_test_browsertest.cc",
+    "../browser/site_per_process_mixed_content_browsertest.cc",
     "../browser/site_per_process_sad_frame_browsertest.cc",
     "../browser/site_per_process_scroll_browsertest.cc",
     "../browser/site_per_process_unload_browsertest.cc",
+    "../browser/site_per_process_web_bundle_browsertest.cc",
     "../browser/sms/sms_browsertest.cc",
     "../browser/snapshot_browsertest.cc",
     "../browser/storage_partition_impl_browsertest.cc",
diff --git a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
index bdf5f59..7e0bb86 100644
--- a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
@@ -5,8 +5,8 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac bigsur catalina lion highsierra mac-10.12 mojave mountainlion
-#             sierra
+#         mac bigsur catalina lion highsierra mac-10.12 mojave monterey
+#             mountainlion sierra
 #         win win7 win8 win10 ]
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-9 android-pixel-2
@@ -143,10 +143,6 @@
 ###################
 # Non-"Skip" expectations go here to suppress regular flakes/failures.
 
-# Flakily timing out on Win x64 Debug bot. Unfortunately we can't identify this
-# separately from the 32-bit bots.
-crbug.com/628697 [ win debug ] ContextLost_WebGLContextLostFromQuantity [ Failure ]
-
 # Flaking on Nexus 5X
 crbug.com/965268 [ android android-nexus-5x ] ContextLost_WebGLBlockedAfterJSNavigation [ Failure ]
 crbug.com/965268 [ android android-nexus-5x ] ContextLost_WebGLUnblockedAfterUserInitiatedReload [ Failure ]
@@ -156,20 +152,9 @@
 crbug.com/1218559 [ android android-nexus-5x no-passthrough ] GpuCrash_GPUProcessCrashesExactlyOncePerVisitToAboutGpuCrash [ Failure ]
 crbug.com/1275180 [ android android-nexus-5x no-passthrough ] ContextLost_WebGLContextLostFromQuantity [ Failure ]
 
-# Flaky on Fuchsia.
-crbug.com/1122373 [ fuchsia ] ContextLost_WebGLContextLostFromQuantity [ Failure ]
-
 # Flakily not getting WebGL blocked on context loss
 crbug.com/1143774 [ chromeos chromeos-board-kevin ] ContextLost_WebGLUnblockedAfterUserInitiatedReload [ RetryOnFailure ]
 
-crbug.com/1254745 [ mac amd debug no-passthrough ] GpuCrash_InfoForDualHardwareGpus [ RetryOnFailure ]
-
-# Flaky tab crash on Win10 x64 Debug (NVIDIA)
-crbug.com/1238162 [ win10 nvidia-0x1cb3 debug ] GpuCrash_InfoForHardwareGpu [ RetryOnFailure ]
-
-# Flaky timeout awaiting crash Win10 FYI x64 Exp Release (Intel HD 630)
-crbug.com/1238162 [ win10 intel-0x5912 release ] GpuCrash_InfoForHardwareGpu [ RetryOnFailure ]
-
 # Flaky for LaCrOS
 crbug.com/1205899 [ linux display-server-wayland ] ContextLost_WebGLBlockedAfterJSNavigation [ RetryOnFailure ]
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/depth_capture_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/depth_capture_expectations.txt
index 21332b40..0a73872a 100644
--- a/content/test/gpu/gpu_tests/test_expectations/depth_capture_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/depth_capture_expectations.txt
@@ -5,8 +5,8 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac bigsur catalina lion highsierra mac-10.12 mojave mountainlion
-#             sierra
+#         mac bigsur catalina lion highsierra mac-10.12 mojave monterey
+#             mountainlion sierra
 #         win win7 win8 win10 ]
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-9 android-pixel-2
diff --git a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
index e1f7255c..3ab28b9 100644
--- a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
@@ -5,8 +5,8 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac bigsur catalina lion highsierra mac-10.12 mojave mountainlion
-#             sierra
+#         mac bigsur catalina lion highsierra mac-10.12 mojave monterey
+#             mountainlion sierra
 #         win win7 win8 win10 ]
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-9 android-pixel-2
@@ -111,9 +111,6 @@
 crbug.com/1099576 [ mac amd ] GpuProcess_mac_webgl_backgrounded_high_performance [ Failure ]
 crbug.com/1099576 [ mac intel ] GpuProcess_mac_webgl_backgrounded_high_performance [ Failure ]
 
-# Flakily hits a DCHECK during shared image creation.
-crbug.com/1188437 [ linux intel display-server-wayland ] GpuProcess_webgl [ RetryOnFailure ]
-
 # Flakey/slow tests using clang_rt.
 crbug.com/1261260 [ android android-nexus-5x ] GpuProcess_webgpu_iframe_removed [ Failure ]
 crbug.com/1261260 [ android android-nexus-9 ] GpuProcess_webgpu_iframe_removed [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
index 3c7e4929..46eb037 100644
--- a/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
@@ -5,8 +5,8 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac bigsur catalina lion highsierra mac-10.12 mojave mountainlion
-#             sierra
+#         mac bigsur catalina lion highsierra mac-10.12 mojave monterey
+#             mountainlion sierra
 #         win win7 win8 win10 ]
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-9 android-pixel-2
diff --git a/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
index 4e3f6bb..04397f4 100644
--- a/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
@@ -5,8 +5,8 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac bigsur catalina lion highsierra mac-10.12 mojave mountainlion
-#             sierra
+#         mac bigsur catalina lion highsierra mac-10.12 mojave monterey
+#             mountainlion sierra
 #         win win7 win8 win10 ]
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-9 android-pixel-2
diff --git a/content/test/gpu/gpu_tests/test_expectations/maps_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/maps_expectations.txt
index 3c7e4929..46eb037 100644
--- a/content/test/gpu/gpu_tests/test_expectations/maps_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/maps_expectations.txt
@@ -5,8 +5,8 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac bigsur catalina lion highsierra mac-10.12 mojave mountainlion
-#             sierra
+#         mac bigsur catalina lion highsierra mac-10.12 mojave monterey
+#             mountainlion sierra
 #         win win7 win8 win10 ]
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-9 android-pixel-2
diff --git a/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt
index 46d9875..a729369 100644
--- a/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt
@@ -5,8 +5,8 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac bigsur catalina lion highsierra mac-10.12 mojave mountainlion
-#             sierra
+#         mac bigsur catalina lion highsierra mac-10.12 mojave monterey
+#             mountainlion sierra
 #         win win7 win8 win10 ]
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-9 android-pixel-2
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 7c8036a..dd791fac 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -5,8 +5,8 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac bigsur catalina lion highsierra mac-10.12 mojave mountainlion
-#             sierra
+#         mac bigsur catalina lion highsierra mac-10.12 mojave monterey
+#             mountainlion sierra
 #         win win7 win8 win10 ]
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-9 android-pixel-2
@@ -486,11 +486,14 @@
 crbug.com/1268144 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_BackgroundImage [ Skip ]
 crbug.com/1268144 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_SolidColorBackground [ Skip ]
 
-crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_CanvasLowLatencyWebGLAlphaFalse [ Failure ]
 crbug.com/1261867 [ fuchsia fuchsia-board-astro skia-renderer-vulkan ] Pixel_WebGLGreenTriangle_AA_NoAlpha [ Failure ]
+crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_WebGLGreenTriangle_AA_NoAlpha [ Failure ]
 crbug.com/1261867 [ fuchsia fuchsia-board-astro skia-renderer-vulkan ] Pixel_CanvasLowLatencyWebGLAlphaFalse [ Failure ]
+crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_CanvasLowLatencyWebGLAlphaFalse [ Failure ]
 crbug.com/1261867 [ fuchsia fuchsia-board-astro skia-renderer-vulkan ] Pixel_WebGL2_ClearBufferfv_Result_Displayed [ Failure ]
+crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_WebGL2_ClearBufferfv_Result_Displayed [ Failure ]
 crbug.com/1261867 [ fuchsia fuchsia-board-astro skia-renderer-vulkan ] Pixel_WebGLGreenTriangle_NoAA_NoAlpha [ Failure ]
+crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_WebGLGreenTriangle_NoAA_NoAlpha [ Failure ]
 
 # Pixel_WebGPUImportVideoFrame hangs on Windows FYI bots
 crbug.com/1274796 [ win nvidia-0x2184 ] Pixel_WebGPUImportVideoFrame [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
index 20932bf..1388f60 100644
--- a/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
@@ -5,8 +5,8 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac bigsur catalina lion highsierra mac-10.12 mojave mountainlion
-#             sierra
+#         mac bigsur catalina lion highsierra mac-10.12 mojave monterey
+#             mountainlion sierra
 #         win win7 win8 win10 ]
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-9 android-pixel-2
diff --git a/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
index 0d33d18..f9768d7 100644
--- a/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
@@ -5,8 +5,8 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac bigsur catalina lion highsierra mac-10.12 mojave mountainlion
-#             sierra
+#         mac bigsur catalina lion highsierra mac-10.12 mojave monterey
+#             mountainlion sierra
 #         win win7 win8 win10 ]
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-9 android-pixel-2
diff --git a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
index bf1ffbe..db0e371 100644
--- a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
@@ -5,8 +5,8 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac bigsur catalina lion highsierra mac-10.12 mojave mountainlion
-#             sierra
+#         mac bigsur catalina lion highsierra mac-10.12 mojave monterey
+#             mountainlion sierra
 #         win win7 win8 win10 ]
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-9 android-pixel-2
diff --git a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
index 9676f1c..6d0f2fb7 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
@@ -5,8 +5,8 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac bigsur catalina lion highsierra mac-10.12 mojave mountainlion
-#             sierra
+#         mac bigsur catalina lion highsierra mac-10.12 mojave monterey
+#             mountainlion sierra
 #         win win7 win8 win10 ]
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-9 android-pixel-2
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 91bd390..81fcef4 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -5,8 +5,8 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac bigsur catalina lion highsierra mac-10.12 mojave mountainlion
-#             sierra
+#         mac bigsur catalina lion highsierra mac-10.12 mojave monterey
+#             mountainlion sierra
 #         win win7 win8 win10 ]
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-9 android-pixel-2
@@ -731,6 +731,33 @@
 crbug.com/1227762 [ bigsur amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] deqp/functional/gles3/texturespecification/texsubimage3d_pbo_3d_00.html [ Failure ]
 crbug.com/1227762 [ bigsur amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] deqp/functional/gles3/texturespecification/texsubimage3d_pbo_3d_01.html [ Failure ]
 
+crbug.com/1278909 [ monterey amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] conformance2/textures/canvas/tex-2d-rgb5_a1-rgba-unsigned_byte.html [ Failure ]
+crbug.com/1278909 [ monterey amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] conformance2/textures/canvas_sub_rectangle/tex-2d-rgb5_a1-rgba-unsigned_byte.html [ Failure ]
+crbug.com/1278909 [ monterey amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] conformance2/textures/image_bitmap_from_canvas/tex-2d-rgb5_a1-rgba-unsigned_byte.html [ Failure ]
+crbug.com/1278909 [ monterey amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] conformance2/textures/image_bitmap_from_video/tex-2d-rgb5_a1-rgba-unsigned_byte.html [ Failure ]
+crbug.com/1278909 [ monterey amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] conformance2/textures/video/tex-2d-rgb5_a1-rgba-unsigned_byte.html [ Failure ]
+crbug.com/1278909 [ monterey amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] conformance2/textures/webgl_canvas/tex-2d-rgb5_a1-rgba-unsigned_byte.html [ Failure ]
+crbug.com/1278909 [ monterey amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] deqp/functional/gles3/texturespecification/texsubimage2d_pbo_2d_00.html [ Failure ]
+crbug.com/1278909 [ monterey amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] deqp/functional/gles3/texturespecification/texsubimage2d_pbo_2d_01.html [ Failure ]
+crbug.com/1278909 [ monterey amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] deqp/functional/gles3/texturespecification/texsubimage2d_pbo_cube_01.html [ Failure ]
+crbug.com/1278909 [ monterey amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] deqp/functional/gles3/texturespecification/texsubimage2d_pbo_cube_02.html [ Failure ]
+crbug.com/1278909 [ monterey amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] deqp/functional/gles3/texturespecification/texsubimage2d_pbo_cube_03.html [ Failure ]
+crbug.com/1278909 [ monterey amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] deqp/functional/gles3/texturespecification/teximage2d_pbo_2d_00.html [ Failure ]
+crbug.com/1278909 [ monterey amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] deqp/functional/gles3/texturespecification/teximage2d_pbo_2d_01.html [ Failure ]
+
+crbug.com/1278935 [ monterey amd-0x6821 skia-renderer-gl angle-disabled ] deqp/functional/gles3/texturespecification/teximage3d_pbo_2d_array_00.html [ Failure ]
+crbug.com/1278935 [ monterey amd-0x6821 skia-renderer-gl angle-disabled ] deqp/functional/gles3/texturespecification/teximage3d_pbo_2d_array_01.html [ Failure ]
+crbug.com/1278935 [ monterey amd-0x6821 skia-renderer-gl angle-disabled ] deqp/functional/gles3/texturespecification/teximage3d_pbo_3d_00.html [ Failure ]
+crbug.com/1278935 [ monterey amd-0x6821 skia-renderer-gl angle-disabled ] deqp/functional/gles3/texturespecification/teximage3d_pbo_3d_01.html [ Failure ]
+crbug.com/1278935 [ monterey amd-0x6821 skia-renderer-gl angle-disabled ] deqp/functional/gles3/texturespecification/texsubimage3d_pbo_3d_00.html [ Failure ]
+crbug.com/1278935 [ monterey amd-0x6821 skia-renderer-gl angle-disabled ] deqp/functional/gles3/texturespecification/texsubimage3d_pbo_3d_01.html [ Failure ]
+crbug.com/1278935 [ monterey amd-0x6821 skia-renderer-gl angle-opengl ] deqp/functional/gles3/texturespecification/teximage3d_pbo_2d_array_00.html [ Failure ]
+crbug.com/1278935 [ monterey amd-0x6821 skia-renderer-gl angle-opengl ] deqp/functional/gles3/texturespecification/teximage3d_pbo_2d_array_01.html [ Failure ]
+crbug.com/1278935 [ monterey amd-0x6821 skia-renderer-gl angle-opengl ] deqp/functional/gles3/texturespecification/teximage3d_pbo_3d_00.html [ Failure ]
+crbug.com/1278935 [ monterey amd-0x6821 skia-renderer-gl angle-opengl ] deqp/functional/gles3/texturespecification/teximage3d_pbo_3d_01.html [ Failure ]
+crbug.com/1278935 [ monterey amd-0x6821 skia-renderer-gl angle-opengl ] deqp/functional/gles3/texturespecification/texsubimage3d_pbo_3d_00.html [ Failure ]
+crbug.com/1278935 [ monterey amd-0x6821 skia-renderer-gl angle-opengl ] deqp/functional/gles3/texturespecification/texsubimage3d_pbo_3d_01.html [ Failure ]
+
 # Mac Intel
 crbug.com/886970 [ mac intel-0xa2e ] conformance/rendering/canvas-alpha-bug.html [ Failure ]
 crbug.com/678526 [ mac intel ] conformance2/rendering/framebuffer-texture-level1.html [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index b436cdac..ce5f030 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -5,8 +5,8 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac bigsur catalina lion highsierra mac-10.12 mojave mountainlion
-#             sierra
+#         mac bigsur catalina lion highsierra mac-10.12 mojave monterey
+#             mountainlion sierra
 #         win win7 win8 win10 ]
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-9 android-pixel-2
@@ -422,6 +422,11 @@
 crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_byte.html [ Failure ]
 crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ Failure ]
 crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-rgb-rgb-unsigned_byte.html [ Failure ]
+crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/misc/tex-input-validation.html [ Failure ]
+crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/misc/texture-corner-case-videos.html [ Failure ]
+crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Failure ]
+crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ Failure ]
+crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Failure ]
 
 ####################
 # Win failures     #
diff --git a/content/test/gpu/validate_tag_consistency.py b/content/test/gpu/validate_tag_consistency.py
index f7b04325..398257a2 100755
--- a/content/test/gpu/validate_tag_consistency.py
+++ b/content/test/gpu/validate_tag_consistency.py
@@ -18,8 +18,8 @@
 #         chromeos
 #         fuchsia
 #         linux ubuntu
-#         mac bigsur catalina lion highsierra mac-10.12 mojave mountainlion
-#             sierra
+#         mac bigsur catalina lion highsierra mac-10.12 mojave monterey
+#             mountainlion sierra
 #         win win7 win8 win10 ]
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-9 android-pixel-2
diff --git a/docs/ui/input_event/index.md b/docs/ui/input_event/index.md
index 00254ee7..4d25ff3 100644
--- a/docs/ui/input_event/index.md
+++ b/docs/ui/input_event/index.md
@@ -423,7 +423,7 @@
 
 Chrome on Linux observes the platform native focus change events FocusIn and
 FocusOut
-[[ref](https://source.chromium.org/chromium/chromium/src/+/main:ui/base/x/x11_window.cc?q=x11::Input::CrossingEvent::FocusIn)]
+[[ref](https://source.chromium.org/chromium/chromium/src/+/main:ui/platform_window/x11/x11_window.cc?q=x11::Input::CrossingEvent::FocusIn)]
 to respond to focus change events. Nevertheless, eventually just like on
 Windows, the events will be interpreted as active window change.
 
diff --git a/extensions/browser/guest_view/web_view/web_view_apitest.h b/extensions/browser/guest_view/web_view/web_view_apitest.h
index ec4ce04..4d20a1f 100644
--- a/extensions/browser/guest_view/web_view/web_view_apitest.h
+++ b/extensions/browser/guest_view/web_view/web_view_apitest.h
@@ -15,10 +15,6 @@
 class WebContents;
 }  // namespace content
 
-namespace guestview {
-class TestGuestViewManager;
-}  // namesapce guestview
-
 namespace extensions {
 
 // Base class for WebView tests in app_shell.
diff --git a/extensions/docs/testing_api.md b/extensions/docs/testing_api.md
index df4870b2..df7e41d 100644
--- a/extensions/docs/testing_api.md
+++ b/extensions/docs/testing_api.md
@@ -328,7 +328,7 @@
 
 ```js
 chrome.test.getConfig((config) => {
-  let url = `http://example.com:${config.port}/simple.html`;
+  let url = `http://example.com:${config.testServer.port}/simple.html`;
   createTab(url);
 });
 ```
diff --git a/gpu/command_buffer/service/external_vk_image_backing.cc b/gpu/command_buffer/service/external_vk_image_backing.cc
index bc1bcc50..726e3da 100644
--- a/gpu/command_buffer/service/external_vk_image_backing.cc
+++ b/gpu/command_buffer/service/external_vk_image_backing.cc
@@ -776,8 +776,7 @@
     if (latest_content_ & kInSharedMemory) {
       if (!shared_memory_wrapper_.IsValid())
         return;
-      if (!WritePixelsWithData(shared_memory_wrapper_.GetMemoryAsSpan(),
-                               shared_memory_wrapper_.GetStride()))
+      if (!WritePixels())
         return;
       latest_content_ |=
           use_separate_gl_texture() ? kInVkImage : kInVkImage | kInGLTexture;
@@ -804,7 +803,10 @@
 
 bool ExternalVkImageBacking::WritePixelsWithCallback(
     size_t data_size,
+    size_t stride,
     WriteBufferCallback callback) {
+  DCHECK(stride == 0 || size().height() * stride <= data_size);
+
   VkBufferCreateInfo buffer_create_info = {
       .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
       .size = data_size,
@@ -856,8 +858,10 @@
           VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
       backend_texture_.setVkImageLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
     }
-    command_buffer->CopyBufferToImage(stage_buffer, image_info.fImage,  //
-                                      0, 0, /* the buffer is packed */
+    uint32_t buffer_width =
+        stride ? stride * 8 / BitsPerPixel(format()) : size().width();
+    command_buffer->CopyBufferToImage(stage_buffer, image_info.fImage,
+                                      buffer_width, size().height(),
                                       size().width(), size().height());
   }
 
@@ -979,33 +983,59 @@
 bool ExternalVkImageBacking::WritePixelsWithData(
     base::span<const uint8_t> pixel_data,
     size_t stride) {
-  size_t row_copy_size = (BitsPerPixel(format()) * size().width()) / 8;
-  if (stride == 0) {
-    stride = row_copy_size;
+  std::vector<ExternalSemaphore> external_semaphores;
+  if (!BeginAccessInternal(false /* readonly */, &external_semaphores)) {
+    DLOG(ERROR) << "BeginAccess() failed.";
+    return false;
+  }
+  auto* gr_context = context_state_->gr_context();
+  WaitSemaphoresOnGrContext(gr_context, &external_semaphores);
+
+  auto info = SkImageInfo::Make(size().width(), size().height(),
+                                ResourceFormatToClosestSkColorType(
+                                    /*gpu_compositing=*/true, format()),
+                                kOpaque_SkAlphaType);
+  SkPixmap pixmap(info, pixel_data.data(), stride);
+  if (!gr_context->updateBackendTexture(backend_texture_, &pixmap,
+                                        /*levels=*/1, nullptr, nullptr)) {
+    DLOG(ERROR) << "updateBackendTexture() failed.";
   }
 
-  DCHECK(stride >= row_copy_size);
-  DCHECK(stride <= pixel_data.size() / size().height());
+  if (!need_synchronization()) {
+    DCHECK(external_semaphores.empty());
+    EndAccessInternal(false /* readonly */, ExternalSemaphore());
+    return true;
+  }
 
-  return WritePixelsWithCallback(
-      pixel_data.size(),
-      base::BindOnce(
-          [](const ExternalVkImageBacking* backing,
-             base::span<const uint8_t> data, size_t stride,
-             size_t row_copy_size, void* buffer) {
-            uint8_t* dest = static_cast<uint8_t*>(buffer);
-            gfx::Size size = backing->size();
-            if (row_copy_size == stride) {
-              memcpy(dest, data.data(), row_copy_size * size.height());
-              return;
-            }
+  gr_context->flush({});
+  gr_context->setBackendTextureState(
+      backend_texture_,
+      GrBackendSurfaceMutableState(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+                                   VK_QUEUE_FAMILY_EXTERNAL));
 
-            for (int y = 0; y < size.height(); y++) {
-              memcpy(dest + y * row_copy_size, data.data() + y * stride,
-                     row_copy_size);
-            }
-          },
-          this, pixel_data, stride, row_copy_size));
+  auto end_access_semaphore = external_semaphore_pool()->GetOrCreateSemaphore();
+  VkSemaphore vk_end_access_semaphore = end_access_semaphore.GetVkSemaphore();
+  GrBackendSemaphore end_access_backend_semaphore;
+  end_access_backend_semaphore.initVulkan(vk_end_access_semaphore);
+  GrFlushInfo flush_info = {
+      .fNumSemaphores = 1,
+      .fSignalSemaphores = &end_access_backend_semaphore,
+  };
+  gr_context->flush(flush_info);
+
+  // Submit so the |end_access_semaphore| is ready for waiting.
+  gr_context->submit();
+
+  EndAccessInternal(false /* readonly */, std::move(end_access_semaphore));
+  // |external_semaphores| have been waited on and can be reused when submitted
+  // GPU work is done.
+  ReturnPendingSemaphoresWithFenceHelper(std::move(external_semaphores));
+  return true;
+}
+
+bool ExternalVkImageBacking::WritePixels() {
+  return WritePixelsWithData(shared_memory_wrapper_.GetMemoryAsSpan(),
+                             shared_memory_wrapper_.GetStride());
 }
 
 void ExternalVkImageBacking::CopyPixelsFromGLTextureToVkImage() {
@@ -1056,7 +1086,7 @@
   gl::ScopedPixelStore pack_alignment(GL_PACK_ALIGNMENT, 1);
 
   WritePixelsWithCallback(
-      checked_size.ValueOrDie(),
+      checked_size.ValueOrDie(), 0,
       base::BindOnce(
           [](gl::GLApi* api, const gfx::Size& size, GLenum format, GLenum type,
              void* buffer) {
diff --git a/gpu/command_buffer/service/external_vk_image_backing.h b/gpu/command_buffer/service/external_vk_image_backing.h
index a22b956..04b32ed 100644
--- a/gpu/command_buffer/service/external_vk_image_backing.h
+++ b/gpu/command_buffer/service/external_vk_image_backing.h
@@ -180,12 +180,15 @@
   using WriteBufferCallback = base::OnceCallback<void(void* buffer)>;
   // TODO(penghuang): Remove it when GrContext::updateBackendTexture() supports
   // compressed texture and callback.
-  bool WritePixelsWithCallback(size_t data_size, WriteBufferCallback callback);
+  bool WritePixelsWithCallback(size_t data_size,
+                               size_t stride,
+                               WriteBufferCallback callback);
   using ReadBufferCallback = base::OnceCallback<void(const void* buffer)>;
   bool ReadPixelsWithCallback(size_t data_size,
                               size_t stride,
                               ReadBufferCallback callback);
   bool WritePixelsWithData(base::span<const uint8_t> pixel_data, size_t stride);
+  bool WritePixels();
   void CopyPixelsFromGLTextureToVkImage();
   void CopyPixelsFromShmToGLTexture();
   void CopyPixelsFromVkImageToGLTexture();
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_angle_vulkan.cc b/gpu/command_buffer/service/shared_image_backing_factory_angle_vulkan.cc
index f853d08..83a311a 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_angle_vulkan.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_angle_vulkan.cc
@@ -368,7 +368,7 @@
                                         gpu_feature_info,
                                         context_state->progress_reporter()),
       context_state_(context_state) {
-  DCHECK(gl::GLSurfaceEGL::IsANGLEVulkanImageClientBufferSupported());
+  DCHECK(gl::GLSurfaceEGL::IsANGLEVulkanImageSupported());
 }
 
 SharedImageBackingFactoryAngleVulkan::~SharedImageBackingFactoryAngleVulkan() =
diff --git a/gpu/skia_bindings/grcontext_for_gles2_interface.cc b/gpu/skia_bindings/grcontext_for_gles2_interface.cc
index ad9ea0e..b808d21 100644
--- a/gpu/skia_bindings/grcontext_for_gles2_interface.cc
+++ b/gpu/skia_bindings/grcontext_for_gles2_interface.cc
@@ -26,7 +26,8 @@
     gpu::ContextSupport* context_support,
     const gpu::Capabilities& capabilities,
     size_t max_resource_cache_bytes,
-    size_t max_glyph_cache_texture_bytes)
+    size_t max_glyph_cache_texture_bytes,
+    bool support_bilerp_from_flyph_atlas)
     : context_support_(context_support) {
   GrContextOptions options;
   options.fGlyphCacheTextureMaximumBytes = max_glyph_cache_texture_bytes;
@@ -37,6 +38,7 @@
   // TODO(csmartdalton): enable internal multisampling after the related Skia
   // rolls are in.
   options.fInternalMultisampleCount = 0;
+  options.fSupportBilerpFromGlyphAtlas = support_bilerp_from_flyph_atlas;
   sk_sp<GrGLInterface> interface(
       skia_bindings::CreateGLES2InterfaceBindings(gl, context_support));
   gr_context_ = GrDirectContext::MakeGL(std::move(interface), options);
diff --git a/gpu/skia_bindings/grcontext_for_gles2_interface.h b/gpu/skia_bindings/grcontext_for_gles2_interface.h
index 7076b22d..083ceaea 100644
--- a/gpu/skia_bindings/grcontext_for_gles2_interface.h
+++ b/gpu/skia_bindings/grcontext_for_gles2_interface.h
@@ -24,11 +24,12 @@
 // is alive.
 class GrContextForGLES2Interface : public GrContextOptions::ShaderErrorHandler {
  public:
-  explicit GrContextForGLES2Interface(gpu::gles2::GLES2Interface* gl,
-                                      gpu::ContextSupport* context_support,
-                                      const gpu::Capabilities& capabilities,
-                                      size_t max_resource_cache_bytes,
-                                      size_t max_glyph_cache_texture_bytes);
+  GrContextForGLES2Interface(gpu::gles2::GLES2Interface* gl,
+                             gpu::ContextSupport* context_support,
+                             const gpu::Capabilities& capabilities,
+                             size_t max_resource_cache_bytes,
+                             size_t max_glyph_cache_texture_bytes,
+                             bool support_bilerp_from_flyph_atlas = false);
 
   GrContextForGLES2Interface(const GrContextForGLES2Interface&) = delete;
   GrContextForGLES2Interface& operator=(const GrContextForGLES2Interface&) =
diff --git a/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json b/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json
index 182da6a2..133a120 100644
--- a/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json
+++ b/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json
@@ -26,21 +26,21 @@
       "include": "webrtc_tests.json",
       "device type": "iPhone 11",
       "os": "14.4",
-      "host os": "Mac-10.15|Mac-11",
+      "host os": "Mac-11",
       "pool": "chromium.tests"
     },
     {
       "include": "webrtc_tests.json",
       "device type": "iPhone X",
       "os": "14.4",
-      "host os": "Mac-10.15|Mac-11",
+      "host os": "Mac-11",
       "pool": "chromium.tests"
     },
     {
       "include": "webrtc_tests.json",
       "device type": "iPad Air (3rd generation)",
       "os": "14.4",
-      "host os": "Mac-10.15|Mac-11",
+      "host os": "Mac-11",
       "pool": "chromium.tests"
     }
   ]
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index 24469c75..f239f66 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -2886,32 +2886,38 @@
     _fullscreenDisabler =
         std::make_unique<ScopedFullscreenDisabler>(self.fullscreenController);
   }
-  // Hide the tab strip and take a snapshot of it. If a snapshot of a hidden
-  // view is taken, the snapshot will be a blank view. However, if the view's
-  // parent is hidden but the view itself is not, the snapshot will not be a
-  // blank view.
-  [self.tabStripSnapshot removeFromSuperview];
-  // During initial setup, the tab strip view may be nil, but the missing
-  // snapshot will never be visible because all three animation methods are
-  // called in succession.
-  if (self.tabStripView && !base::FeatureList::IsEnabled(kModernTabStrip)) {
-    self.tabStripSnapshot = [self.tabStripView screenshotForAnimation];
-    self.tabStripSnapshot.translatesAutoresizingMaskIntoConstraints = NO;
-    self.tabStripSnapshot.transform =
-        currentViewRevealState == ViewRevealState::Hidden
-            ? [self.tabStripView
-                  adjustTransformForRTL:CGAffineTransformIdentity]
-            : [self.tabStripView
-                  adjustTransformForRTL:CGAffineTransformMakeTranslation(
-                                            0, self.tabStripView.frame.size
-                                                   .height)];
-    self.tabStripSnapshot.alpha =
-        currentViewRevealState == ViewRevealState::Revealed ||
-                currentViewRevealState == ViewRevealState::Fullscreen
-            ? 0
-            : 1;
-    [self.contentArea addSubview:self.tabStripSnapshot];
-    AddSameConstraints(self.tabStripSnapshot, self.tabStripView);
+
+  // Hide the tab strip and take a snapshot of it for better animation. However,
+  // this is not necessary to do if the thumb strip will never actually be
+  // revealed.
+  if (currentViewRevealState != ViewRevealState::Hidden ||
+      nextViewRevealState != ViewRevealState::Hidden) {
+    // If a snapshot of a hidden view is taken, the snapshot will be a blank
+    // view. However, if the view's parent is hidden but the view itself is not,
+    // the snapshot will not be a blank view.
+    [self.tabStripSnapshot removeFromSuperview];
+    // During initial setup, the tab strip view may be nil, but the missing
+    // snapshot will never be visible because all three animation methods are
+    // called in succession.
+    if (self.tabStripView && !base::FeatureList::IsEnabled(kModernTabStrip)) {
+      self.tabStripSnapshot = [self.tabStripView screenshotForAnimation];
+      self.tabStripSnapshot.translatesAutoresizingMaskIntoConstraints = NO;
+      self.tabStripSnapshot.transform =
+          currentViewRevealState == ViewRevealState::Hidden
+              ? [self.tabStripView
+                    adjustTransformForRTL:CGAffineTransformIdentity]
+              : [self.tabStripView
+                    adjustTransformForRTL:CGAffineTransformMakeTranslation(
+                                              0, self.tabStripView.frame.size
+                                                     .height)];
+      self.tabStripSnapshot.alpha =
+          currentViewRevealState == ViewRevealState::Revealed ||
+                  currentViewRevealState == ViewRevealState::Fullscreen
+              ? 0
+              : 1;
+      [self.contentArea addSubview:self.tabStripSnapshot];
+      AddSameConstraints(self.tabStripSnapshot, self.tabStripView);
+    }
   }
 
   // Remove the fake status bar to allow the thumb strip animations to appear.
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index ad30fc7..6ebb097 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-67954db99e8f9b886cc85c29b9d93bee7682217e
\ No newline at end of file
+3e17bc2e8ec0948b3c6a99e70977960b45b94579
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index c2ef827..cba87f97 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-d03b1be23d59a99b3b008217a5cc8e0a31d64fac
\ No newline at end of file
+8602d2a6ad556c4ac6b286aa4133e4dfb6393d7e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 4577932..546de63 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-ebce3952c81d8fbb1e147e07facb13c9da30c1c5
\ No newline at end of file
+6d42e1e7f8840a6dcb1340fa86953ea3fa97e9ca
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 7ebea87..170639e 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-651f2db4616d149fc84e79485634f6a972d4775b
\ No newline at end of file
+fdaa14f6986163123c9340e7531ec1c2b0b6ae1e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index a4573770..b7d6d33 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-95818481490bce2f520af7f43ee118cb24182305
\ No newline at end of file
+1d36bd4741489e0ea6b1e9fb6654bcd5c76f218e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index f239f2e..b903ec58 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-8fcb3f7f24cb7f3476fa156502ba9ca15a66866a
\ No newline at end of file
+a5a8302d482ac087f9cffba8948c0d43aa5d6167
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 11e6c8ca..04be327 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-321fd382a83f8f671eefe3c90952ef407c0f0284
\ No newline at end of file
+8cd8df1da4858b3c00efdc9304e754e1a315ae9b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 179c71b..75749d51 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-fae1b380608b45729060670e5b9a10d78658d2c2
\ No newline at end of file
+05da2d91340fee41f515a57dbb7a3f9a623cfbfa
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 3b45b768..ff1434b5 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-2ed371c80cd73faddccbf7094b711275443cdf4f
\ No newline at end of file
+b6fbb31766680af41c8b91a0b6d01d86409efc0f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 9b87666f..946c756a 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-5188c53e26e8021a566b1ba5eaafd50f07ab8165
\ No newline at end of file
+db5aa98d17715ca1b7b19843250b477dee920cde
\ No newline at end of file
diff --git a/ios/third_party/material_components_ios/BUILD.gn b/ios/third_party/material_components_ios/BUILD.gn
index 17c2164..127de03 100644
--- a/ios/third_party/material_components_ios/BUILD.gn
+++ b/ios/third_party/material_components_ios/BUILD.gn
@@ -54,6 +54,7 @@
   "src/components/Availability/src/MDCAvailability.h",
   "src/components/Availability/src/MaterialAvailability.h",
   "src/components/Badges/src/MDCBadgeView.h",
+  "src/components/Badges/src/MDCDotBadgeView.h",
   "src/components/Banner/src/MDCBannerView.h",
   "src/components/Banner/src/MaterialBanner.h",
   "src/components/Banner/src/Theming/MDCBannerView+MaterialTheming.h",
@@ -781,6 +782,8 @@
   "src/components/Availability/src/MaterialAvailability.h",
   "src/components/Badges/src/MDCBadgeView.h",
   "src/components/Badges/src/MDCBadgeView.m",
+  "src/components/Badges/src/MDCDotBadgeView.h",
+  "src/components/Badges/src/MDCDotBadgeView.m",
   "src/components/Banner/src/MDCBannerView.h",
   "src/components/Banner/src/MDCBannerView.m",
   "src/components/Banner/src/MaterialBanner.h",
diff --git a/ios/web/favicon/favicon_java_script_feature.mm b/ios/web/favicon/favicon_java_script_feature.mm
index b51e07a..79987362 100644
--- a/ios/web/favicon/favicon_java_script_feature.mm
+++ b/ios/web/favicon/favicon_java_script_feature.mm
@@ -73,4 +73,4 @@
     static_cast<WebStateImpl*>(web_state)->OnFaviconUrlUpdated(urls);
 }
 
-}  // namspace web
+}  // namespace web
diff --git a/ios/web/public/test/http_server/http_server.h b/ios/web/public/test/http_server/http_server.h
index 2f91e93d..36c6d22 100644
--- a/ios/web/public/test/http_server/http_server.h
+++ b/ios/web/public/test/http_server/http_server.h
@@ -148,6 +148,6 @@
 };
 
 }  // namespace test
-}  // namspace web
+}  // namespace web
 
 #endif  // IOS_WEB_PUBLIC_TEST_HTTP_SERVER_HTTP_SERVER_H_
diff --git a/ios/web/public/test/http_server/response_provider.h b/ios/web/public/test/http_server/response_provider.h
index 7ec62cbe..c8a9a20 100644
--- a/ios/web/public/test/http_server/response_provider.h
+++ b/ios/web/public/test/http_server/response_provider.h
@@ -81,6 +81,6 @@
   virtual ~ResponseProvider() {}
 };
 
-}  // namspace web
+}  // namespace web
 
 #endif  // IOS_WEB_PUBLIC_TEST_HTTP_SERVER_RESPONSE_PROVIDER_H_
diff --git a/media/base/bitstream_buffer.cc b/media/base/bitstream_buffer.cc
index eeafe67..97cb6be 100644
--- a/media/base/bitstream_buffer.cc
+++ b/media/base/bitstream_buffer.cc
@@ -4,6 +4,7 @@
 
 #include "media/base/bitstream_buffer.h"
 
+#include "base/numerics/checked_math.h"
 #include "media/base/decrypt_config.h"
 
 namespace media {
@@ -41,8 +42,25 @@
 BitstreamBuffer::~BitstreamBuffer() = default;
 
 scoped_refptr<DecoderBuffer> BitstreamBuffer::ToDecoderBuffer() {
-  scoped_refptr<DecoderBuffer> buffer =
-      DecoderBuffer::FromSharedMemoryRegion(std::move(region_), offset_, size_);
+  return ToDecoderBuffer(0, size_);
+}
+scoped_refptr<DecoderBuffer> BitstreamBuffer::ToDecoderBuffer(off_t offset,
+                                                              size_t size) {
+  // We do allow mapping beyond the bounds of the original specified size in
+  // order to deal with the OEMCrypto secure buffer format, but we do ensure
+  // it stays within the shared memory region.
+  base::CheckedNumeric<off_t> total_range(offset_);
+  total_range += offset;
+  if (!total_range.IsValid())
+    return nullptr;
+  const off_t final_offset = total_range.ValueOrDie();
+  total_range += base::checked_cast<off_t>(size);
+  if (!total_range.IsValid<size_t>())
+    return nullptr;
+  if (total_range.ValueOrDie<size_t>() > region_.GetSize())
+    return nullptr;
+  scoped_refptr<DecoderBuffer> buffer = DecoderBuffer::FromSharedMemoryRegion(
+      std::move(region_), final_offset, size);
   if (!buffer)
     return nullptr;
   buffer->set_timestamp(presentation_timestamp_);
diff --git a/media/base/bitstream_buffer.h b/media/base/bitstream_buffer.h
index 11787d6..dfff2d3 100644
--- a/media/base/bitstream_buffer.h
+++ b/media/base/bitstream_buffer.h
@@ -58,14 +58,20 @@
 
   ~BitstreamBuffer();
 
-  // Produce an equivalent DecoderBuffer. This consumes region(), even if
+  // Produce an equivalent DecoderBuffer. This may consume region(), even if
   // nullptr is returned.
   //
   // This method is only intended to be used by VDAs that are being converted to
   // use DecoderBuffer.
   //
+  // The 2 arg variant adds |offset| to the internal offset and will then use
+  // |size| bytes at that location. This is allowed to go beyond the size of the
+  // buffer specified in the constructor, but it does ensure it does not go
+  // beyond the size of the shared memory region.
+  //
   // TODO(sandersd): Remove once all VDAs are converted.
   scoped_refptr<DecoderBuffer> ToDecoderBuffer();
+  scoped_refptr<DecoderBuffer> ToDecoderBuffer(off_t offset, size_t size);
 
   // TODO(crbug.com/813845): As this is only used by Android, include
   // EncryptionScheme and optional EncryptionPattern when updating for Android.
diff --git a/media/gpu/chromeos/BUILD.gn b/media/gpu/chromeos/BUILD.gn
index a5c8945b..7cb4c3e 100644
--- a/media/gpu/chromeos/BUILD.gn
+++ b/media/gpu/chromeos/BUILD.gn
@@ -5,6 +5,7 @@
 import("//build/config/chromeos/ui_mode.gni")
 import("//media/gpu/args.gni")
 import("//testing/test.gni")
+import("//third_party/protobuf/proto_library.gni")
 
 assert(use_v4l2_codec || use_vaapi)
 
@@ -102,8 +103,10 @@
       "decoder_buffer_transcryptor.cc",
       "decoder_buffer_transcryptor.h",
     ]
-    deps +=
-        [ "//chromeos/components/cdm_factory_daemon:cdm_factory_daemon_gpu" ]
+    deps += [
+      ":cdm-oemcrypto-proto",
+      "//chromeos/components/cdm_factory_daemon:cdm_factory_daemon_gpu",
+    ]
   }
 }
 
@@ -142,6 +145,15 @@
   ]
 }
 
+if (is_chromeos_ash) {
+  proto_library("cdm-oemcrypto-proto") {
+    sources = [
+      "//third_party/cros_system_api/dbus/cdm_oemcrypto/secure_buffer.proto",
+    ]
+    proto_out_dir = "media/gpu/chromeos"
+  }
+}
+
 source_set("unit_tests") {
   testonly = true
   deps = [
diff --git a/media/gpu/chromeos/DEPS b/media/gpu/chromeos/DEPS
index 8a68810..a22f2ffd 100644
--- a/media/gpu/chromeos/DEPS
+++ b/media/gpu/chromeos/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+chromeos/components/cdm_factory_daemon",
+  "+third_party/cros_system_api/constants/cdm_oemcrypto.h",
 ]
\ No newline at end of file
diff --git a/media/gpu/chromeos/vd_video_decode_accelerator.cc b/media/gpu/chromeos/vd_video_decode_accelerator.cc
index da3a79e..7799dffb 100644
--- a/media/gpu/chromeos/vd_video_decode_accelerator.cc
+++ b/media/gpu/chromeos/vd_video_decode_accelerator.cc
@@ -23,10 +23,20 @@
 #include "media/gpu/buffer_validation.h"
 #include "media/gpu/chromeos/gpu_buffer_layout.h"
 #include "media/gpu/macros.h"
+#include "media/media_buildflags.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 #include "ui/gl/gl_bindings.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+// gn check does not account for BUILDFLAG(), so including these headers will
+// make gn check fail for builds other than ash-chrome. See gn help nogncheck
+// for more information.
+#include "chromeos/components/cdm_factory_daemon/chromeos_cdm_factory.h"  // nogncheck
+#include "media/gpu/chromeos/secure_buffer.pb.h"                  // nogncheck
+#include "third_party/cros_system_api/constants/cdm_oemcrypto.h"  // nogncheck
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 namespace media {
 namespace {
 
@@ -66,6 +76,98 @@
   return result.str();
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+scoped_refptr<DecoderBuffer> DecryptBitstreamBuffer(
+    BitstreamBuffer bitstream_buffer) {
+  // Check to see if we have our secure buffer tag and then extract the
+  // decrypt parameters.
+  auto mem_region = base::UnsafeSharedMemoryRegion::Deserialize(
+      bitstream_buffer.DuplicateRegion());
+  if (!mem_region.IsValid()) {
+    DVLOG(2) << "Invalid shared memory region";
+    return nullptr;
+  }
+  const size_t available_size =
+      mem_region.GetSize() -
+      base::checked_cast<size_t>(bitstream_buffer.offset());
+  auto mapping = mem_region.Map();
+  if (!mapping.IsValid()) {
+    DVLOG(2) << "Failed mapping shared memory";
+    return nullptr;
+  }
+  // Checks if this buffer contains the details needed for HW protected video
+  // decoding.
+  // The header is 1KB in size (cdm_oemcrypto::kSecureBufferHeaderSize).
+  // It consists of 3 components.
+  // 1. Marker tag - cdm_oemcrypto::kSecureBufferTag
+  // 2. unsigned 32-bit size of #3
+  // 3. Serialized ArcSecureBufferForChrome proto
+  uint8_t* data = mapping.GetMemoryAs<uint8_t>();
+  if (!data) {
+    DVLOG(2) << "Failed accessing shared memory";
+    return nullptr;
+  }
+  // Apply the offset here so we don't need to worry about page alignment in the
+  // mapping.
+  data += bitstream_buffer.offset();
+  if (available_size <= cdm_oemcrypto::kSecureBufferHeaderSize ||
+      memcmp(data, cdm_oemcrypto::kSecureBufferTag,
+             cdm_oemcrypto::kSecureBufferTagLen)) {
+    return nullptr;
+  }
+  VLOG(2) << "Detected secure buffer format in VDVDA";
+  // Read the protobuf size.
+  uint32_t proto_size = 0;
+  memcpy(&proto_size, data + cdm_oemcrypto::kSecureBufferTagLen,
+         sizeof(uint32_t));
+  if (proto_size > cdm_oemcrypto::kSecureBufferHeaderSize -
+                       cdm_oemcrypto::kSecureBufferProtoOffset) {
+    DVLOG(2) << "Proto size goes beyond header size";
+    return nullptr;
+  }
+  // Read the serialized proto.
+  std::string serialized_proto(
+      data + cdm_oemcrypto::kSecureBufferProtoOffset,
+      data + cdm_oemcrypto::kSecureBufferProtoOffset + proto_size);
+  chromeos::cdm::ArcSecureBufferForChrome buffer_proto;
+  if (!buffer_proto.ParseFromString(serialized_proto)) {
+    DVLOG(2) << "Failed deserializing secure buffer proto";
+    return nullptr;
+  }
+
+  // Now extract the DecryptConfig info from the protobuf.
+  std::vector<media::SubsampleEntry> subsamples;
+  size_t buffer_size = 0;
+  for (const auto& subsample : buffer_proto.subsample()) {
+    buffer_size += subsample.clear_bytes() + subsample.cypher_bytes();
+    subsamples.emplace_back(subsample.clear_bytes(), subsample.cypher_bytes());
+  }
+  absl::optional<EncryptionPattern> pattern = absl::nullopt;
+  if (buffer_proto.has_pattern()) {
+    pattern.emplace(buffer_proto.pattern().cypher_bytes(),
+                    buffer_proto.pattern().clear_bytes());
+  }
+  // Now create the DecryptConfig and set it in the decoder buffer.
+  scoped_refptr<DecoderBuffer> buffer = bitstream_buffer.ToDecoderBuffer(
+      cdm_oemcrypto::kSecureBufferHeaderSize, buffer_size);
+  if (!buffer) {
+    DVLOG(2) << "Secure buffer data goes beyond shared memory size";
+    return nullptr;
+  }
+  if (buffer_proto.encryption_scheme() !=
+      chromeos::cdm::ArcSecureBufferForChrome::NONE) {
+    buffer->set_decrypt_config(std::make_unique<DecryptConfig>(
+        buffer_proto.encryption_scheme() ==
+                chromeos::cdm::ArcSecureBufferForChrome::CBCS
+            ? EncryptionScheme::kCbcs
+            : EncryptionScheme::kCenc,
+        buffer_proto.key_id(), buffer_proto.iv(), std::move(subsamples),
+        std::move(pattern)));
+  }
+  return buffer;
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 }  // namespace
 
 // static
@@ -118,12 +220,13 @@
                                           Client* client) {
   VLOGF(2) << "config: " << config.AsHumanReadableString();
   DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
-  DCHECK(!client_);
 
+#if !BUILDFLAG(USE_ARC_PROTECTED_MEDIA)
   if (config.is_encrypted()) {
     VLOGF(1) << "Encrypted streams are not supported";
     return false;
   }
+#endif  //  !BUILDFLAG(USE_ARC_PROTECTED_MEDIA)
   if (config.output_mode != Config::OutputMode::IMPORT) {
     VLOGF(1) << "Only IMPORT OutputMode is supported.";
     return false;
@@ -133,30 +236,37 @@
     return false;
   }
 
-  std::unique_ptr<VdaVideoFramePool> frame_pool =
-      std::make_unique<VdaVideoFramePool>(weak_this_, client_task_runner_);
-  vd_ = create_vd_cb_.Run(client_task_runner_, std::move(frame_pool),
-                          std::make_unique<VideoFrameConverter>(),
-                          std::make_unique<NullMediaLog>());
-  if (!vd_)
-    return false;
+  // In case we are re-initializing for encrypted content.
+  if (!vd_) {
+    std::unique_ptr<VdaVideoFramePool> frame_pool =
+        std::make_unique<VdaVideoFramePool>(weak_this_, client_task_runner_);
+    vd_ = create_vd_cb_.Run(client_task_runner_, std::move(frame_pool),
+                            std::make_unique<VideoFrameConverter>(),
+                            std::make_unique<NullMediaLog>());
+    if (!vd_)
+      return false;
 
-  client_ = client;
-
+    client_ = client;
+  }
+  media::CdmContext* cdm_context = nullptr;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  is_encrypted_ = config.is_encrypted();
+  if (is_encrypted_)
+    cdm_context = chromeos::ChromeOsCdmFactory::GetArcCdmContext();
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
   VideoDecoderConfig vd_config(
       VideoCodecProfileToVideoCodec(config.profile), config.profile,
       VideoDecoderConfig::AlphaMode::kIsOpaque, config.container_color_space,
       VideoTransformation(), config.initial_expected_coded_size,
       gfx::Rect(config.initial_expected_coded_size),
       config.initial_expected_coded_size, std::vector<uint8_t>(),
-      EncryptionScheme::kUnencrypted);
+      config.encryption_scheme);
   auto init_cb =
       base::BindOnce(&VdVideoDecodeAccelerator::OnInitializeDone, weak_this_);
   auto output_cb =
       base::BindRepeating(&VdVideoDecodeAccelerator::OnFrameReady, weak_this_);
-  vd_->Initialize(std::move(vd_config), false /* low_delay */,
-                  nullptr /* cdm_context */, std::move(init_cb),
-                  std::move(output_cb), WaitingCB());
+  vd_->Initialize(std::move(vd_config), false /* low_delay */, cdm_context,
+                  std::move(init_cb), std::move(output_cb), WaitingCB());
   return true;
 }
 
@@ -169,7 +279,21 @@
 }
 
 void VdVideoDecodeAccelerator::Decode(BitstreamBuffer bitstream_buffer) {
-  Decode(bitstream_buffer.ToDecoderBuffer(), bitstream_buffer.id());
+  const int32_t bitstream_id = bitstream_buffer.id();
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  if (is_encrypted_) {
+    scoped_refptr<DecoderBuffer> buffer =
+        DecryptBitstreamBuffer(std::move(bitstream_buffer));
+    // This happens in the error case.
+    if (!buffer) {
+      OnError(FROM_HERE, PLATFORM_FAILURE);
+      return;
+    }
+    Decode(std::move(buffer), bitstream_id);
+    return;
+  }
+#endif  // BUILFLAG(IS_CHROMEOS_ASH)
+  Decode(bitstream_buffer.ToDecoderBuffer(), bitstream_id);
 }
 
 void VdVideoDecodeAccelerator::Decode(scoped_refptr<DecoderBuffer> buffer,
diff --git a/media/gpu/chromeos/vd_video_decode_accelerator.h b/media/gpu/chromeos/vd_video_decode_accelerator.h
index f0cd41c..7aa2470a 100644
--- a/media/gpu/chromeos/vd_video_decode_accelerator.h
+++ b/media/gpu/chromeos/vd_video_decode_accelerator.h
@@ -14,6 +14,7 @@
 #include "base/sequence_checker.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/thread_annotations.h"
+#include "build/chromeos_buildflags.h"
 #include "media/base/status.h"
 #include "media/base/video_decoder.h"
 #include "media/gpu/chromeos/dmabuf_video_frame_pool.h"
@@ -147,6 +148,12 @@
            std::pair<scoped_refptr<VideoFrame>, size_t /* num_sent */>>
       picture_at_client_;
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // Indicates we are handling encrypted content which requires an extra check
+  // to see if it is a secure buffer format.
+  bool is_encrypted_ = false;
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
   // Main task runner and its sequence checker. All methods should be called
   // on it.
   scoped_refptr<base::SequencedTaskRunner> client_task_runner_;
diff --git a/media/gpu/chromeos/vda_video_frame_pool.cc b/media/gpu/chromeos/vda_video_frame_pool.cc
index 67b11cd..ff94f6c 100644
--- a/media/gpu/chromeos/vda_video_frame_pool.cc
+++ b/media/gpu/chromeos/vda_video_frame_pool.cc
@@ -9,6 +9,7 @@
 #include "base/synchronization/waitable_event.h"
 #include "media/gpu/chromeos/gpu_buffer_layout.h"
 #include "media/gpu/macros.h"
+#include "media/media_buildflags.h"
 
 namespace media {
 
@@ -39,10 +40,12 @@
   DVLOGF(3);
   DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);
 
+#if !BUILDFLAG(USE_ARC_PROTECTED_MEDIA)
   if (use_protected) {
     LOG(ERROR) << "Cannot allocated protected buffers for VDA";
     return CroStatus::Codes::kProtectedContentUnsupported;
   }
+#endif  // !BUILDFLAG(USE_ARC_PROTECTED_MEDIA)
 
   visible_rect_ = visible_rect;
   natural_size_ = natural_size;
diff --git a/media/gpu/vaapi/vaapi_video_decoder.cc b/media/gpu/vaapi/vaapi_video_decoder.cc
index 9752aa6..f6ddc829 100644
--- a/media/gpu/vaapi/vaapi_video_decoder.cc
+++ b/media/gpu/vaapi/vaapi_video_decoder.cc
@@ -952,6 +952,17 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(state_ == State::kWaitingForInput);
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // We do not need to invoke transcryption if this is coming from ARC since
+  // that will already be done.
+  if (cdm_context_ref_ &&
+      cdm_context_ref_->GetCdmContext()->GetChromeOsCdmContext() &&
+      cdm_context_ref_->GetCdmContext()
+          ->GetChromeOsCdmContext()
+          ->UsingArcCdm()) {
+    return false;
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
   return transcryption_;
 }
 
diff --git a/media/remoting/demuxer_stream_adapter.cc b/media/remoting/demuxer_stream_adapter.cc
index 0729d34..566e8f2 100644
--- a/media/remoting/demuxer_stream_adapter.cc
+++ b/media/remoting/demuxer_stream_adapter.cc
@@ -63,7 +63,7 @@
       base::BindOnce(
           &RpcMessenger::RegisterMessageReceiverCallback, rpc_messenger_,
           rpc_handle_,
-          [cb = receive_callback](
+          [cb = std::move(receive_callback)](
               std::unique_ptr<openscreen::cast::RpcMessage> message) {
             cb.Run(std::move(message));
           }));
diff --git a/media/remoting/demuxer_stream_adapter_unittest.cc b/media/remoting/demuxer_stream_adapter_unittest.cc
index 17218b3..ed85241 100644
--- a/media/remoting/demuxer_stream_adapter_unittest.cc
+++ b/media/remoting/demuxer_stream_adapter_unittest.cc
@@ -314,5 +314,5 @@
   EXPECT_EQ(MOJO_PIPE_ERROR, errors[0]);
 }
 
-}  // namesapce remoting
+}  // namespace remoting
 }  // namespace media
diff --git a/media/remoting/receiver.cc b/media/remoting/receiver.cc
index df53b0a..73e0ed1b9b 100644
--- a/media/remoting/receiver.cc
+++ b/media/remoting/receiver.cc
@@ -61,7 +61,7 @@
 
   // Listening all renderer rpc messages.
   rpc_messenger_->RegisterMessageReceiverCallback(
-      rpc_handle_, [cb = receive_callback](
+      rpc_handle_, [cb = std::move(receive_callback)](
                        std::unique_ptr<openscreen::cast::RpcMessage> message) {
         cb.Run(std::move(message));
       });
diff --git a/media/remoting/remoting_renderer_factory.cc b/media/remoting/remoting_renderer_factory.cc
index 334b80c..54979b12 100644
--- a/media/remoting/remoting_renderer_factory.cc
+++ b/media/remoting/remoting_renderer_factory.cc
@@ -4,6 +4,7 @@
 
 #include "media/remoting/remoting_renderer_factory.h"
 
+#include "base/task/bind_post_task.h"
 #include "media/base/demuxer.h"
 #include "media/remoting/receiver.h"
 #include "media/remoting/receiver_controller.h"
@@ -28,13 +29,15 @@
   DCHECK(receiver_controller_);
 
   // Register the callback to listen RPC_ACQUIRE_RENDERER message.
+  auto receive_callback = base::BindPostTask(
+      media_task_runner,
+      BindRepeating(&RemotingRendererFactory::OnAcquireRenderer,
+                    weak_factory_.GetWeakPtr()));
   rpc_messenger_->RegisterMessageReceiverCallback(
       RpcMessenger::kAcquireRendererHandle,
-      [ptr = weak_factory_.GetWeakPtr()](
+      [cb = std::move(receive_callback)](
           std::unique_ptr<openscreen::cast::RpcMessage> message) {
-        if (ptr) {
-          ptr->OnAcquireRenderer(std::move(message));
-        }
+        cb.Run(std::move(message));
       });
   receiver_controller_->Initialize(std::move(remotee));
 }
diff --git a/media/remoting/stream_provider.cc b/media/remoting/stream_provider.cc
index 9df45502..9b6496e 100644
--- a/media/remoting/stream_provider.cc
+++ b/media/remoting/stream_provider.cc
@@ -69,9 +69,9 @@
       media_task_runner_,
       BindRepeating(&MediaStream::OnReceivedRpc, media_weak_this_));
   rpc_messenger_->RegisterMessageReceiverCallback(
-      rpc_handle_, [receive_callback](
+      rpc_handle_, [cb = std::move(receive_callback)](
                        std::unique_ptr<openscreen::cast::RpcMessage> message) {
-        receive_callback.Run(std::move(message));
+        cb.Run(std::move(message));
       });
 }
 
@@ -441,8 +441,9 @@
       base::BindRepeating(&StreamProvider::OnReceivedRpc, media_weak_this_));
   rpc_messenger_->RegisterMessageReceiverCallback(
       RpcMessenger::kAcquireDemuxerHandle,
-      [callback](std::unique_ptr<openscreen::cast::RpcMessage> message) {
-        callback.Run(std::move(message));
+      [cb = std::move(callback)](
+          std::unique_ptr<openscreen::cast::RpcMessage> message) {
+        cb.Run(std::move(message));
       });
 }
 
diff --git a/media/video/openh264_video_encoder.cc b/media/video/openh264_video_encoder.cc
index bb1218e3..dbba576 100644
--- a/media/video/openh264_video_encoder.cc
+++ b/media/video/openh264_video_encoder.cc
@@ -382,6 +382,7 @@
     h264_converter_ = std::make_unique<H264AnnexBToAvcBitstreamConverter>();
   }
 
+  options_ = options;
   if (!output_cb.is_null())
     output_cb_ = BindToCurrentLoop(std::move(output_cb));
   std::move(done_cb).Run(OkStatus());
diff --git a/media/video/software_video_encoder_test.cc b/media/video/software_video_encoder_test.cc
index 6e2023a..8e4ed08 100644
--- a/media/video/software_video_encoder_test.cc
+++ b/media/video/software_video_encoder_test.cc
@@ -515,6 +515,107 @@
     }
   }
 }
+
+TEST_P(H264VideoEncoderTest, ReconfigureWithResize) {
+  VideoEncoder::Options options;
+  gfx::Size size1(320, 200), size2(400, 240);
+  options.frame_size = size1;
+  options.bitrate = Bitrate::ConstantBitrate(1e6);  // 1Mbps
+  options.framerate = 25;
+  if (codec_ == VideoCodec::kH264)
+    options.avc.produce_annexb = true;
+  struct ChunkWithConfig {
+    VideoEncoderOutput output;
+    gfx::Size size;
+  };
+  std::vector<scoped_refptr<VideoFrame>> frames_to_encode;
+  std::vector<scoped_refptr<VideoFrame>> decoded_frames;
+  std::vector<ChunkWithConfig> chunks;
+  size_t total_frames_count = 8;
+  auto frame_duration = base::Seconds(1.0 / options.framerate.value());
+
+  VideoEncoder::OutputCB encoder_output_cb = base::BindLambdaForTesting(
+      [&](VideoEncoderOutput output,
+          absl::optional<VideoEncoder::CodecDescription> desc) {
+        chunks.push_back({std::move(output), options.frame_size});
+      });
+
+  encoder_->Initialize(profile_, options, std::move(encoder_output_cb),
+                       ValidatingStatusCB());
+  RunUntilIdle();
+
+  uint32_t color = 0x0080FF;
+  for (auto frame_index = 0u; frame_index < total_frames_count; frame_index++) {
+    const auto timestamp = frame_index * frame_duration;
+    const bool reconfigure = (frame_index == total_frames_count / 2);
+
+    if (reconfigure) {
+      encoder_->Flush(ValidatingStatusCB());
+      RunUntilIdle();
+
+      // Ask encoder to change encoded resolution, empty output callback
+      // means the encoder should keep the old one.
+      options.frame_size = size2;
+      encoder_->ChangeOptions(options, VideoEncoder::OutputCB(),
+                              ValidatingStatusCB());
+      RunUntilIdle();
+    }
+
+    auto frame =
+        CreateFrame(options.frame_size, pixel_format_, timestamp, color);
+    frames_to_encode.push_back(frame);
+    encoder_->Encode(frame, false, ValidatingStatusCB());
+    RunUntilIdle();
+  }
+  encoder_->Flush(ValidatingStatusCB());
+  RunUntilIdle();
+
+  EXPECT_EQ(chunks.size(), total_frames_count);
+  gfx::Size current_size;
+  for (auto& chunk : chunks) {
+    VideoDecoder::OutputCB decoder_output_cb =
+        base::BindLambdaForTesting([&](scoped_refptr<VideoFrame> frame) {
+          decoded_frames.push_back(frame);
+        });
+
+    if (chunk.size != current_size) {
+      if (decoder_)
+        DecodeAndWaitForStatus(DecoderBuffer::CreateEOSBuffer());
+      PrepareDecoder(chunk.size, std::move(decoder_output_cb));
+      current_size = chunk.size;
+    }
+    auto& output = chunk.output;
+    auto buffer = DecoderBuffer::FromArray(std::move(output.data), output.size);
+    buffer->set_timestamp(output.timestamp);
+    buffer->set_is_key_frame(output.key_frame);
+    DecodeAndWaitForStatus(std::move(buffer));
+  }
+  DecodeAndWaitForStatus(DecoderBuffer::CreateEOSBuffer());
+
+  EXPECT_EQ(decoded_frames.size(), frames_to_encode.size());
+  std::vector<uint8_t> conversion_buffer;
+  for (auto i = 0u; i < decoded_frames.size(); i++) {
+    auto original_frame = frames_to_encode[i];
+    auto decoded_frame = decoded_frames[i];
+    EXPECT_EQ(decoded_frame->timestamp(), original_frame->timestamp());
+    EXPECT_EQ(decoded_frame->visible_rect(), original_frame->visible_rect());
+    if (decoded_frame->format() != original_frame->format()) {
+      // The frame was converted from RGB to YUV, we can't easily compare to
+      // the original frame, so we're going to compare with a new white frame.
+      EXPECT_EQ(decoded_frame->format(), PIXEL_FORMAT_I420);
+      auto size = decoded_frame->visible_rect().size();
+      auto i420_frame =
+          VideoFrame::CreateFrame(PIXEL_FORMAT_I420, size, gfx::Rect(size),
+                                  size, decoded_frame->timestamp());
+      EXPECT_TRUE(
+          ConvertAndScaleFrame(*original_frame, *i420_frame, conversion_buffer)
+              .is_ok());
+      original_frame = i420_frame;
+    }
+    EXPECT_LE(CountDifferentPixels(*decoded_frame, *original_frame),
+              original_frame->visible_rect().width());
+  }
+}
 #endif  // ENABLE_FFMPEG_VIDEO_DECODERS
 
 TEST_P(H264VideoEncoderTest, AvcExtraData) {
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index ef046e9..3d972b95 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -1012,3 +1012,6 @@
 // Error should be handled as if an HTTP redirect was received to redirect to
 // https or wss.
 NET_ERROR(DNS_NAME_HTTPS_ONLY, -809)
+
+// All DNS requests associated with this job have been cancelled.
+NET_ERROR(DNS_REQUEST_CANCELLED, -810)
diff --git a/net/dns/dns_client.cc b/net/dns/dns_client.cc
index 3baa53f..bb846c1 100644
--- a/net/dns/dns_client.cc
+++ b/net/dns/dns_client.cc
@@ -4,13 +4,17 @@
 
 #include "net/dns/dns_client.h"
 
+#include <memory>
+#include <string>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/rand_util.h"
+#include "base/ranges/algorithm.h"
 #include "base/values.h"
+#include "net/base/address_list.h"
 #include "net/dns/address_sorter.h"
 #include "net/dns/dns_session.h"
 #include "net/dns/dns_socket_allocator.h"
@@ -21,6 +25,9 @@
 #include "net/log/net_log.h"
 #include "net/log/net_log_event_type.h"
 #include "net/socket/client_socket_factory.h"
+#include "net/third_party/uri_template/uri_template.h"
+#include "url/gurl.h"
+#include "url/scheme_host_port.h"
 
 namespace net {
 
@@ -180,6 +187,26 @@
     return &config->hosts;
   }
 
+  absl::optional<AddressList> GetPresetAddrs(
+      const url::SchemeHostPort& endpoint) const override {
+    DCHECK(endpoint.IsValid());
+    if (!session_)
+      return absl::nullopt;
+    const auto& servers = session_->config().dns_over_https_servers;
+    auto it = base::ranges::find_if(servers, [&](const auto& server) {
+      std::string uri;
+      bool valid = uri_template::Expand(server.server_template(), {}, &uri);
+      // Server templates are validated before being allowed into the config.
+      DCHECK(valid);
+      GURL gurl(uri);
+      return url::SchemeHostPort(gurl) == endpoint;
+    });
+    if (it == servers.end())
+      return absl::nullopt;
+    // TODO(crbug.com/1200908): Read preset IPs from the server config.
+    return absl::nullopt;
+  }
+
   DnsTransactionFactory* GetTransactionFactory() override {
     return session_.get() ? factory_.get() : nullptr;
   }
diff --git a/net/dns/dns_client.h b/net/dns/dns_client.h
index 6eb4ca70..c9b32bd 100644
--- a/net/dns/dns_client.h
+++ b/net/dns/dns_client.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "net/base/address_list.h"
 #include "net/base/net_export.h"
 #include "net/base/rand_callback.h"
 #include "net/dns/dns_config.h"
@@ -14,6 +15,12 @@
 #include "net/dns/public/dns_config_overrides.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
+namespace url {
+
+class SchemeHostPort;
+
+}  // namespace url
+
 namespace net {
 
 class AddressSorter;
@@ -80,6 +87,11 @@
   virtual const DnsConfig* GetEffectiveConfig() const = 0;
   virtual const DnsHosts* GetHosts() const = 0;
 
+  // Returns all preset addresses for the specified endpoint, if any are
+  // present in the current effective DnsConfig.
+  virtual absl::optional<AddressList> GetPresetAddrs(
+      const url::SchemeHostPort& endpoint) const = 0;
+
   // Returns null if the current config is not valid.
   virtual DnsTransactionFactory* GetTransactionFactory() = 0;
 
diff --git a/net/dns/dns_config_service_linux.cc b/net/dns/dns_config_service_linux.cc
index 587e1ba..ffa7ad5 100644
--- a/net/dns/dns_config_service_linux.cc
+++ b/net/dns/dns_config_service_linux.cc
@@ -443,10 +443,11 @@
       base::ScopedBlockingCall scoped_blocking_call(
           FROM_HERE, base::BlockingType::MAY_BLOCK);
 
-      std::unique_ptr<struct __res_state> res = resolv_reader_->GetResState();
-      if (res) {
-        dns_config_ = ConvertResStateToDnsConfig(*res.get());
-        resolv_reader_->CloseResState(res.get());
+      {
+        std::unique_ptr<ScopedResState> res = resolv_reader_->GetResState();
+        if (res) {
+          dns_config_ = ConvertResStateToDnsConfig(res->state());
+        }
       }
 
       UMA_HISTOGRAM_BOOLEAN("Net.DNS.DnsConfig.Resolv.Read",
diff --git a/net/dns/dns_config_service_linux_unittest.cc b/net/dns/dns_config_service_linux_unittest.cc
index 7964890..1daebed8 100644
--- a/net/dns/dns_config_service_linux_unittest.cc
+++ b/net/dns/dns_config_service_linux_unittest.cc
@@ -52,7 +52,7 @@
 
 const char* const kNameserversIPv6[] = {
     nullptr,
-    "2001:DB8:0::42",
+    "2001:db8::42",
     nullptr,
     "::FFFF:129.144.52.38",
 };
@@ -226,25 +226,44 @@
       base::ThreadTaskRunnerHandle::Get();
 };
 
+class TestScopedResState : public ScopedResState {
+ public:
+  explicit TestScopedResState(std::unique_ptr<struct __res_state> res)
+      : res_(std::move(res)) {}
+
+  ~TestScopedResState() override {
+    if (res_) {
+      // Assume `res->_u._ext.nsaddrs` memory allocated via malloc, e.g. by
+      // `InitializeResState()`.
+      for (int i = 0; i < res_->nscount; ++i) {
+        if (res_->_u._ext.nsaddrs[i] != nullptr)
+          free(res_->_u._ext.nsaddrs[i]);
+      }
+    }
+  }
+
+  const struct __res_state& state() const override {
+    EXPECT_TRUE(res_);
+    return *res_;
+  }
+
+ private:
+  std::unique_ptr<struct __res_state> res_;
+};
+
 class TestResolvReader : public ResolvReader {
  public:
-  ~TestResolvReader() override {
-    if (value_)
-      CloseResState(value_.get());
-  }
+  ~TestResolvReader() override = default;
 
   void set_value(std::unique_ptr<struct __res_state> value) {
     CHECK(!value_);
-    CHECK(closed_);
-
-    value_ = std::move(value);
-    closed_ = false;
+    value_ = std::make_unique<TestScopedResState>(std::move(value));
   }
 
-  bool closed() { return closed_; }
+  bool closed() { return !value_; }
 
   // ResolvReader:
-  std::unique_ptr<struct __res_state> GetResState() override {
+  std::unique_ptr<ScopedResState> GetResState() override {
     if (blocking_helper_)
       blocking_helper_->WaitUntilUnblocked();
 
@@ -252,24 +271,12 @@
     return std::move(value_);
   }
 
-  void CloseResState(struct __res_state* res) override {
-    closed_ = true;
-
-    // Assume `res->_u._ext.nsaddrs` memory allocated via malloc, e.g. by
-    // `InitializeResState()`.
-    for (int i = 0; i < res->nscount; ++i) {
-      if (res->_u._ext.nsaddrs[i] != nullptr)
-        free(res->_u._ext.nsaddrs[i]);
-    }
-  }
-
   void set_blocking_helper(BlockingHelper* blocking_helper) {
     blocking_helper_ = blocking_helper;
   }
 
  private:
-  std::unique_ptr<struct __res_state> value_;
-  bool closed_ = true;  // Start "closed" until a value is set.
+  std::unique_ptr<TestScopedResState> value_;
   BlockingHelper* blocking_helper_ = nullptr;
 };
 
diff --git a/net/dns/dns_config_service_posix.cc b/net/dns/dns_config_service_posix.cc
index 3a541ac..c8e997d 100644
--- a/net/dns/dns_config_service_posix.cc
+++ b/net/dns/dns_config_service_posix.cc
@@ -25,6 +25,7 @@
 #include "net/dns/dns_config.h"
 #include "net/dns/dns_hosts.h"
 #include "net/dns/notify_watcher_mac.h"
+#include "net/dns/public/resolv_reader.h"
 #include "net/dns/serial_worker.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -92,25 +93,13 @@
                                                 base::BlockingType::MAY_BLOCK);
 
   absl::optional<DnsConfig> dns_config;
-// TODO(fuchsia): Use res_ninit() when it's implemented on Fuchsia.
-#if defined(OS_OPENBSD) || defined(OS_FUCHSIA)
-  // Note: res_ninit in glibc always returns 0 and sets RES_INIT.
-  // res_init behaves the same way.
-  memset(&_res, 0, sizeof(_res));
-  if (res_init() == 0)
-    dns_config = ConvertResStateToDnsConfig(_res);
-#else  // all other OS_POSIX
-  struct __res_state res;
-  memset(&res, 0, sizeof(res));
-  if (res_ninit(&res) == 0)
-    dns_config = ConvertResStateToDnsConfig(res);
-    // Prefer res_ndestroy where available.
-#if defined(OS_APPLE) || defined(OS_FREEBSD)
-  res_ndestroy(&res);
-#else
-  res_nclose(&res);
-#endif  // defined(OS_APPLE) || defined(OS_FREEBSD)
-#endif  // defined(OS_OPENBSD)
+  {
+    std::unique_ptr<ScopedResState> scoped_res_state =
+        ResolvReader().GetResState();
+    if (scoped_res_state) {
+      dns_config = ConvertResStateToDnsConfig(scoped_res_state->state());
+    }
+  }
 
   if (!dns_config.has_value())
     return dns_config;
@@ -265,58 +254,11 @@
   if (!(res.options & RES_INIT))
     return absl::nullopt;
 
-#if defined(OS_APPLE) || defined(OS_FREEBSD)
-  union res_sockaddr_union addresses[MAXNS];
-  int nscount = res_getservers(const_cast<res_state>(&res), addresses, MAXNS);
-  DCHECK_GE(nscount, 0);
-  DCHECK_LE(nscount, MAXNS);
-  for (int i = 0; i < nscount; ++i) {
-    IPEndPoint ipe;
-    if (!ipe.FromSockAddr(
-            reinterpret_cast<const struct sockaddr*>(&addresses[i]),
-            sizeof addresses[i])) {
-      return absl::nullopt;
-    }
-    dns_config.nameservers.push_back(ipe);
-  }
-#elif defined(OS_CHROMEOS)
-  static_assert(std::extent<decltype(res.nsaddr_list)>() >= MAXNS &&
-                    std::extent<decltype(res._u._ext.nsaddrs)>() >= MAXNS,
-                "incompatible libresolv res_state");
-  DCHECK_LE(res.nscount, MAXNS);
-  // Initially, glibc stores IPv6 in |_ext.nsaddrs| and IPv4 in |nsaddr_list|.
-  // In res_send.c:res_nsend, it merges |nsaddr_list| into |nsaddrs|,
-  // but we have to combine the two arrays ourselves.
-  for (int i = 0; i < res.nscount; ++i) {
-    IPEndPoint ipe;
-    const struct sockaddr* addr = nullptr;
-    size_t addr_len = 0;
-    if (res.nsaddr_list[i].sin_family) {  // The indicator used by res_nsend.
-      addr = reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]);
-      addr_len = sizeof res.nsaddr_list[i];
-    } else if (res._u._ext.nsaddrs[i]) {
-      addr = reinterpret_cast<const struct sockaddr*>(res._u._ext.nsaddrs[i]);
-      addr_len = sizeof *res._u._ext.nsaddrs[i];
-    } else {
-      return absl::nullopt;
-    }
-    if (!ipe.FromSockAddr(addr, addr_len))
-      return absl::nullopt;
-    dns_config.nameservers.push_back(ipe);
-  }
-#else   // !(defined(OS_CHROMEOS) || defined(OS_APPLE) || defined(OS_FREEBSD))
-  DCHECK_LE(res.nscount, MAXNS);
-  for (int i = 0; i < res.nscount; ++i) {
-    IPEndPoint ipe;
-    if (!ipe.FromSockAddr(
-            reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]),
-            sizeof res.nsaddr_list[i])) {
-      return absl::nullopt;
-    }
-    dns_config.nameservers.push_back(ipe);
-  }
-#endif  // defined(OS_APPLE) || defined(OS_FREEBSD)
+  absl::optional<std::vector<IPEndPoint>> nameservers = GetNameservers(res);
+  if (!nameservers)
+    return absl::nullopt;
 
+  dns_config.nameservers = std::move(*nameservers);
   dns_config.search.clear();
   for (int i = 0; (i < MAXDNSRCH) && res.dnsrch[i]; ++i) {
     dns_config.search.emplace_back(res.dnsrch[i]);
diff --git a/net/dns/dns_test_util.cc b/net/dns/dns_test_util.cc
index 696c6b0..7e37f15 100644
--- a/net/dns/dns_test_util.cc
+++ b/net/dns/dns_test_util.cc
@@ -19,6 +19,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "net/base/address_list.h"
 #include "net/base/io_buffer.h"
 #include "net/base/ip_address.h"
 #include "net/base/net_errors.h"
@@ -30,7 +31,9 @@
 #include "net/dns/dns_util.h"
 #include "net/dns/public/dns_over_https_server_config.h"
 #include "net/dns/resolve_context.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/scheme_host_port.h"
 
 namespace net {
 namespace {
@@ -734,6 +737,12 @@
   NOTREACHED();
 }
 
+absl::optional<AddressList> MockDnsClient::GetPresetAddrs(
+    const url::SchemeHostPort& endpoint) const {
+  EXPECT_THAT(preset_endpoint_, testing::Optional(endpoint));
+  return preset_addrs_;
+}
+
 void MockDnsClient::CompleteDelayedTransactions() {
   factory_->CompleteDelayedTransactions();
 }
diff --git a/net/dns/dns_test_util.h b/net/dns/dns_test_util.h
index 04757f6..8836e2a 100644
--- a/net/dns/dns_test_util.h
+++ b/net/dns/dns_test_util.h
@@ -19,6 +19,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
+#include "net/base/address_list.h"
 #include "net/dns/dns_client.h"
 #include "net/dns/dns_config.h"
 #include "net/dns/dns_response.h"
@@ -29,6 +30,7 @@
 #include "net/dns/public/secure_dns_mode.h"
 #include "net/socket/socket_test_util.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/scheme_host_port.h"
 
 namespace net {
 
@@ -404,6 +406,8 @@
   DnsConfigOverrides GetConfigOverridesForTesting() const override;
   void SetTransactionFactoryForTesting(
       std::unique_ptr<DnsTransactionFactory> factory) override;
+  absl::optional<AddressList> GetPresetAddrs(
+      const url::SchemeHostPort& endpoint) const override;
 
   // Completes all DnsTransactions that were delayed by a rule.
   void CompleteDelayedTransactions();
@@ -420,6 +424,14 @@
     ignore_system_config_changes_ = ignore_system_config_changes;
   }
 
+  void set_preset_endpoint(absl::optional<url::SchemeHostPort> endpoint) {
+    preset_endpoint_ = std::move(endpoint);
+  }
+
+  void set_preset_addrs(AddressList preset_addrs) {
+    preset_addrs_ = std::move(preset_addrs);
+  }
+
   void SetForceDohServerAvailable(bool available);
 
   MockDnsTransactionFactory* factory() { return factory_.get(); }
@@ -447,6 +459,8 @@
   absl::optional<DnsConfig> effective_config_;
   std::unique_ptr<MockDnsTransactionFactory> factory_;
   std::unique_ptr<AddressSorter> address_sorter_;
+  absl::optional<url::SchemeHostPort> preset_endpoint_;
+  absl::optional<AddressList> preset_addrs_;
 };
 
 // Convert a list of templates into a list of server configs.
diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc
index b1cf7dd..f37d5fa1 100644
--- a/net/dns/dns_transaction.cc
+++ b/net/dns/dns_transaction.cc
@@ -434,8 +434,9 @@
     }
 
     request_->SetExtraRequestHeaders(extra_request_headers);
-    // Disable secure DNS for any DoH server hostname lookups to avoid deadlock.
-    request_->SetSecureDnsPolicy(SecureDnsPolicy::kDisable);
+    // Apply special policy to DNS lookups for for a DoH server hostname to
+    // avoid deadlock and enable the use of preconfigured IP addresses.
+    request_->SetSecureDnsPolicy(SecureDnsPolicy::kBootstrap);
     request_->SetLoadFlags(request_->load_flags() | LOAD_DISABLE_CACHE |
                            LOAD_BYPASS_PROXY);
     request_->set_allow_credentials(false);
diff --git a/net/dns/dns_transaction_unittest.cc b/net/dns/dns_transaction_unittest.cc
index c2702b6..1c536bb9 100644
--- a/net/dns/dns_transaction_unittest.cc
+++ b/net/dns/dns_transaction_unittest.cc
@@ -838,7 +838,7 @@
     }
 
     EXPECT_FALSE(request->allow_credentials());
-    EXPECT_EQ(SecureDnsPolicy::kDisable, request->secure_dns_policy());
+    EXPECT_EQ(SecureDnsPolicy::kBootstrap, request->secure_dns_policy());
 
     std::string accept;
     EXPECT_TRUE(request->extra_request_headers().GetHeader("Accept", &accept));
diff --git a/net/dns/host_cache.cc b/net/dns/host_cache.cc
index 123208b..9c1ce87a 100644
--- a/net/dns/host_cache.cc
+++ b/net/dns/host_cache.cc
@@ -5,6 +5,7 @@
 #include "net/dns/host_cache.h"
 
 #include <algorithm>
+#include <ostream>
 #include <string>
 #include <unordered_set>
 #include <vector>
@@ -999,3 +1000,10 @@
 }
 
 }  // namespace net
+
+// Debug logging support
+std::ostream& operator<<(std::ostream& out,
+                         const net::HostCache::EntryStaleness& s) {
+  return out << "EntryStaleness{" << s.expired_by << ", " << s.network_changes
+             << ", " << s.stale_hits << "}";
+}
diff --git a/net/dns/host_cache.h b/net/dns/host_cache.h
index a7752c4..7592240d 100644
--- a/net/dns/host_cache.h
+++ b/net/dns/host_cache.h
@@ -10,6 +10,7 @@
 #include <functional>
 #include <map>
 #include <memory>
+#include <ostream>
 #include <string>
 #include <tuple>
 #include <utility>
@@ -115,6 +116,8 @@
       SOURCE_DNS,
       // Address list was obtained by searching a HOSTS file.
       SOURCE_HOSTS,
+      // Address list was a preset from the DnsConfig.
+      SOURCE_CONFIG,
     };
 
     // |ttl=absl::nullopt| for unknown TTL.
@@ -278,6 +281,9 @@
     // network change.  When an Entry is replaced by one whose pinning flag
     // is not set, HostCache will copy this flag to the replacement.
     // If this flag is null, HostCache will set it to false for simplicity.
+    // Note: This flag is not yet used, and should be removed if the proposals
+    // for followup queries after insecure/expired bootstrap are abandoned (see
+    // TODO(crbug.com/1200908) in HostResolverManager).
     absl::optional<bool> pinning_;
     // TTL obtained from the nameserver. Negative if unknown.
     base::TimeDelta ttl_ = base::Seconds(-1);
@@ -459,4 +465,8 @@
 
 }  // namespace net
 
+// Debug logging support
+std::ostream& operator<<(std::ostream& out,
+                         const net::HostCache::EntryStaleness& s);
+
 #endif  // NET_DNS_HOST_CACHE_H_
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index 2930f88..f1efb4b3 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -615,6 +615,19 @@
       DnsQueryTypeToQtype(query_type));
 }
 
+AddressList FilterAddresses(AddressList addresses, DnsQueryType query_type) {
+  if (query_type == DnsQueryType::UNSPECIFIED)
+    return addresses;
+
+  AddressFamily family = HostResolver::DnsQueryTypeToAddressFamily(query_type);
+  addresses.endpoints().erase(
+      base::ranges::remove_if(
+          addresses,
+          [&](const auto& endpoint) { return endpoint.GetFamily() != family; }),
+      addresses.end());
+  return addresses;
+}
+
 }  // namespace
 
 //-----------------------------------------------------------------------------
@@ -2126,7 +2139,7 @@
       // If we were called from a Request's callback within CompleteRequests,
       // that Request could not have been cancelled, so num_active_requests()
       // could not be 0. Therefore, we are not in CompleteRequests().
-      CompleteRequestsWithError(ERR_FAILED /* cancelled */);
+      CompleteRequestsWithError(ERR_DNS_REQUEST_CANCELLED);
     }
   }
 
@@ -2293,6 +2306,7 @@
         break;
       case TaskType::SECURE_CACHE_LOOKUP:
       case TaskType::CACHE_LOOKUP:
+      case TaskType::CONFIG_PRESET:
         // These task types should have been handled synchronously in
         // ResolveLocally() prior to Job creation.
         NOTREACHED();
@@ -2748,7 +2762,7 @@
 
     Finish();
 
-    if (num_active_requests() == 0) {
+    if (results.error() == ERR_DNS_REQUEST_CANCELLED) {
       net_log_.AddEvent(NetLogEventType::CANCELLED);
       net_log_.EndEventWithNetErrorCode(
           NetLogEventType::HOST_RESOLVER_MANAGER_JOB, OK);
@@ -2758,8 +2772,6 @@
     net_log_.EndEventWithNetErrorCode(
         NetLogEventType::HOST_RESOLVER_MANAGER_JOB, results.error());
 
-    DCHECK(!requests_.empty());
-
     // Handle all caching before completing requests as completing requests may
     // start new requests that rely on cached results.
     if (allow_cache)
@@ -2792,6 +2804,10 @@
       if (!resolver_.get())
         return;
     }
+
+    // TODO(crbug.com/1200908): Call StartBootstrapFollowup() if any of the
+    // requests have the Bootstrap policy.  Note: A naive implementation could
+    // cause an infinite loop if the bootstrap result has TTL=0.
   }
 
   void CompleteRequestsWithoutCache(
@@ -3151,6 +3167,19 @@
   proc_task_runner_ = std::move(task_runner);
 }
 
+// static
+bool HostResolverManager::IsLocalTask(TaskType task) {
+  switch (task) {
+    case TaskType::SECURE_CACHE_LOOKUP:
+    case TaskType::INSECURE_CACHE_LOOKUP:
+    case TaskType::CACHE_LOOKUP:
+    case TaskType::CONFIG_PRESET:
+      return true;
+    default:
+      return false;
+  }
+}
+
 int HostResolverManager::Resolve(RequestImpl* request) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // Request should not yet have a scheduled Job.
@@ -3184,8 +3213,8 @@
   std::deque<TaskType> tasks;
   absl::optional<HostCache::EntryStaleness> stale_info;
   HostCache::Entry results = ResolveLocally(
-      job_key, ip_address, parameters.cache_usage, request->source_net_log(),
-      request->host_cache(), &tasks, &stale_info);
+      job_key, ip_address, parameters.cache_usage, parameters.secure_dns_policy,
+      request->source_net_log(), request->host_cache(), &tasks, &stale_info);
   if (results.error() != ERR_DNS_CACHE_MISS ||
       request->parameters().source == HostResolverSource::LOCAL_ONLY ||
       tasks.empty()) {
@@ -3211,6 +3240,7 @@
     const JobKey& job_key,
     const IPAddress& ip_address,
     ResolveHostParameters::CacheUsage cache_usage,
+    SecureDnsPolicy secure_dns_policy,
     const NetLogWithSource& source_net_log,
     HostCache* cache,
     std::deque<TaskType>* out_tasks,
@@ -3218,7 +3248,7 @@
   DCHECK(out_stale_info);
   *out_stale_info = absl::nullopt;
 
-  CreateTaskSequence(job_key, cache_usage, out_tasks);
+  CreateTaskSequence(job_key, cache_usage, secure_dns_policy, out_tasks);
 
   if (!ip_address.IsValid()) {
     // Check that the caller supplied a valid hostname to resolve. For
@@ -3259,31 +3289,45 @@
   if (resolved)
     return resolved.value();
 
-  // Do initial cache lookup.
-  if (!out_tasks->empty() &&
-      (out_tasks->front() == TaskType::SECURE_CACHE_LOOKUP ||
-       out_tasks->front() == TaskType::INSECURE_CACHE_LOOKUP ||
-       out_tasks->front() == TaskType::CACHE_LOOKUP)) {
-    bool secure = out_tasks->front() == TaskType::SECURE_CACHE_LOOKUP;
-    HostCache::Key key = job_key.ToCacheKey(secure);
-
-    bool ignore_secure = false;
-    if (out_tasks->front() == TaskType::CACHE_LOOKUP)
-      ignore_secure = true;
-
+  // Do initial cache lookups.
+  while (!out_tasks->empty() && IsLocalTask(out_tasks->front())) {
+    TaskType task = out_tasks->front();
     out_tasks->pop_front();
+    if (task == TaskType::SECURE_CACHE_LOOKUP ||
+        task == TaskType::INSECURE_CACHE_LOOKUP ||
+        task == TaskType::CACHE_LOOKUP) {
+      bool secure = task == TaskType::SECURE_CACHE_LOOKUP;
+      HostCache::Key key = job_key.ToCacheKey(secure);
 
-    resolved = MaybeServeFromCache(cache, key, cache_usage, ignore_secure,
-                                   source_net_log, out_stale_info);
-    if (resolved) {
-      // |MaybeServeFromCache()| will update |*out_stale_info| as needed.
-      DCHECK(out_stale_info->has_value());
-      source_net_log.AddEvent(NetLogEventType::HOST_RESOLVER_MANAGER_CACHE_HIT,
-                              [&] { return NetLogResults(resolved.value()); });
+      bool ignore_secure = task == TaskType::CACHE_LOOKUP;
+      resolved = MaybeServeFromCache(cache, key, cache_usage, ignore_secure,
+                                     source_net_log, out_stale_info);
+      if (resolved) {
+        // |MaybeServeFromCache()| will update |*out_stale_info| as needed.
+        DCHECK(out_stale_info->has_value());
+        source_net_log.AddEvent(
+            NetLogEventType::HOST_RESOLVER_MANAGER_CACHE_HIT,
+            [&] { return NetLogResults(resolved.value()); });
 
-      return resolved.value();
+        // TODO(crbug.com/1200908): Call StartBootstrapFollowup() if the Secure
+        // DNS Policy is kBootstrap and the result is not secure.  Note: A naive
+        // implementation could cause an infinite loop if |resolved| always
+        // expires or is evicted before the followup runs.
+        return resolved.value();
+      }
+      DCHECK(!out_stale_info->has_value());
+    } else if (task == TaskType::CONFIG_PRESET) {
+      resolved = MaybeReadFromConfig(job_key);
+      if (resolved) {
+        source_net_log.AddEvent(
+            NetLogEventType::HOST_RESOLVER_MANAGER_CONFIG_PRESET_MATCH,
+            [&] { return NetLogResults(resolved.value()); });
+        StartBootstrapFollowup(job_key, cache, source_net_log);
+        return resolved.value();
+      }
+    } else {
+      NOTREACHED();
     }
-    DCHECK(!out_stale_info->has_value());
   }
 
   // TODO(szym): Do not do this if nsswitch.conf instructs not to.
@@ -3307,14 +3351,9 @@
   auto jobit = jobs_.find(key);
   Job* job;
   if (jobit == jobs_.end()) {
-    auto new_job = std::make_unique<Job>(
-        weak_ptr_factory_.GetWeakPtr(), key, request->parameters().cache_usage,
-        request->host_cache(), std::move(tasks), request->priority(),
-        proc_task_runner_, request->source_net_log(), tick_clock_);
-    job = new_job.get();
-    auto insert_result = jobs_.emplace(std::move(key), std::move(new_job));
-    DCHECK(insert_result.second);
-    job->OnAddedToJobMap(insert_result.first);
+    job = AddJobWithoutRequest(key, request->parameters().cache_usage,
+                               request->host_cache(), std::move(tasks),
+                               request->priority(), request->source_net_log());
     job->AddRequest(request);
     job->RunNextTask();
   } else {
@@ -3323,6 +3362,26 @@
   }
 }
 
+HostResolverManager::Job* HostResolverManager::AddJobWithoutRequest(
+    JobKey key,
+    ResolveHostParameters::CacheUsage cache_usage,
+    HostCache* host_cache,
+    std::deque<TaskType> tasks,
+    RequestPriority priority,
+    const NetLogWithSource& source_net_log) {
+  auto new_job =
+      std::make_unique<Job>(weak_ptr_factory_.GetWeakPtr(), key, cache_usage,
+                            host_cache, std::move(tasks), priority,
+                            proc_task_runner_, source_net_log, tick_clock_);
+  auto insert_result = jobs_.emplace(std::move(key), std::move(new_job));
+  auto& iterator = insert_result.first;
+  bool is_new = insert_result.second;
+  DCHECK(is_new);
+  auto& job = iterator->second;
+  job->OnAddedToJobMap(iterator);
+  return job.get();
+}
+
 HostCache::Entry HostResolverManager::ResolveAsIP(DnsQueryType query_type,
                                                   bool resolve_canonname,
                                                   const IPAddress& ip_address) {
@@ -3387,6 +3446,42 @@
   return absl::nullopt;
 }
 
+absl::optional<HostCache::Entry> HostResolverManager::MaybeReadFromConfig(
+    const JobKey& key) {
+  DCHECK(IsAddressType(key.query_type));
+  if (!absl::holds_alternative<url::SchemeHostPort>(key.host))
+    return absl::nullopt;
+  absl::optional<AddressList> preset_addrs =
+      dns_client_->GetPresetAddrs(absl::get<url::SchemeHostPort>(key.host));
+  if (!preset_addrs)
+    return absl::nullopt;
+
+  AddressList filtered_addresses =
+      FilterAddresses(std::move(*preset_addrs), key.query_type);
+  if (filtered_addresses.empty())
+    return absl::nullopt;
+
+  return HostCache::Entry(OK, std::move(filtered_addresses),
+                          HostCache::Entry::SOURCE_CONFIG);
+}
+
+void HostResolverManager::StartBootstrapFollowup(
+    JobKey key,
+    HostCache* host_cache,
+    const NetLogWithSource& source_net_log) {
+  DCHECK_EQ(SecureDnsMode::kOff, key.secure_dns_mode);
+  DCHECK(host_cache);
+
+  key.secure_dns_mode = SecureDnsMode::kSecure;
+  if (jobs_.count(key) != 0)
+    return;
+
+  Job* job = AddJobWithoutRequest(
+      key, ResolveHostParameters::CacheUsage::ALLOWED, host_cache,
+      {TaskType::SECURE_DNS}, RequestPriority::LOW, source_net_log);
+  job->RunNextTask();
+}
+
 absl::optional<HostCache::Entry> HostResolverManager::ServeFromHosts(
     base::StringPiece hostname,
     DnsQueryType query_type,
@@ -3449,23 +3544,13 @@
     return absl::nullopt;
   }
 
-  AddressList filtered_addresses;
-  for (const auto& address : resolved_addresses) {
-    // Include the address if:
-    // - caller didn't specify an address family, or
-    // - caller specifically asked for the address family of this address, or
-    // - this is an IPv6 address and caller specifically asked for IPv4 due
-    //   to lack of detected IPv6 support. (See SystemHostResolverCall for
-    //   rationale).
-    if (query_type == DnsQueryType::UNSPECIFIED ||
-        HostResolver::DnsQueryTypeToAddressFamily(query_type) ==
-            address.GetFamily() ||
-        (address.GetFamily() == ADDRESS_FAMILY_IPV6 &&
-         query_type == DnsQueryType::A && default_family_due_to_no_ipv6)) {
-      filtered_addresses.push_back(address);
-    }
+  if (query_type == DnsQueryType::A && default_family_due_to_no_ipv6) {
+    // The caller specifically asked for IPv4 due to lack of detected IPv6
+    // support. (See SystemHostResolverCall for rationale).
+    query_type = DnsQueryType::UNSPECIFIED;
   }
-
+  AddressList filtered_addresses =
+      FilterAddresses(std::move(resolved_addresses), query_type);
   return HostCache::Entry(OK, std::move(filtered_addresses),
                           HostCache::Entry::SOURCE_UNKNOWN);
 }
@@ -3498,6 +3583,7 @@
   // Use switch() instead of if() to ensure that all policies are handled.
   switch (secure_dns_policy) {
     case SecureDnsPolicy::kDisable:
+    case SecureDnsPolicy::kBootstrap:
       return SecureDnsMode::kOff;
     case SecureDnsPolicy::kAllow:
       break;
@@ -3572,7 +3658,7 @@
       }
       break;
     case SecureDnsMode::kOff:
-      DCHECK(!allow_cache || out_tasks->front() == TaskType::CACHE_LOOKUP);
+      DCHECK(!allow_cache || IsLocalTask(out_tasks->front()));
       if (dns_tasks_allowed && insecure_tasks_allowed)
         out_tasks->push_back(TaskType::DNS);
       break;
@@ -3597,6 +3683,7 @@
 void HostResolverManager::CreateTaskSequence(
     const JobKey& job_key,
     ResolveHostParameters::CacheUsage cache_usage,
+    SecureDnsPolicy secure_dns_policy,
     std::deque<TaskType>* out_tasks) {
   DCHECK(out_tasks->empty());
 
@@ -3604,7 +3691,14 @@
   // DnsTask, this task may be replaced.
   bool allow_cache =
       cache_usage != ResolveHostParameters::CacheUsage::DISALLOWED;
-  if (allow_cache) {
+  if (secure_dns_policy == SecureDnsPolicy::kBootstrap) {
+    DCHECK_EQ(SecureDnsMode::kOff, job_key.secure_dns_mode);
+    if (allow_cache)
+      out_tasks->push_front(TaskType::INSECURE_CACHE_LOOKUP);
+    out_tasks->push_front(TaskType::CONFIG_PRESET);
+    if (allow_cache)
+      out_tasks->push_front(TaskType::SECURE_CACHE_LOOKUP);
+  } else if (allow_cache) {
     if (job_key.secure_dns_mode == SecureDnsMode::kSecure) {
       out_tasks->push_front(TaskType::SECURE_CACHE_LOOKUP);
     } else {
diff --git a/net/dns/host_resolver_manager.h b/net/dns/host_resolver_manager.h
index 6af3325..f9f9038d 100644
--- a/net/dns/host_resolver_manager.h
+++ b/net/dns/host_resolver_manager.h
@@ -246,8 +246,12 @@
     CACHE_LOOKUP,
     INSECURE_CACHE_LOOKUP,
     SECURE_CACHE_LOOKUP,
+    CONFIG_PRESET,
   };
 
+  // Returns true if the task is local, synchronous, and instantaneous.
+  static bool IsLocalTask(TaskType task);
+
   // Attempts host resolution for |request|. Generally only expected to be
   // called from RequestImpl::Start().
   int Resolve(RequestImpl* request);
@@ -271,6 +275,7 @@
       const JobKey& job_key,
       const IPAddress& ip_address,
       ResolveHostParameters::CacheUsage cache_usage,
+      SecureDnsPolicy secure_dns_policy,
       const NetLogWithSource& request_net_log,
       HostCache* cache,
       std::deque<TaskType>* out_tasks,
@@ -282,6 +287,14 @@
                          std::deque<TaskType> tasks,
                          RequestImpl* request);
 
+  HostResolverManager::Job* AddJobWithoutRequest(
+      JobKey key,
+      ResolveHostParameters::CacheUsage cache_usage,
+      HostCache* host_cache,
+      std::deque<TaskType> tasks,
+      RequestPriority priority,
+      const NetLogWithSource& source_net_log);
+
   // Resolves the IP literal hostname represented by `ip_address`.
   HostCache::Entry ResolveAsIP(DnsQueryType query_type,
                                bool resolve_canonname,
@@ -299,6 +312,16 @@
       const NetLogWithSource& source_net_log,
       absl::optional<HostCache::EntryStaleness>* out_stale_info);
 
+  // Returns any preset resolution result from the active DoH configuration that
+  // matches |key.host|.
+  absl::optional<HostCache::Entry> MaybeReadFromConfig(const JobKey& key);
+
+  // Populates the secure cache with an optimal entry that supersedes the
+  // bootstrap result.
+  void StartBootstrapFollowup(JobKey key,
+                              HostCache* host_cache,
+                              const NetLogWithSource& source_net_log);
+
   // Iff we have a DnsClient with a valid DnsConfig and we're not about to
   // attempt a system lookup, then try to resolve the query using the HOSTS
   // file.
@@ -338,6 +361,7 @@
   // may be adjusted later and not all tasks need to be run.
   void CreateTaskSequence(const JobKey& job_key,
                           ResolveHostParameters::CacheUsage cache_usage,
+                          SecureDnsPolicy secure_dns_policy,
                           std::deque<TaskType>* out_tasks);
 
   // Determines "effective" request parameters using manager properties and IPv6
diff --git a/net/dns/host_resolver_manager_unittest.cc b/net/dns/host_resolver_manager_unittest.cc
index bea0abdc..1c5d518 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -13847,4 +13847,346 @@
   EXPECT_THAT(response.result_error(), IsOk());
 }
 
+class HostResolverManagerBootstrapTest : public HostResolverManagerDnsTest {
+ protected:
+  using MockResult = MockDnsClientRule::ResultType;
+
+  void SetUp() override {
+    HostResolverManagerDnsTest::SetUp();
+
+    // MockHostResolverProc only returns failure if there is at least one
+    // non-matching rule.
+    proc_->AddRuleForAllFamilies("other_name", {});
+    proc_->SignalMultiple(1u);  // Allow up to one proc query.
+
+    // The request host scheme and port are only preserved if the SVCB feature
+    // is enabled.
+    features.InitAndEnableFeature(features::kUseDnsHttpsSvcb);
+  }
+
+  const NetworkIsolationKey kIsolationKey;
+  const url::SchemeHostPort kEndpoint =
+      url::SchemeHostPort(url::kHttpsScheme, "bootstrap", 443);
+  const AddressList kCacheAddrs = AddressList::CreateFromIPAddressList(
+      {{0x20, 0x01, 0x0d, 0xb1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
+       {192, 0, 2, 1}},
+      {});
+  const AddressList kBootstrapAddrs = AddressList::CreateFromIPAddressList(
+      {{0x20, 0x01, 0x0d, 0xb1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2},
+       {192, 0, 2, 2}},
+      {});
+  // The mock DNS client always returns localhost.
+  const AddressList kRemoteAddrs = AddressList::CreateFromIPAddressList(
+      {IPAddress::IPv6Localhost(), IPAddress::IPv4Localhost()},
+      {});
+
+  static HostResolver::ResolveHostParameters bootstrap_params() {
+    HostResolver::ResolveHostParameters params;
+    params.secure_dns_policy = SecureDnsPolicy::kBootstrap;
+    return params;
+  }
+
+  void ConfigureMockDns(MockResult insecure_result, MockResult secure_result) {
+    MockDnsClientRuleList rules;
+    AddDnsRule(&rules, kEndpoint.host(), dns_protocol::kTypeA, insecure_result,
+               /*delay=*/false);
+    AddDnsRule(&rules, kEndpoint.host(), dns_protocol::kTypeAAAA,
+               insecure_result, /*delay=*/false);
+    AddSecureDnsRule(&rules, kEndpoint.host(), dns_protocol::kTypeA,
+                     secure_result, /*delay=*/false);
+    AddSecureDnsRule(&rules, kEndpoint.host(), dns_protocol::kTypeAAAA,
+                     secure_result, /*delay=*/false);
+    UseMockDnsClient(CreateValidDnsConfig(), std::move(rules));
+    dns_client_->set_preset_endpoint(kEndpoint);
+  }
+
+  HostCache::Key MakeCacheKey(bool secure) {
+    HostCache::Key cache_key(kEndpoint, DnsQueryType::UNSPECIFIED, 0,
+                             HostResolverSource::ANY, kIsolationKey);
+    cache_key.secure = secure;
+    return cache_key;
+  }
+
+  void PopulateCache(bool secure) {
+    constexpr base::TimeDelta kTtl = base::Seconds(3600);
+    HostCache::Entry entry(OK, kCacheAddrs, HostCache::Entry::SOURCE_DNS, kTtl);
+    resolve_context_->host_cache()->Set(MakeCacheKey(secure), std::move(entry),
+                                        GetMockTickClock()->NowTicks(), kTtl);
+  }
+
+  base::test::ScopedFeatureList features;
+};
+
+std::vector<IPAddress> IPAddresses(const AddressList& addresses) {
+  std::vector<IPAddress> ip_addresses;
+  base::ranges::transform(addresses, std::back_inserter(ip_addresses),
+                          &IPEndPoint::address);
+  return ip_addresses;
+}
+
+MATCHER_P(AddressesMatch, expected, "Matches addresses between AddressLists") {
+  return testing::Matches(testing::UnorderedElementsAreArray(
+      IPAddresses(expected)))(IPAddresses(arg));
+}
+
+TEST_F(HostResolverManagerBootstrapTest, BlankSlate) {
+  ConfigureMockDns(/*insecure_result=*/MockResult::kOk,
+                   /*secure_result=*/MockResult::kUnexpected);
+
+  ResolveHostResponseHelper bootstrap_response(resolver_->CreateRequest(
+      kEndpoint, kIsolationKey, NetLogWithSource(), bootstrap_params(),
+      resolve_context_.get(), resolve_context_->host_cache()));
+
+  EXPECT_FALSE(bootstrap_response.complete());
+  EXPECT_THAT(bootstrap_response.result_error(), IsOk());
+  EXPECT_THAT(bootstrap_response.request()->GetAddressResults(),
+              Optional(AddressesMatch(kRemoteAddrs)));
+}
+
+TEST_F(HostResolverManagerBootstrapTest, InsecureCacheEntry) {
+  ConfigureMockDns(/*insecure_result=*/MockResult::kUnexpected,
+                   /*secure_result=*/MockResult::kUnexpected);
+  PopulateCache(/*secure=*/false);
+
+  ResolveHostResponseHelper bootstrap_response(resolver_->CreateRequest(
+      kEndpoint, kIsolationKey, NetLogWithSource(), bootstrap_params(),
+      resolve_context_.get(), resolve_context_->host_cache()));
+
+  EXPECT_TRUE(bootstrap_response.complete());
+  EXPECT_THAT(bootstrap_response.result_error(), IsOk());
+  EXPECT_THAT(bootstrap_response.request()->GetAddressResults(),
+              Optional(AddressesMatch(kCacheAddrs)));
+}
+
+TEST_F(HostResolverManagerBootstrapTest, SecureCacheEntry) {
+  ConfigureMockDns(/*insecure_result=*/MockResult::kUnexpected,
+                   /*secure_result=*/MockResult::kUnexpected);
+  PopulateCache(/*secure=*/true);
+
+  ResolveHostResponseHelper bootstrap_response(resolver_->CreateRequest(
+      kEndpoint, kIsolationKey, NetLogWithSource(), bootstrap_params(),
+      resolve_context_.get(), resolve_context_->host_cache()));
+
+  EXPECT_TRUE(bootstrap_response.complete());
+  EXPECT_THAT(bootstrap_response.result_error(), IsOk());
+  EXPECT_THAT(bootstrap_response.request()->GetAddressResults(),
+              Optional(AddressesMatch(kCacheAddrs)));
+}
+
+TEST_F(HostResolverManagerBootstrapTest, OnlyBootstrap) {
+  ConfigureMockDns(/*insecure_result=*/MockResult::kUnexpected,
+                   /*secure_result=*/MockResult::kOk);
+  dns_client_->set_preset_addrs(kBootstrapAddrs);
+
+  ResolveHostResponseHelper bootstrap_response(resolver_->CreateRequest(
+      kEndpoint, kIsolationKey, NetLogWithSource(), bootstrap_params(),
+      resolve_context_.get(), resolve_context_->host_cache()));
+
+  EXPECT_TRUE(bootstrap_response.complete());
+  EXPECT_THAT(bootstrap_response.result_error(), IsOk());
+  EXPECT_THAT(bootstrap_response.request()->GetAddressResults(),
+              Optional(AddressesMatch(kBootstrapAddrs)));
+
+  // Run the followup query.
+  RunUntilIdle();
+
+  // Confirm that the remote addresses are now in the secure cache.
+  const auto* secure_result = resolve_context_->host_cache()->Lookup(
+      MakeCacheKey(/*secure=*/true), GetMockTickClock()->NowTicks());
+  ASSERT_THAT(secure_result, testing::NotNull());
+  EXPECT_THAT(secure_result->second.addresses(),
+              Optional(AddressesMatch(kRemoteAddrs)));
+}
+
+// The insecure cache is ignored, so the results are identical to
+// OnlyBootstrap.
+TEST_F(HostResolverManagerBootstrapTest, BootstrapAndInsecureCache) {
+  ConfigureMockDns(/*insecure_result=*/MockResult::kUnexpected,
+                   /*secure_result=*/MockResult::kOk);
+  dns_client_->set_preset_addrs(kBootstrapAddrs);
+  PopulateCache(/*secure=*/false);
+
+  ResolveHostResponseHelper bootstrap_response(resolver_->CreateRequest(
+      kEndpoint, kIsolationKey, NetLogWithSource(), bootstrap_params(),
+      resolve_context_.get(), resolve_context_->host_cache()));
+
+  EXPECT_TRUE(bootstrap_response.complete());
+  EXPECT_THAT(bootstrap_response.result_error(), IsOk());
+  EXPECT_THAT(bootstrap_response.request()->GetAddressResults(),
+              Optional(AddressesMatch(kBootstrapAddrs)));
+
+  // Run the followup query.
+  RunUntilIdle();
+
+  // Confirm that the remote addresses are now in the secure cache.
+  const auto* secure_result = resolve_context_->host_cache()->Lookup(
+      MakeCacheKey(/*secure=*/true), GetMockTickClock()->NowTicks());
+  ASSERT_THAT(secure_result, testing::NotNull());
+  EXPECT_THAT(secure_result->second.addresses(),
+              Optional(AddressesMatch(kRemoteAddrs)));
+}
+
+// The bootstrap addrs are ignored, so the results are identical to
+// SecureCacheEntry.
+TEST_F(HostResolverManagerBootstrapTest, BootstrapAndSecureCacheEntry) {
+  ConfigureMockDns(/*insecure_result=*/MockResult::kUnexpected,
+                   /*secure_result=*/MockResult::kUnexpected);
+  dns_client_->set_preset_addrs(kBootstrapAddrs);
+  PopulateCache(/*secure=*/true);
+
+  ResolveHostResponseHelper bootstrap_response(resolver_->CreateRequest(
+      kEndpoint, kIsolationKey, NetLogWithSource(), bootstrap_params(),
+      resolve_context_.get(), resolve_context_->host_cache()));
+
+  EXPECT_TRUE(bootstrap_response.complete());
+  EXPECT_THAT(bootstrap_response.result_error(), IsOk());
+  EXPECT_THAT(bootstrap_response.request()->GetAddressResults(),
+              Optional(AddressesMatch(kCacheAddrs)));
+}
+
+TEST_F(HostResolverManagerBootstrapTest, BlankSlateFailure) {
+  ConfigureMockDns(/*insecure_result=*/MockResult::kFail,
+                   /*secure_result=*/MockResult::kUnexpected);
+
+  ResolveHostResponseHelper bootstrap_response(resolver_->CreateRequest(
+      kEndpoint, kIsolationKey, NetLogWithSource(), bootstrap_params(),
+      resolve_context_.get(), resolve_context_->host_cache()));
+
+  EXPECT_FALSE(bootstrap_response.complete());
+  EXPECT_THAT(bootstrap_response.result_error(),
+              IsError(ERR_NAME_NOT_RESOLVED));
+  EXPECT_FALSE(bootstrap_response.request()
+                   ->GetResolveErrorInfo()
+                   .is_secure_network_error);
+}
+
+TEST_F(HostResolverManagerBootstrapTest, BootstrapFollowupFailure) {
+  ConfigureMockDns(/*insecure_result=*/MockResult::kUnexpected,
+                   /*secure_result=*/MockResult::kFail);
+  dns_client_->set_preset_addrs(kBootstrapAddrs);
+
+  ResolveHostResponseHelper bootstrap_response(resolver_->CreateRequest(
+      kEndpoint, kIsolationKey, NetLogWithSource(), bootstrap_params(),
+      resolve_context_.get(), resolve_context_->host_cache()));
+
+  EXPECT_TRUE(bootstrap_response.complete());
+  EXPECT_THAT(bootstrap_response.result_error(), IsOk());
+  EXPECT_THAT(bootstrap_response.request()->GetAddressResults(),
+              Optional(AddressesMatch(kBootstrapAddrs)));
+
+  // Run the followup query.
+  RunUntilIdle();
+
+  // Confirm that the secure cache remains empty.
+  const auto* secure_result = resolve_context_->host_cache()->Lookup(
+      MakeCacheKey(/*secure=*/true), GetMockTickClock()->NowTicks());
+  EXPECT_THAT(secure_result, testing::IsNull());
+}
+
+TEST_F(HostResolverManagerBootstrapTest, ContextClose) {
+  ConfigureMockDns(/*insecure_result=*/MockResult::kUnexpected,
+                   /*secure_result=*/MockResult::kOk);
+  dns_client_->set_preset_addrs(kBootstrapAddrs);
+
+  // Trigger a followup request.
+  ResolveHostResponseHelper bootstrap_response(resolver_->CreateRequest(
+      kEndpoint, kIsolationKey, NetLogWithSource(), bootstrap_params(),
+      resolve_context_.get(), resolve_context_->host_cache()));
+
+  // Deregistering the resolve context should clean up the pending followup job.
+  EXPECT_EQ(1u, resolver_->num_jobs_for_testing());
+  resolver_->DeregisterResolveContext(resolve_context_.get());
+  EXPECT_EQ(0u, resolver_->num_jobs_for_testing());
+
+  resolver_ = nullptr;  // Avoid duplicate Deregister in TearDown.
+}
+
+// Equivalent to OnlyBootstrap + BootstrapAndSecureCacheEntry
+TEST_F(HostResolverManagerBootstrapTest, BootstrapAfterFollowup) {
+  ConfigureMockDns(/*insecure_result=*/MockResult::kUnexpected,
+                   /*secure_result=*/MockResult::kOk);
+  dns_client_->set_preset_addrs(kBootstrapAddrs);
+
+  // Run bootstrap and its followup query.
+  ResolveHostResponseHelper bootstrap_response1(resolver_->CreateRequest(
+      kEndpoint, kIsolationKey, NetLogWithSource(), bootstrap_params(),
+      resolve_context_.get(), resolve_context_->host_cache()));
+  RunUntilIdle();
+
+  // The remote addresses are now in the secure cache.
+  // Rerun bootstrap, which reads the secure cache results.
+  ResolveHostResponseHelper bootstrap_response2(resolver_->CreateRequest(
+      kEndpoint, kIsolationKey, NetLogWithSource(), bootstrap_params(),
+      resolve_context_.get(), resolve_context_->host_cache()));
+
+  EXPECT_TRUE(bootstrap_response2.complete());
+  EXPECT_THAT(bootstrap_response2.result_error(), IsOk());
+  EXPECT_THAT(bootstrap_response2.request()->GetAddressResults(),
+              Optional(AddressesMatch(kRemoteAddrs)));
+}
+
+TEST_F(HostResolverManagerBootstrapTest, BootstrapFollowupFailureTwice) {
+  ConfigureMockDns(/*insecure_result=*/MockResult::kUnexpected,
+                   /*secure_result=*/MockResult::kFail);
+  dns_client_->set_preset_addrs(kBootstrapAddrs);
+
+  // Run the bootstrap query and the followup, which will fail.
+  ResolveHostResponseHelper bootstrap_response1(resolver_->CreateRequest(
+      kEndpoint, kIsolationKey, NetLogWithSource(), bootstrap_params(),
+      resolve_context_.get(), resolve_context_->host_cache()));
+  RunUntilIdle();
+
+  // Reissue the bootstrap query.
+  ResolveHostResponseHelper bootstrap_response2(resolver_->CreateRequest(
+      kEndpoint, kIsolationKey, NetLogWithSource(), bootstrap_params(),
+      resolve_context_.get(), resolve_context_->host_cache()));
+
+  EXPECT_TRUE(bootstrap_response2.complete());
+  EXPECT_THAT(bootstrap_response2.result_error(), IsOk());
+  EXPECT_THAT(bootstrap_response2.request()->GetAddressResults(),
+              Optional(AddressesMatch(kBootstrapAddrs)));
+
+  // Run the followup query again.
+  RunUntilIdle();
+
+  // Confirm that the secure cache remains empty.
+  const auto* secure_result = resolve_context_->host_cache()->Lookup(
+      MakeCacheKey(/*secure=*/true), GetMockTickClock()->NowTicks());
+  EXPECT_THAT(secure_result, testing::IsNull());
+}
+
+TEST_F(HostResolverManagerBootstrapTest, OnlyBootstrapTwice) {
+  ConfigureMockDns(/*insecure_result=*/MockResult::kUnexpected,
+                   /*secure_result=*/MockResult::kOk);
+  dns_client_->set_preset_addrs(kBootstrapAddrs);
+
+  ResolveHostResponseHelper bootstrap_response1(resolver_->CreateRequest(
+      kEndpoint, kIsolationKey, NetLogWithSource(), bootstrap_params(),
+      resolve_context_.get(), resolve_context_->host_cache()));
+
+  EXPECT_TRUE(bootstrap_response1.complete());
+  EXPECT_THAT(bootstrap_response1.result_error(), IsOk());
+  EXPECT_THAT(bootstrap_response1.request()->GetAddressResults(),
+              Optional(AddressesMatch(kBootstrapAddrs)));
+
+  ResolveHostResponseHelper bootstrap_response2(resolver_->CreateRequest(
+      kEndpoint, kIsolationKey, NetLogWithSource(), bootstrap_params(),
+      resolve_context_.get(), resolve_context_->host_cache()));
+
+  EXPECT_TRUE(bootstrap_response2.complete());
+  EXPECT_THAT(bootstrap_response2.result_error(), IsOk());
+  EXPECT_THAT(bootstrap_response2.request()->GetAddressResults(),
+              Optional(AddressesMatch(kBootstrapAddrs)));
+
+  // Run the followup query.
+  RunUntilIdle();
+
+  // Confirm that the remote addresses are now in the secure cache.
+  const auto* secure_result = resolve_context_->host_cache()->Lookup(
+      MakeCacheKey(/*secure=*/true), GetMockTickClock()->NowTicks());
+  ASSERT_THAT(secure_result, testing::NotNull());
+  EXPECT_THAT(secure_result->second.addresses(),
+              Optional(AddressesMatch(kRemoteAddrs)));
+}
+
 }  // namespace net
diff --git a/net/dns/public/BUILD.gn b/net/dns/public/BUILD.gn
index b5169d8..92d865ed 100644
--- a/net/dns/public/BUILD.gn
+++ b/net/dns/public/BUILD.gn
@@ -37,10 +37,12 @@
     "util.h",
   ]
 
-  if (is_linux) {
+  if (is_posix && !is_android) {
     sources += [
       "resolv_reader.cc",
       "resolv_reader.h",
+      "scoped_res_state.cc",
+      "scoped_res_state.h",
     ]
   }
 
@@ -70,7 +72,7 @@
     "util_unittest.cc",
   ]
 
-  if (is_linux) {
+  if (is_posix && !is_android) {
     sources += [ "resolv_reader_unittest.cc" ]
   }
 
diff --git a/net/dns/public/resolv_reader.cc b/net/dns/public/resolv_reader.cc
index 5686762..438d470a 100644
--- a/net/dns/public/resolv_reader.cc
+++ b/net/dns/public/resolv_reader.cc
@@ -15,26 +15,19 @@
 
 #include "base/bind.h"
 #include "base/check_op.h"
+#include "build/build_config.h"
 #include "net/base/ip_endpoint.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace net {
 
-std::unique_ptr<struct __res_state> ResolvReader::GetResState() {
-  auto res = std::make_unique<struct __res_state>();
-  memset(res.get(), 0, sizeof(struct __res_state));
-
-  if (res_ninit(res.get()) != 0) {
-    CloseResState(res.get());
+std::unique_ptr<ScopedResState> ResolvReader::GetResState() {
+  auto res = std::make_unique<ScopedResState>();
+  if (!res->IsValid())
     return nullptr;
-  }
-
   return res;
 }
 
-void ResolvReader::CloseResState(struct __res_state* res) {
-  res_nclose(res);
-}
-
 absl::optional<std::vector<IPEndPoint>> GetNameservers(
     const struct __res_state& res) {
   std::vector<IPEndPoint> nameservers;
@@ -42,6 +35,21 @@
   if (!(res.options & RES_INIT))
     return absl::nullopt;
 
+#if defined(OS_APPLE) || defined(OS_FREEBSD)
+  union res_sockaddr_union addresses[MAXNS];
+  int nscount = res_getservers(const_cast<res_state>(&res), addresses, MAXNS);
+  DCHECK_GE(nscount, 0);
+  DCHECK_LE(nscount, MAXNS);
+  for (int i = 0; i < nscount; ++i) {
+    IPEndPoint ipe;
+    if (!ipe.FromSockAddr(
+            reinterpret_cast<const struct sockaddr*>(&addresses[i]),
+            sizeof addresses[i])) {
+      return absl::nullopt;
+    }
+    nameservers.push_back(ipe);
+  }
+#elif defined(OS_CHROMEOS) || defined(OS_LINUX)
   static_assert(std::extent<decltype(res.nsaddr_list)>() >= MAXNS &&
                     std::extent<decltype(res._u._ext.nsaddrs)>() >= MAXNS,
                 "incompatible libresolv res_state");
@@ -66,8 +74,21 @@
       return absl::nullopt;
     nameservers.push_back(ipe);
   }
+#else  // !(defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_APPLE) ||
+       //   defined(OS_FREEBSD))
+  DCHECK_LE(res.nscount, MAXNS);
+  for (int i = 0; i < res.nscount; ++i) {
+    IPEndPoint ipe;
+    if (!ipe.FromSockAddr(
+            reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]),
+            sizeof res.nsaddr_list[i])) {
+      return absl::nullopt;
+    }
+    nameservers.push_back(ipe);
+  }
+#endif
 
   return nameservers;
 }
 
-}  // namespace net
\ No newline at end of file
+}  // namespace net
diff --git a/net/dns/public/resolv_reader.h b/net/dns/public/resolv_reader.h
index 0f9d2ac..c64f8f66 100644
--- a/net/dns/public/resolv_reader.h
+++ b/net/dns/public/resolv_reader.h
@@ -12,6 +12,7 @@
 
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_export.h"
+#include "net/dns/public/scoped_res_state.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace net {
@@ -22,10 +23,8 @@
  public:
   virtual ~ResolvReader() = default;
 
-  // Null on failure. If not null, result must be cleaned up through a call
-  // to `CloseResState()`.
-  virtual std::unique_ptr<struct __res_state> GetResState();
-  virtual void CloseResState(struct __res_state* res);
+  // Null on failure.
+  virtual std::unique_ptr<ScopedResState> GetResState();
 };
 
 // Returns configured DNS servers or nullopt on failure.
@@ -34,4 +33,4 @@
 
 }  // namespace net
 
-#endif  // NET_DNS_PUBLIC_RESOLV_READER_H_
\ No newline at end of file
+#endif  // NET_DNS_PUBLIC_RESOLV_READER_H_
diff --git a/net/dns/public/resolv_reader_unittest.cc b/net/dns/public/resolv_reader_unittest.cc
index 7a07a2c..ef236b3 100644
--- a/net/dns/public/resolv_reader_unittest.cc
+++ b/net/dns/public/resolv_reader_unittest.cc
@@ -21,6 +21,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
+#include "build/build_config.h"
 #include "net/base/ip_address.h"
 #include "net/dns/public/dns_protocol.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -38,12 +39,14 @@
     "1.0.0.1",
 };
 
+#if defined(OS_LINUX)
 const char* const kNameserversIPv6[] = {
     nullptr,
-    "2001:DB8:0::42",
+    "2001:db8::42",
     nullptr,
     "::FFFF:129.144.52.38",
 };
+#endif
 
 // Fills in |res| with sane configuration.
 void InitializeResState(res_state res) {
@@ -59,6 +62,7 @@
     ++res->nscount;
   }
 
+#if defined(OS_LINUX)
   // Install IPv6 addresses, replacing the corresponding IPv4 addresses.
   unsigned nscount6 = 0;
   for (unsigned i = 0; i < base::size(kNameserversIPv6) && i < MAXNS; ++i) {
@@ -76,23 +80,39 @@
     ++nscount6;
   }
   res->_u._ext.nscount6 = nscount6;
+#endif
 }
 
 void FreeResState(struct __res_state* res) {
+#if defined(OS_LINUX)
   for (int i = 0; i < res->nscount; ++i) {
     if (res->_u._ext.nsaddrs[i] != nullptr)
       free(res->_u._ext.nsaddrs[i]);
   }
+#endif
 }
 
 TEST(ResolvReaderTest, GetNameservers) {
   auto res = std::make_unique<struct __res_state>();
   InitializeResState(res.get());
 
-  EXPECT_TRUE(GetNameservers(*res.get()).has_value());
+  absl::optional<std::vector<IPEndPoint>> nameservers =
+      GetNameservers(*res.get());
+  EXPECT_TRUE(nameservers.has_value());
+
+#if defined(OS_LINUX)
+  EXPECT_EQ(kNameserversIPv4[0], nameservers->at(0).ToStringWithoutPort());
+  EXPECT_EQ(kNameserversIPv6[1], nameservers->at(1).ToStringWithoutPort());
+  EXPECT_EQ(kNameserversIPv4[2], nameservers->at(2).ToStringWithoutPort());
+#else
+  EXPECT_EQ(kNameserversIPv4[0], nameservers->at(0).ToStringWithoutPort());
+  EXPECT_EQ(kNameserversIPv4[1], nameservers->at(1).ToStringWithoutPort());
+  EXPECT_EQ(kNameserversIPv4[2], nameservers->at(2).ToStringWithoutPort());
+#endif
+
   FreeResState(res.get());
 }
 
 }  // namespace
 
-}  // namespace net
\ No newline at end of file
+}  // namespace net
diff --git a/net/dns/public/scoped_res_state.cc b/net/dns/public/scoped_res_state.cc
new file mode 100644
index 0000000..0ecaab2
--- /dev/null
+++ b/net/dns/public/scoped_res_state.cc
@@ -0,0 +1,51 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/dns/public/scoped_res_state.h"
+
+#include <memory>
+
+#include "base/check.h"
+
+namespace net {
+
+ScopedResState::ScopedResState() {
+#if defined(OS_OPENBSD) || defined(OS_FUCHSIA)
+  // Note: res_ninit in glibc always returns 0 and sets RES_INIT.
+  // res_init behaves the same way.
+  memset(&_res, 0, sizeof(_res));
+  res_init_result_ = res_init();
+#else
+  memset(&res_, 0, sizeof(res_));
+  res_init_result_ = res_ninit(&res_);
+#endif  // defined(OS_OPENBSD) || defined(OS_FUCHSIA)
+}
+
+ScopedResState::~ScopedResState() {
+#if !defined(OS_OPENBSD) && !defined(OS_FUCHSIA)
+
+  // Prefer res_ndestroy where available.
+#if defined(OS_APPLE) || defined(OS_FREEBSD)
+  res_ndestroy(&res_);
+#else
+  res_nclose(&res_);
+#endif  // defined(OS_APPLE) || defined(OS_FREEBSD)
+
+#endif  // !defined(OS_OPENBSD) && !defined(OS_FUCHSIA)
+}
+
+bool ScopedResState::IsValid() const {
+  return res_init_result_ == 0;
+}
+
+const struct __res_state& ScopedResState::state() const {
+  DCHECK(IsValid());
+#if defined(OS_OPENBSD) || defined(OS_FUCHSIA)
+  return _res;
+#else
+  return res_;
+#endif  // defined(OS_OPENBSD) || defined(OS_FUCHSIA)
+}
+
+}  // namespace net
diff --git a/net/dns/public/scoped_res_state.h b/net/dns/public/scoped_res_state.h
new file mode 100644
index 0000000..81b9008
--- /dev/null
+++ b/net/dns/public/scoped_res_state.h
@@ -0,0 +1,43 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DNS_PUBLIC_SCOPED_RES_STATE_H_
+#define NET_DNS_PUBLIC_SCOPED_RES_STATE_H_
+
+#include <resolv.h>
+
+#include "build/build_config.h"
+#include "net/base/net_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace net {
+
+// Helper class to open, read and close a __res_state.
+class NET_EXPORT ScopedResState {
+ public:
+  // This constructor will call memset and res_init/res_ninit a __res_state, and
+  // store the result in `res_init_result_`.
+  ScopedResState();
+
+  // Calls res_ndestroy or res_nclose if the platform uses `res_`.
+  virtual ~ScopedResState();
+
+  // Returns true iff a __res_state was initialized successfully.
+  // Other methods in this class shouldn't be called if it returns false.
+  bool IsValid() const;
+
+  // Access the __res_state used by this class to compute other values.
+  virtual const struct __res_state& state() const;
+
+ private:
+#if !defined(OS_OPENBSD) && !defined(OS_FUCHSIA)
+  struct __res_state res_;
+#endif  // !defined(OS_OPENBSD) && !defined(OS_FUCHSIA)
+
+  int res_init_result_ = -1;
+};
+
+}  // namespace net
+
+#endif  // NET_DNS_PUBLIC_SCOPED_RES_STATE_H_
diff --git a/net/dns/public/secure_dns_policy.h b/net/dns/public/secure_dns_policy.h
index 12dc5be..7e95fef 100644
--- a/net/dns/public/secure_dns_policy.h
+++ b/net/dns/public/secure_dns_policy.h
@@ -14,6 +14,8 @@
   kAllow,
   // This request must not use Secure DNS, even when it is otherwise enabled.
   kDisable,
+  // This request is part of the Secure DNS bootstrap process.
+  kBootstrap,
 };
 
 }  // namespace net
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h
index 4b8c1130..47a2b926 100644
--- a/net/log/net_log_event_type_list.h
+++ b/net/log/net_log_event_type_list.h
@@ -87,6 +87,13 @@
 //   }
 EVENT_TYPE(HOST_RESOLVER_MANAGER_HOSTS_HIT)
 
+// This event is logged when a bootstrap request matches a config preset entry.
+// It contains the following parameter:
+//   {
+//     "results": <HostCache::Entry of results>,
+//   }
+EVENT_TYPE(HOST_RESOLVER_MANAGER_CONFIG_PRESET_MATCH)
+
 // This event is created when a new HostResolverManager::Job is about to be
 // created for a request.
 EVENT_TYPE(HOST_RESOLVER_MANAGER_CREATE_JOB)
diff --git a/net/socket/client_socket_pool.cc b/net/socket/client_socket_pool.cc
index cb9cd4c4..63bf2f8 100644
--- a/net/socket/client_socket_pool.cc
+++ b/net/socket/client_socket_pool.cc
@@ -124,6 +124,9 @@
     case SecureDnsPolicy::kDisable:
       result = "dsd/" + result;
       break;
+    case SecureDnsPolicy::kBootstrap:
+      result = "dns_bootstrap/" + result;
+      break;
   }
 
   return result;
diff --git a/pdf/pdfium/pdfium_form_filler.cc b/pdf/pdfium/pdfium_form_filler.cc
index 5855bc6..bc0e34d 100644
--- a/pdf/pdfium/pdfium_form_filler.cc
+++ b/pdf/pdfium/pdfium_form_filler.cc
@@ -9,6 +9,7 @@
 #include <string>
 #include <utility>
 
+#include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/check_op.h"
 #include "base/containers/contains.h"
@@ -309,6 +310,9 @@
   if (!engine->PageIndexInBounds(page_index))
     return;
 
+  base::AutoReset<bool> defer_page_unload_guard(&engine->defer_page_unload_,
+                                                true);
+
   // Maintain viewport if we are updating focus. This is to ensure that we don't
   // scroll the focused annotation into view when focus is regained.
   if (!engine->updating_focus_)
diff --git a/services/audio/output_device_mixer_impl.cc b/services/audio/output_device_mixer_impl.cc
index b332333..b936f8c 100644
--- a/services/audio/output_device_mixer_impl.cc
+++ b/services/audio/output_device_mixer_impl.cc
@@ -465,7 +465,7 @@
 
 OutputDeviceMixerImpl::~OutputDeviceMixerImpl() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
-  DCHECK(!active_tracks_.size());
+  DCHECK(active_tracks_.empty());
   DCHECK(!HasListeners());
   DCHECK(!mixing_graph_output_stream_);
 
@@ -561,7 +561,7 @@
       mixing_stats_->AddListener();
     }
   }
-  if (!mixing_graph_output_stream_ && active_tracks_.size()) {
+  if (!mixing_graph_output_stream_ && !active_tracks_.empty()) {
     // Start reference playback only if at least one audio stream is playing.
     for (MixTrack* mix_track : active_tracks_)
       mix_track->StopIndependentRenderingStream();
@@ -602,7 +602,7 @@
 
   // Mixing graph playback is ongoing.
 
-  if (!active_tracks_.size()) {
+  if (active_tracks_.empty()) {
     // There is no actual playback: we were just sending silence to the
     // listener as a reference.
     StopMixingGraphPlayback(MixingError::kNone);
@@ -671,18 +671,26 @@
   active_tracks_.erase(mix_track);
 
   if (mixing_graph_output_stream_) {
-    // We are playing all audio a |mixing_graph_| output.
+    // We are playing all audio as a |mixing_graph_| output.
     mix_track->StopProvidingAudioToMixingGraph();
     DCHECK(mixing_stats_);
     mixing_stats_->RemoveActiveTrack();
 
-    // Note: we do not stop the reference playback even if there are no active
-    // mix members. This way the echo canceller will be in a consistent state
-    // when the playback is activated again. Drawback: we keep playing silent
-    // audio. An example would some occasional notification sounds and no other
-    // audio output: we start the mixing playback at the first notification, and
-    // stop it only when the echo cancellation session is finished (i.e. all the
-    // listeners are gone).
+    if (!HasListeners() && active_tracks_.empty()) {
+      // All listeners are gone, which means a switch to an independent playback
+      // is scheduled. But since we have no active tracks, we can stop mixing
+      // immediately.
+      DCHECK(switch_to_unmixed_playback_delay_timer_.IsRunning());
+      StopMixingGraphPlayback(MixingError::kNone);
+    }
+
+    // Note: if there are listeners, we do not stop the reference playback even
+    // if there are no active mix members. This way the echo canceller will be
+    // in a consistent state when the playback is activated again. Drawback: we
+    // keep playing silent audio. An example would be some occasional
+    // notification sounds and no other audio output: we start the mixing
+    // playback at the first notification, and stop it only when the echo
+    // cancellation session is finished (i.e. all the listeners are gone).
   } else {
     mix_track->StopIndependentRenderingStream();
   }
diff --git a/services/audio/output_device_mixer_impl.h b/services/audio/output_device_mixer_impl.h
index 317efe8..367748d 100644
--- a/services/audio/output_device_mixer_impl.h
+++ b/services/audio/output_device_mixer_impl.h
@@ -159,7 +159,8 @@
 
   // Delays switching to unmixed playback in case a new listener is coming
   // soon (within kSwitchToIndependentPlaybackDelay).
-  base::OneShotTimer switch_to_unmixed_playback_delay_timer_;
+  base::OneShotTimer switch_to_unmixed_playback_delay_timer_
+      GUARDED_BY_CONTEXT(owning_sequence_);
 
   // Non-null when the playback is being mixed. Collects mixing statistics.
   // Logs them upon the destruction when mixing stops.
diff --git a/services/audio/output_device_mixer_impl_unittest.cc b/services/audio/output_device_mixer_impl_unittest.cc
index 03a02a45..bced4c9f 100644
--- a/services/audio/output_device_mixer_impl_unittest.cc
+++ b/services/audio/output_device_mixer_impl_unittest.cc
@@ -914,6 +914,30 @@
   VerifyAndClearAllExpectations();
 }
 
+TEST_F(OutputDeviceMixerImplTest,
+       DeleteMixer_WhileGraphOutputStreamStopIsDelayed) {
+  std::unique_ptr<OutputDeviceMixer> mixer = CreateMixerUnderTest();
+
+  MockListener listener;
+  mixer->StartListening(&listener);
+  VerifyAndClearAllExpectations();
+
+  auto stream_under_test = CreateNextStreamUnderTest(mixer.get());
+  OpenAndVerifyStreamUnderTest(stream_under_test);
+  StartAndVerifyStreamUnderTest(stream_under_test, PlaybackMode::kMixing);
+  mixer->StopListening(&listener);
+
+  // Since there are no listeners left, mixing playback must be stopped as soon
+  // as the stream is gone.
+  ExpectMixingGraphOutputStreamStopped();
+  StopAndVerifyStreamUnderTest(stream_under_test, PlaybackMode::kMixing);
+
+  CloseAndVerifyStreamUnderTest(stream_under_test);
+
+  mixer = nullptr;
+  VerifyAndClearAllExpectations();
+}
+
 TEST_P(OutputDeviceMixerImplTest, NStreamsMixing_OnMixingStreamError) {
   std::unique_ptr<OutputDeviceMixer> mixer = CreateMixerUnderTest();
 
diff --git a/services/network/public/cpp/client_hints.cc b/services/network/public/cpp/client_hints.cc
index 37269bb..eb60166 100644
--- a/services/network/public/cpp/client_hints.cc
+++ b/services/network/public/cpp/client_hints.cc
@@ -123,7 +123,17 @@
   return absl::make_optional(std::move(result));
 }
 
-absl::optional<ClientHintToDelegatedThirdPartiesMap>
+ClientHintToDelegatedThirdPartiesHeader::
+    ClientHintToDelegatedThirdPartiesHeader() = default;
+
+ClientHintToDelegatedThirdPartiesHeader::
+    ~ClientHintToDelegatedThirdPartiesHeader() = default;
+
+ClientHintToDelegatedThirdPartiesHeader::
+    ClientHintToDelegatedThirdPartiesHeader(
+        const ClientHintToDelegatedThirdPartiesHeader&) = default;
+
+absl::optional<const ClientHintToDelegatedThirdPartiesHeader>
 ParseClientHintToDelegatedThirdPartiesHeader(const std::string& header) {
   // Accept-CH is an sh-dictionary of tokens to origins; see:
   // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-header-structure-19#section-3.2
@@ -134,7 +144,7 @@
   if (!maybe_dictionary.has_value())
     return absl::nullopt;
 
-  ClientHintToDelegatedThirdPartiesMap result;
+  ClientHintToDelegatedThirdPartiesHeader result;
 
   // Now convert those to actual hint enums.
   const DecodeMap& decode_map = GetDecodeMap();
@@ -144,14 +154,21 @@
       if (!member.item.is_token())
         continue;
       const GURL maybe_gurl = GURL(member.item.GetString());
-      if (!maybe_gurl.is_valid())
+      if (!maybe_gurl.is_valid()) {
+        result.had_invalid_origins = true;
         continue;
-      delegates.push_back(url::Origin::Create(maybe_gurl));
+      }
+      url::Origin maybe_origin = url::Origin::Create(maybe_gurl);
+      if (maybe_origin.opaque()) {
+        result.had_invalid_origins = true;
+        continue;
+      }
+      delegates.push_back(maybe_origin);
     }
     const std::string& client_hint_string = dictionary_pair.first;
     auto iter = decode_map.find(client_hint_string);
     if (iter != decode_map.end())
-      result.insert(std::make_pair(iter->second, delegates));
+      result.map.insert(std::make_pair(iter->second, delegates));
   }  // for dictionary_pair
   return absl::make_optional(std::move(result));
 }
diff --git a/services/network/public/cpp/client_hints.h b/services/network/public/cpp/client_hints.h
index 0d65cbb1..dff644c 100644
--- a/services/network/public/cpp/client_hints.h
+++ b/services/network/public/cpp/client_hints.h
@@ -36,15 +36,22 @@
     COMPONENT_EXPORT(NETWORK_CPP)
         ParseClientHintsHeader(const std::string& header);
 
-using ClientHintToDelegatedThirdPartiesMap =
-    base::flat_map<network::mojom::WebClientHintsType,
-                   std::vector<url::Origin>>;
+struct COMPONENT_EXPORT(NETWORK_CPP) ClientHintToDelegatedThirdPartiesHeader {
+  ClientHintToDelegatedThirdPartiesHeader();
+  ~ClientHintToDelegatedThirdPartiesHeader();
+  ClientHintToDelegatedThirdPartiesHeader(
+      const ClientHintToDelegatedThirdPartiesHeader&);
+
+  base::flat_map<network::mojom::WebClientHintsType, std::vector<url::Origin>>
+      map;
+  bool had_invalid_origins{false};
+};
 
 // Tries to parse an Accept-CH header w/ third-party delegation ability (i.e. a
 // named meta tag). Returns absl::nullopt if parsing failed and the header
 // should be ignored; otherwise returns a (possibly empty) map of hints to
 // delegated third-parties.
-absl::optional<ClientHintToDelegatedThirdPartiesMap> COMPONENT_EXPORT(
+absl::optional<const ClientHintToDelegatedThirdPartiesHeader> COMPONENT_EXPORT(
     NETWORK_CPP)
     ParseClientHintToDelegatedThirdPartiesHeader(const std::string& header);
 
diff --git a/services/network/public/cpp/client_hints_unittest.cc b/services/network/public/cpp/client_hints_unittest.cc
index 1e603ac..8976014 100644
--- a/services/network/public/cpp/client_hints_unittest.cc
+++ b/services/network/public/cpp/client_hints_unittest.cc
@@ -62,24 +62,26 @@
 }
 
 TEST(ClientHintsTest, ParseClientHintToDelegatedThirdPartiesHeader) {
-  absl::optional<ClientHintToDelegatedThirdPartiesMap> result;
+  absl::optional<ClientHintToDelegatedThirdPartiesHeader> result;
 
   // Empty is OK.
   result = ParseClientHintToDelegatedThirdPartiesHeader(" ");
-  ASSERT_TRUE(result.has_value());
-  EXPECT_TRUE(result.value().empty());
+  EXPECT_TRUE(result.has_value());
+  EXPECT_TRUE(result.value().map.empty());
+  EXPECT_FALSE(result.value().had_invalid_origins);
 
   // Normal case.
   result = ParseClientHintToDelegatedThirdPartiesHeader("device-memory,  rtt ");
-  ASSERT_TRUE(result.has_value());
+  EXPECT_TRUE(result.has_value());
   EXPECT_THAT(
-      result.value(),
+      result.value().map,
       UnorderedElementsAre(
           std::make_pair(
               network::mojom::WebClientHintsType::kDeviceMemory_DEPRECATED,
               (std::vector<url::Origin>){}),
           std::make_pair(network::mojom::WebClientHintsType::kRtt_DEPRECATED,
                          (std::vector<url::Origin>){})));
+  EXPECT_FALSE(result.value().had_invalid_origins);
 
   // Must be a list of tokens, not other things.
   result = ParseClientHintToDelegatedThirdPartiesHeader(
@@ -90,48 +92,51 @@
   // spec.
   result = ParseClientHintToDelegatedThirdPartiesHeader(
       "device-memory;resolution=GIB, rtt");
-  ASSERT_TRUE(result.has_value());
+  EXPECT_TRUE(result.has_value());
   EXPECT_THAT(
-      result.value(),
+      result.value().map,
       UnorderedElementsAre(
           std::make_pair(
               network::mojom::WebClientHintsType::kDeviceMemory_DEPRECATED,
               (std::vector<url::Origin>){}),
           std::make_pair(network::mojom::WebClientHintsType::kRtt_DEPRECATED,
                          (std::vector<url::Origin>){})));
+  EXPECT_FALSE(result.value().had_invalid_origins);
 
   // Unknown tokens are fine, since this meant to be extensible.
   result = ParseClientHintToDelegatedThirdPartiesHeader(
       "device-memory,  rtt , nosuchtokenwhywhywhy");
-  ASSERT_TRUE(result.has_value());
+  EXPECT_TRUE(result.has_value());
   EXPECT_THAT(
-      result.value(),
+      result.value().map,
       UnorderedElementsAre(
           std::make_pair(
               network::mojom::WebClientHintsType::kDeviceMemory_DEPRECATED,
               (std::vector<url::Origin>){}),
           std::make_pair(network::mojom::WebClientHintsType::kRtt_DEPRECATED,
                          (std::vector<url::Origin>){})));
+  EXPECT_FALSE(result.value().had_invalid_origins);
 
   // Matching is case-insensitive.
   result = ParseClientHintToDelegatedThirdPartiesHeader("Device-meMory,  Rtt ");
-  ASSERT_TRUE(result.has_value());
+  EXPECT_TRUE(result.has_value());
   EXPECT_THAT(
-      result.value(),
+      result.value().map,
       UnorderedElementsAre(
           std::make_pair(
               network::mojom::WebClientHintsType::kDeviceMemory_DEPRECATED,
               (std::vector<url::Origin>){}),
           std::make_pair(network::mojom::WebClientHintsType::kRtt_DEPRECATED,
                          (std::vector<url::Origin>){})));
+  EXPECT_FALSE(result.value().had_invalid_origins);
 
   // Matching can find a one or more origins.
   result = ParseClientHintToDelegatedThirdPartiesHeader(
       "device-memory=https://foo.bar,  rtt=( https://foo.bar "
       "https://baz.qux) ");
-  ASSERT_TRUE(result.has_value());
+  EXPECT_TRUE(result.has_value());
   EXPECT_THAT(
-      result.value(),
+      result.value().map,
       UnorderedElementsAre(
           std::make_pair(
               network::mojom::WebClientHintsType::kDeviceMemory_DEPRECATED,
@@ -141,13 +146,14 @@
                          (std::vector<url::Origin>){
                              url::Origin::Create(GURL("https://foo.bar")),
                              url::Origin::Create(GURL("https://baz.qux"))})));
+  EXPECT_FALSE(result.value().had_invalid_origins);
 
   // Matching ignores invalid domains
   result = ParseClientHintToDelegatedThirdPartiesHeader(
-      "device-memory=6,  rtt=( https://foo.bar/wasd self ) ");
-  ASSERT_TRUE(result.has_value());
+      "device-memory=6,  rtt=( https://foo.bar/wasd self about:blank ) ");
+  EXPECT_TRUE(result.has_value());
   EXPECT_THAT(
-      result.value(),
+      result.value().map,
       UnorderedElementsAre(
           std::make_pair(
               network::mojom::WebClientHintsType::kDeviceMemory_DEPRECATED,
@@ -155,6 +161,7 @@
           std::make_pair(network::mojom::WebClientHintsType::kRtt_DEPRECATED,
                          (std::vector<url::Origin>){
                              url::Origin::Create(GURL("https://foo.bar"))})));
+  EXPECT_TRUE(result.value().had_invalid_origins);
 }
 
 }  // namespace network
diff --git a/services/network/public/cpp/host_resolver_mojom_traits.cc b/services/network/public/cpp/host_resolver_mojom_traits.cc
index 20a1643..8d79e4d 100644
--- a/services/network/public/cpp/host_resolver_mojom_traits.cc
+++ b/services/network/public/cpp/host_resolver_mojom_traits.cc
@@ -335,12 +335,16 @@
 // static
 network::mojom::SecureDnsPolicy
 EnumTraits<network::mojom::SecureDnsPolicy, net::SecureDnsPolicy>::ToMojom(
-    net::SecureDnsPolicy secure_dns_mode) {
-  switch (secure_dns_mode) {
+    net::SecureDnsPolicy secure_dns_policy) {
+  switch (secure_dns_policy) {
     case net::SecureDnsPolicy::kAllow:
       return network::mojom::SecureDnsPolicy::ALLOW;
     case net::SecureDnsPolicy::kDisable:
       return network::mojom::SecureDnsPolicy::DISABLE;
+    case net::SecureDnsPolicy::kBootstrap:
+      NOTREACHED();  // The bootstrap policy is only for use within the net
+                     // component.
+      return network::mojom::SecureDnsPolicy::DISABLE;
   }
 }
 
diff --git a/services/network/public/mojom/host_resolver.mojom b/services/network/public/mojom/host_resolver.mojom
index 4eface45..6c6c67d 100644
--- a/services/network/public/mojom/host_resolver.mojom
+++ b/services/network/public/mojom/host_resolver.mojom
@@ -39,6 +39,8 @@
 enum SecureDnsPolicy {
   ALLOW,
   DISABLE,
+  // The "bootstrap" policy is only used within the network process, so it is
+  // not exposed via Mojo.
 };
 
 // Overridable DNS configuration values for host resolution. All fields default
diff --git a/services/shape_detection/barcode_detection_impl_mac_unittest.mm b/services/shape_detection/barcode_detection_impl_mac_unittest.mm
index 62a57d72..9eb09c40 100644
--- a/services/shape_detection/barcode_detection_impl_mac_unittest.mm
+++ b/services/shape_detection/barcode_detection_impl_mac_unittest.mm
@@ -10,15 +10,13 @@
 #include <string>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/mac/mac_util.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_nsobject.h"
-#include "base/run_loop.h"
 #include "base/strings/sys_string_conversions.h"
-#include "base/test/gmock_callback_support.h"
 #include "base/test/task_environment.h"
+#include "base/test/test_future.h"
 #include "services/shape_detection/barcode_detection_impl_mac_vision.h"
 #include "services/shape_detection/public/mojom/barcodedetection.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -26,7 +24,6 @@
 #include "third_party/skia/include/utils/mac/SkCGUtils.h"
 #include "ui/gl/gl_switches.h"
 
-using base::test::RunOnceClosure;
 using ::testing::TestWithParam;
 using ::testing::ValuesIn;
 
@@ -57,24 +54,24 @@
 const std::string kInfoString = "https://www.chromium.org";
 
 struct TestParams {
-  size_t num_barcodes;
+  bool allow_duplicates;
   mojom::BarcodeFormat symbology;
   BarcodeDetectorFactory factory;
   NSString* test_code_generator;
 } kTestParams[] = {
     // CoreImage only supports QR Codes.
-    {1, mojom::BarcodeFormat::QR_CODE,
+    {false, mojom::BarcodeFormat::QR_CODE,
      base::BindRepeating(&CreateBarcodeDetectorImplMacCoreImage),
      @"CIQRCodeGenerator"},
     // Vision only supports a number of 1D/2D codes. Not all of them are
     // available for generation, though, only a few.
-    {1, mojom::BarcodeFormat::PDF417,
+    {false, mojom::BarcodeFormat::PDF417,
      base::BindRepeating(&CreateBarcodeDetectorImplMacVision),
      @"CIPDF417BarcodeGenerator"},
-    {1, mojom::BarcodeFormat::QR_CODE,
+    {false, mojom::BarcodeFormat::QR_CODE,
      base::BindRepeating(&CreateBarcodeDetectorImplMacVision),
      @"CIQRCodeGenerator"},
-    {6 /* 1D barcode makes the detector find the same code several times. */,
+    {true,  // 1D barcode makes the detector find the same code several times.
      mojom::BarcodeFormat::CODE_128,
      base::BindRepeating(&CreateBarcodeDetectorImplMacVision),
      @"CICode128BarcodeGenerator"}};
@@ -84,27 +81,14 @@
  public:
   ~BarcodeDetectionImplMacTest() override = default;
 
-  void DetectCallback(size_t num_barcodes,
-                      mojom::BarcodeFormat symbology,
-                      const std::string& barcode_value,
-                      std::vector<mojom::BarcodeDetectionResultPtr> results) {
-    EXPECT_EQ(num_barcodes, results.size());
-    for (const auto& barcode : results) {
-      EXPECT_EQ(barcode_value, barcode->raw_value);
-      EXPECT_EQ(symbology, barcode->format);
-    }
-
-    Detection();
-  }
-  MOCK_METHOD0(Detection, void(void));
-
-  std::unique_ptr<mojom::BarcodeDetection> impl_;
+ private:
   base::test::SingleThreadTaskEnvironment task_environment_;
 };
 
 TEST_P(BarcodeDetectionImplMacTest, CreateAndDestroy) {
-  impl_ = GetParam().factory.Run(mojom::BarcodeDetectorOptions::New());
-  if (!impl_) {
+  std::unique_ptr<mojom::BarcodeDetection> impl =
+      GetParam().factory.Run(mojom::BarcodeDetectorOptions::New());
+  if (!impl) {
     LOG(WARNING) << "Barcode Detection for this (library, OS version) pair is "
                     "not supported, skipping test.";
     return;
@@ -119,7 +103,8 @@
     return;
   }
 
-  impl_ = GetParam().factory.Run(mojom::BarcodeDetectorOptions::New());
+  std::unique_ptr<mojom::BarcodeDetection> impl =
+      GetParam().factory.Run(mojom::BarcodeDetectorOptions::New());
 
   // Generate a barcode image as a CIImage by using |qr_code_generator|.
   NSData* const qr_code_data = [base::SysUTF8ToNSString(kInfoString)
@@ -144,17 +129,19 @@
   SkBitmap bitmap;
   ASSERT_TRUE(SkCreateBitmapFromCGImage(&bitmap, cg_image));
 
-  base::RunLoop run_loop;
-  // Send the image Detect() and expect the response in callback.
-  EXPECT_CALL(*this, Detection())
-      .WillOnce(RunOnceClosure(run_loop.QuitClosure()));
-  // TODO(crbug.com/938663): expect detected symbology.
-  impl_->Detect(bitmap,
-                base::BindOnce(&BarcodeDetectionImplMacTest::DetectCallback,
-                               base::Unretained(this), GetParam().num_barcodes,
-                               GetParam().symbology, kInfoString));
+  base::test::TestFuture<std::vector<mojom::BarcodeDetectionResultPtr>> future;
+  impl->Detect(bitmap, future.GetCallback());
 
-  run_loop.Run();
+  auto results = future.Take();
+  if (GetParam().allow_duplicates) {
+    EXPECT_GE(results.size(), 1u);
+  } else {
+    EXPECT_EQ(results.size(), 1u);
+  }
+  for (const auto& barcode : results) {
+    EXPECT_EQ(kInfoString, barcode->raw_value);
+    EXPECT_EQ(GetParam().symbology, barcode->format);
+  }
 }
 
 INSTANTIATE_TEST_SUITE_P(, BarcodeDetectionImplMacTest, ValuesIn(kTestParams));
diff --git a/services/video_capture/public/mojom/video_source.mojom b/services/video_capture/public/mojom/video_source.mojom
index 7033ccb..90a1662 100644
--- a/services/video_capture/public/mojom/video_source.mojom
+++ b/services/video_capture/public/mojom/video_source.mojom
@@ -8,10 +8,14 @@
 import "media/capture/mojom/video_capture_types.mojom";
 import "services/video_capture/public/mojom/video_frame_handler.mojom";
 
-enum CreatePushSubscriptionResultCode {
+enum CreatePushSubscriptionSuccessCode {
   kCreatedWithRequestedSettings,
   kCreatedWithDifferentSettings,
-  kFailed,
+};
+
+union CreatePushSubscriptionResultCode{
+  CreatePushSubscriptionSuccessCode success_code;
+  media.mojom.VideoCaptureError error_code;
 };
 
 // In a PushVideoStreamSubscription, the provider pushes frames to the
diff --git a/services/video_capture/push_video_stream_subscription_impl.cc b/services/video_capture/push_video_stream_subscription_impl.cc
index 2f1a85c..b817179 100644
--- a/services/video_capture/push_video_stream_subscription_impl.cc
+++ b/services/video_capture/push_video_stream_subscription_impl.cc
@@ -46,13 +46,16 @@
     // Creation callback has already been run from a previous device start.
     return;
   }
-  mojom::CreatePushSubscriptionResultCode result_code =
+  mojom::CreatePushSubscriptionSuccessCode success_code =
       settings == requested_settings_
-          ? mojom::CreatePushSubscriptionResultCode::
+          ? mojom::CreatePushSubscriptionSuccessCode::
                 kCreatedWithRequestedSettings
-          : mojom::CreatePushSubscriptionResultCode::
+          : mojom::CreatePushSubscriptionSuccessCode::
                 kCreatedWithDifferentSettings;
-  std::move(creation_callback_).Run(result_code, settings);
+  std::move(creation_callback_)
+      .Run(
+          mojom::CreatePushSubscriptionResultCode::NewSuccessCode(success_code),
+          settings);
   status_ = Status::kNotYetActivated;
 }
 
@@ -65,7 +68,7 @@
     return;
   }
   std::move(creation_callback_)
-      .Run(mojom::CreatePushSubscriptionResultCode::kFailed,
+      .Run(mojom::CreatePushSubscriptionResultCode::NewErrorCode(error),
            requested_settings_);
   status_ = Status::kClosed;
 }
diff --git a/services/video_capture/test/mock_device_shared_access_unittest.cc b/services/video_capture/test/mock_device_shared_access_unittest.cc
index a5a64c1..b583151 100644
--- a/services/video_capture/test/mock_device_shared_access_unittest.cc
+++ b/services/video_capture/test/mock_device_shared_access_unittest.cc
@@ -95,12 +95,12 @@
         base::BindOnce(
             [](base::RunLoop* run_loop,
                media::VideoCaptureParams* requested_settings,
-               mojom::CreatePushSubscriptionResultCode result_code,
+               mojom::CreatePushSubscriptionResultCodePtr result_code,
                const media::VideoCaptureParams&
                    settings_source_was_opened_with) {
-              ASSERT_EQ(mojom::CreatePushSubscriptionResultCode::
+              ASSERT_EQ(mojom::CreatePushSubscriptionSuccessCode::
                             kCreatedWithRequestedSettings,
-                        result_code);
+                        result_code->get_success_code());
               ASSERT_EQ(*requested_settings, settings_source_was_opened_with);
               run_loop->Quit();
             },
@@ -111,12 +111,14 @@
   void LetClient2ConnectWithRequestableSettingsAndExpectToGetThem() {
     LetClient2ConnectWithRequestableSettings(
         false /*force_reopen_with_new_settings*/,
-        mojom::CreatePushSubscriptionResultCode::kCreatedWithRequestedSettings);
+        mojom::CreatePushSubscriptionResultCode::NewSuccessCode(
+            mojom::CreatePushSubscriptionSuccessCode::
+                kCreatedWithRequestedSettings));
   }
 
   void LetClient2ConnectWithRequestableSettings(
       bool force_reopen_with_new_settings,
-      mojom::CreatePushSubscriptionResultCode expected_result_code) {
+      mojom::CreatePushSubscriptionResultCodePtr expected_result_code) {
     base::RunLoop run_loop;
     source_->CreatePushSubscription(
         std::move(video_frame_handler_2_), requestable_settings_,
@@ -125,25 +127,29 @@
         base::BindOnce(
             [](base::RunLoop* run_loop,
                media::VideoCaptureParams* requested_settings,
-               mojom::CreatePushSubscriptionResultCode expected_result_code,
-               mojom::CreatePushSubscriptionResultCode result_code,
+               mojom::CreatePushSubscriptionResultCodePtr expected_result_code,
+               mojom::CreatePushSubscriptionResultCodePtr result_code,
                const media::VideoCaptureParams&
                    settings_source_was_opened_with) {
               ASSERT_EQ(expected_result_code, result_code);
-              if (expected_result_code ==
-                  mojom::CreatePushSubscriptionResultCode::
-                      kCreatedWithRequestedSettings) {
-                ASSERT_EQ(*requested_settings, settings_source_was_opened_with);
-              }
-              if (expected_result_code ==
-                  mojom::CreatePushSubscriptionResultCode::
-                      kCreatedWithDifferentSettings) {
-                ASSERT_FALSE(*requested_settings ==
-                             settings_source_was_opened_with);
+              if (expected_result_code->is_success_code()) {
+                mojom::CreatePushSubscriptionSuccessCode success_code =
+                    expected_result_code->get_success_code();
+                if (success_code == mojom::CreatePushSubscriptionSuccessCode::
+                                        kCreatedWithRequestedSettings) {
+                  ASSERT_EQ(*requested_settings,
+                            settings_source_was_opened_with);
+                }
+                if (success_code == mojom::CreatePushSubscriptionSuccessCode::
+                                        kCreatedWithDifferentSettings) {
+                  ASSERT_FALSE(*requested_settings ==
+                               settings_source_was_opened_with);
+                }
               }
               run_loop->Quit();
             },
-            &run_loop, &requestable_settings_, expected_result_code));
+            &run_loop, &requestable_settings_,
+            std::move(expected_result_code)));
     run_loop.Run();
   }
 
@@ -162,12 +168,12 @@
         base::BindOnce(
             [](base::RunLoop* run_loop_1,
                media::VideoCaptureParams* requested_settings,
-               mojom::CreatePushSubscriptionResultCode result_code,
+               mojom::CreatePushSubscriptionResultCodePtr result_code,
                const media::VideoCaptureParams&
                    settings_source_was_opened_with) {
-              ASSERT_EQ(mojom::CreatePushSubscriptionResultCode::
+              ASSERT_EQ(mojom::CreatePushSubscriptionSuccessCode::
                             kCreatedWithRequestedSettings,
-                        result_code);
+                        result_code->get_success_code());
               ASSERT_EQ(*requested_settings, settings_source_was_opened_with);
               run_loop_1->Quit();
             },
@@ -185,12 +191,12 @@
         base::BindOnce(
             [](base::RunLoop* run_loop_2,
                media::VideoCaptureParams* requested_settings,
-               mojom::CreatePushSubscriptionResultCode result_code,
+               mojom::CreatePushSubscriptionResultCodePtr result_code,
                const media::VideoCaptureParams&
                    settings_source_was_opened_with) {
-              ASSERT_EQ(mojom::CreatePushSubscriptionResultCode::
+              ASSERT_EQ(mojom::CreatePushSubscriptionSuccessCode::
                             kCreatedWithDifferentSettings,
-                        result_code);
+                        result_code->get_success_code());
               ASSERT_EQ(*requested_settings, settings_source_was_opened_with);
               run_loop_2->Quit();
             },
@@ -303,7 +309,9 @@
 
   LetClient2ConnectWithRequestableSettings(
       true /*force_reopen_with_new_settings*/,
-      mojom::CreatePushSubscriptionResultCode::kCreatedWithRequestedSettings);
+      mojom::CreatePushSubscriptionResultCode::NewSuccessCode(
+          mojom::CreatePushSubscriptionSuccessCode::
+              kCreatedWithRequestedSettings));
   subscription_2_->Activate();
   SendFrameAndExpectToArriveAtBothSubscribers();
 }
@@ -313,7 +321,9 @@
   LetClient1ConnectWithRequestableSettingsAndExpectToGetThem();
   LetClient2ConnectWithRequestableSettings(
       true /*force_reopen_with_new_settings*/,
-      mojom::CreatePushSubscriptionResultCode::kCreatedWithRequestedSettings);
+      mojom::CreatePushSubscriptionResultCode::NewSuccessCode(
+          mojom::CreatePushSubscriptionSuccessCode::
+              kCreatedWithRequestedSettings));
   subscription_1_->Activate();
   subscription_2_->Activate();
   SendFrameAndExpectToArriveAtBothSubscribers();
@@ -347,7 +357,9 @@
 
   LetClient2ConnectWithRequestableSettings(
       true /*force_reopen_with_new_settings*/,
-      mojom::CreatePushSubscriptionResultCode::kCreatedWithRequestedSettings);
+      mojom::CreatePushSubscriptionResultCode::NewSuccessCode(
+          mojom::CreatePushSubscriptionSuccessCode::
+              kCreatedWithRequestedSettings));
   subscription_2_->Activate();
 
   mock_device_.SendOnStarted();
@@ -362,7 +374,9 @@
 
   LetClient2ConnectWithRequestableSettings(
       false /*force_reopen_with_new_settings*/,
-      mojom::CreatePushSubscriptionResultCode::kFailed);
+      mojom::CreatePushSubscriptionResultCode::NewErrorCode(
+          media::VideoCaptureError::
+              kVideoCaptureControllerInvalidOrUnsupportedVideoCaptureParametersRequested));
 }
 
 TEST_F(MockVideoCaptureDeviceSharedAccessTest,
diff --git a/storage/browser/quota/quota_manager_impl.cc b/storage/browser/quota/quota_manager_impl.cc
index 13262569..a771255d 100644
--- a/storage/browser/quota/quota_manager_impl.cc
+++ b/storage/browser/quota/quota_manager_impl.cc
@@ -283,6 +283,7 @@
 constexpr int64_t QuotaManagerImpl::kPerHostPersistentQuotaLimit;
 constexpr int QuotaManagerImpl::kEvictionIntervalInMilliSeconds;
 constexpr int QuotaManagerImpl::kThresholdOfErrorsToBeDenylisted;
+constexpr int QuotaManagerImpl::kThresholdOfErrorsToDisableDatabase;
 constexpr int QuotaManagerImpl::kThresholdRandomizationPercent;
 constexpr char QuotaManagerImpl::kDatabaseName[];
 constexpr char QuotaManagerImpl::kEvictedBucketAccessedCountHistogram[];
@@ -1098,6 +1099,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   EnsureDatabaseOpened();
 
+  if (db_disabled_) {
+    std::move(callback).Run(QuotaError::kDatabaseError);
+    return;
+  }
   PostTaskAndReplyWithResultForDBThread(
       base::BindOnce(&GetOrCreateBucketOnDBThread, storage_key, bucket_name),
       base::BindOnce(&QuotaManagerImpl::DidGetBucket,
@@ -1112,6 +1117,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   EnsureDatabaseOpened();
 
+  if (db_disabled_) {
+    std::move(callback).Run(QuotaError::kDatabaseError);
+    return;
+  }
   PostTaskAndReplyWithResultForDBThread(
       base::BindOnce(&CreateBucketOnDBThread, storage_key, bucket_name,
                      storage_type),
@@ -1127,6 +1136,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   EnsureDatabaseOpened();
 
+  if (db_disabled_) {
+    std::move(callback).Run(QuotaError::kDatabaseError);
+    return;
+  }
   PostTaskAndReplyWithResultForDBThread(
       base::BindOnce(&GetBucketOnDBThread, storage_key, bucket_name, type),
       base::BindOnce(&QuotaManagerImpl::DidGetBucket,
@@ -1138,6 +1151,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   EnsureDatabaseOpened();
 
+  if (db_disabled_) {
+    std::move(callback).Run(std::set<StorageKey>());
+    return;
+  }
   PostTaskAndReplyWithResultForDBThread(
       base::BindOnce(&GetStorageKeysForTypeOnDBThread, type),
       base::BindOnce(&QuotaManagerImpl::DidGetStorageKeys,
@@ -1150,6 +1167,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   EnsureDatabaseOpened();
 
+  if (db_disabled_) {
+    std::move(callback).Run(QuotaError::kDatabaseError);
+    return;
+  }
   PostTaskAndReplyWithResultForDBThread(
       base::BindOnce(&GetBucketsForTypeOnDBThread, type),
       base::BindOnce(&QuotaManagerImpl::DidGetBuckets,
@@ -1163,6 +1184,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   EnsureDatabaseOpened();
 
+  if (db_disabled_) {
+    std::move(callback).Run(QuotaError::kDatabaseError);
+    return;
+  }
   PostTaskAndReplyWithResultForDBThread(
       base::BindOnce(&GetBucketsForHostOnDBThread, host, type),
       base::BindOnce(&QuotaManagerImpl::DidGetBuckets,
@@ -1176,6 +1201,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   EnsureDatabaseOpened();
 
+  if (db_disabled_) {
+    std::move(callback).Run(QuotaError::kDatabaseError);
+    return;
+  }
   PostTaskAndReplyWithResultForDBThread(
       base::BindOnce(&GetBucketsForStorageKeyOnDBThread, storage_key, type),
       base::BindOnce(&QuotaManagerImpl::DidGetBuckets,
@@ -1322,6 +1351,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   EnsureDatabaseOpened();
 
+  if (db_disabled_) {
+    std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorInvalidAccess);
+    return;
+  }
   PostTaskAndReplyWithResultForDBThread(
       base::BindOnce(&GetBucketOnDBThread, storage_key, bucket_name,
                      StorageType::kTemporary),
@@ -1421,7 +1454,6 @@
                             -1);
     return;
   }
-
   int64_t* new_quota_ptr = new int64_t(new_quota);
   PostTaskAndReplyWithResultForDBThread(
       base::BindOnce(&SetPersistentHostQuotaOnDBThread, host,
@@ -1483,6 +1515,11 @@
                                                  GetBucketsCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   EnsureDatabaseOpened();
+
+  if (db_disabled_) {
+    std::move(callback).Run(std::set<BucketLocator>(), type);
+    return;
+  }
   PostTaskAndReplyWithResultForDBThread(
       base::BindOnce(&GetModifiedBetweenOnDBThread, type, begin, end),
       base::BindOnce(&QuotaManagerImpl::DidGetModifiedBetween,
@@ -1719,6 +1756,10 @@
 
 void QuotaManagerImpl::DumpQuotaTable(DumpQuotaTableCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (db_disabled_) {
+    std::move(callback).Run(QuotaTableEntries());
+    return;
+  }
   DumpQuotaTableHelper* helper = new DumpQuotaTableHelper;
   PostTaskAndReplyWithResultForDBThread(
       base::BindOnce(&DumpQuotaTableHelper::DumpQuotaTableOnDBThread,
@@ -1730,6 +1771,10 @@
 
 void QuotaManagerImpl::DumpBucketTable(DumpBucketTableCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (db_disabled_) {
+    std::move(callback).Run(BucketTableEntries());
+    return;
+  }
   DumpBucketTableHelper* helper = new DumpBucketTableHelper;
   PostTaskAndReplyWithResultForDBThread(
       base::BindOnce(&DumpBucketTableHelper::DumpBucketTableOnDBThread,
@@ -1799,6 +1844,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   EnsureDatabaseOpened();
 
+  if (db_disabled_) {
+    std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorInvalidAccess);
+    return;
+  }
   BucketDataDeleter* deleter =
       new BucketDataDeleter(this, bucket, std::move(quota_client_types),
                             is_eviction, std::move(callback));
@@ -2240,7 +2289,12 @@
 
 void QuotaManagerImpl::DidDatabaseWork(bool success) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  db_disabled_ = !success;
+  if (success)
+    return;
+
+  db_error_count_++;
+  if (db_error_count_ >= QuotaManagerImpl::kThresholdOfErrorsToDisableDatabase)
+    db_disabled_ = true;
 }
 
 void QuotaManagerImpl::OnComplete(QuotaError result) {
diff --git a/storage/browser/quota/quota_manager_impl.h b/storage/browser/quota/quota_manager_impl.h
index 4bda4d8..69fd615 100644
--- a/storage/browser/quota/quota_manager_impl.h
+++ b/storage/browser/quota/quota_manager_impl.h
@@ -395,6 +395,7 @@
   static constexpr int kEvictionIntervalInMilliSeconds =
       30 * kMinutesInMilliSeconds;
   static constexpr int kThresholdOfErrorsToBeDenylisted = 3;
+  static constexpr int kThresholdOfErrorsToDisableDatabase = 3;
   static constexpr int kThresholdRandomizationPercent = 5;
 
   static constexpr char kDatabaseName[] = "QuotaManager";
@@ -632,6 +633,7 @@
   // points to never changes), and the underlying object is thread-safe.
   const scoped_refptr<QuotaManagerProxy> proxy_;
 
+  int db_error_count_ = 0;
   bool db_disabled_ = false;
   bool eviction_disabled_ = false;
   absl::optional<blink::StorageKey>
diff --git a/storage/browser/quota/quota_manager_unittest.cc b/storage/browser/quota/quota_manager_unittest.cc
index 113ce82..6872a4c 100644
--- a/storage/browser/quota/quota_manager_unittest.cc
+++ b/storage/browser/quota/quota_manager_unittest.cc
@@ -746,6 +746,31 @@
                                 UsageInfo("example.com", kPerm, 40)));
 }
 
+TEST_F(QuotaManagerImplTest, DatabaseDisabledAfterThreshold) {
+  OpenDatabase();
+
+  // Disable quota database for database error behavior.
+  disable_quota_database(true);
+
+  ASSERT_FALSE(is_db_disabled());
+
+  StorageKey storage_key = ToStorageKey("http://a.com/");
+  std::string bucket_name = "bucket_a";
+
+  GetOrCreateBucket(storage_key, bucket_name);
+  ASSERT_FALSE(bucket_.ok());
+  ASSERT_FALSE(is_db_disabled());
+
+  GetOrCreateBucket(storage_key, bucket_name);
+  ASSERT_FALSE(bucket_.ok());
+  ASSERT_FALSE(is_db_disabled());
+
+  // Disables access to QuotaDatabase after error counts passes threshold.
+  GetBucket(storage_key, bucket_name, kTemp);
+  ASSERT_FALSE(bucket_.ok());
+  ASSERT_TRUE(is_db_disabled());
+}
+
 TEST_F(QuotaManagerImplTest, GetOrCreateBucket) {
   StorageKey storage_key = ToStorageKey("http://a.com/");
   std::string bucket_name = "bucket_a";
diff --git a/styleguide/c++/c++11.md b/styleguide/c++/c++11.md
index 40f912f..573b626 100644
--- a/styleguide/c++/c++11.md
+++ b/styleguide/c++/c++11.md
@@ -29,7 +29,7 @@
 
 *   **C++11:** _Default allowed; see banned features below_
 *   **C++14:** _Default allowed; see banned features below_
-*   **C++17:** _Not yet supported in Chromium, unlikely before mid-2021;
+*   **C++17:** _Not yet supported in Chromium, unlikely before early-2022;
     [tracking bug](https://crbug.com/752720)_
 *   **C++20:** _Not yet standardized_
 *   **Abseil:** Initially supported July 31, 2020; see allowed/banned/TBD
@@ -43,7 +43,7 @@
 
 The following C++11 language features are not allowed in the Chromium codebase.
 
-### Inline Namespaces
+### Inline Namespaces <sup>[banned]</sup>
 
 ```c++
 inline namespace foo { ... }
@@ -61,7 +61,7 @@
 Unclear how it will work with components.
 ***
 
-### long long Type
+### long long Type <sup>[banned]</sup>
 
 ```c++
 long long var = value;
@@ -78,7 +78,7 @@
 [Discussion thread](https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/RxugZ-pIDxk)
 ***
 
-### User-Defined Literals
+### User-Defined Literals <sup>[banned]</sup>
 
 ```c++
 type var = literal_value_type;
@@ -95,7 +95,7 @@
 [Google Style Guide](https://google.github.io/styleguide/cppguide.html#Operator_Overloading).
 ***
 
-### thread_local Storage Class
+### thread_local Storage Class <sup>[banned]</sup>
 
 ```c++
 thread_local int foo = 1;
@@ -119,7 +119,7 @@
 
 The following C++11 library features are not allowed in the Chromium codebase.
 
-### Bind Operations
+### Bind Operations <sup>[banned]</sup>
 
 ```c++
 std::bind(function, args, ...)
@@ -138,7 +138,7 @@
 [Discussion thread](https://groups.google.com/a/chromium.org/forum/#!topic/cxx/SoEj7oIDNuA)
 ***
 
-### C Floating-Point Environment
+### C Floating-Point Environment <sup>[banned]</sup>
 
 ```c++
 #include <cfenv>
@@ -158,7 +158,7 @@
 due to concerns about compiler support.
 ***
 
-### Date and time utilities
+### Date and time utilities <sup>[banned]</sup>
 
 ```c++
 #include <chrono>
@@ -174,7 +174,7 @@
 Overlaps with `Time` APIs in `base/`. Keep using the `base/` classes.
 ***
 
-### Exceptions
+### Exceptions <sup>[banned]</sup>
 
 ```c++
 #include <exception>
@@ -194,7 +194,7 @@
 [Discussion thread](https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/8i4tMqNpHhg)
 ***
 
-### Function Objects
+### Function Objects <sup>[banned]</sup>
 
 ```c++
 std::function
@@ -213,7 +213,7 @@
 [Discussion thread](https://groups.google.com/a/chromium.org/forum/#!topic/cxx/SoEj7oIDNuA)
 ***
 
-### Random Number Engines
+### Random Number Engines <sup>[banned]</sup>
 
 *** aside
 The random number engines defined in `<random>` (see separate item for random
@@ -234,7 +234,7 @@
 [Discussion thread](https://groups.google.com/a/chromium.org/forum/#!topic/cxx/16Xmw05C-Y0)
 ***
 
-### Ratio Template Class
+### Ratio Template Class <sup>[banned]</sup>
 
 ```c++
 std::ratio<numerator, denominator>
@@ -252,7 +252,7 @@
 due to concerns that this is tied to a more template-heavy interface style.
 ***
 
-### Regular Expressions
+### Regular Expressions <sup>[banned]</sup>
 
 ```c++
 #include <regex>
@@ -269,7 +269,7 @@
 `re2`.
 ***
 
-### Shared Pointers
+### Shared Pointers <sup>[banned]</sup>
 
 ```c++
 std::shared_ptr
@@ -289,7 +289,7 @@
 *   [Discussion Thread](https://groups.google.com/a/chromium.org/forum/#!topic/cxx/aT2wsBLKvzI)
 ***
 
-### String-Number Conversion Functions
+### String-Number Conversion Functions <sup>[banned]</sup>
 
 ```c++
 std::stoi()
@@ -319,7 +319,7 @@
 instead.
 ***
 
-### Thread Library
+### Thread Library <sup>[banned]</sup>
 
 *** aside
 `<thread>` and related headers, including `<future>`, `<mutex>`,
@@ -340,7 +340,7 @@
 replace our locking/synchronization classes.
 ***
 
-### Weak Pointers
+### Weak Pointers <sup>[banned]</sup>
 
 ```c++
 std::weak_ptr
@@ -360,7 +360,7 @@
 
 The following C++14 library features are not allowed in the Chromium codebase.
 
-### std::chrono literals
+### std::chrono literals <sup>[banned]</sup>
 
 ```c++
 using namespace std::chrono_literals;
@@ -381,7 +381,7 @@
 
 The following Abseil library features are allowed in the Chromium codebase.
 
-### Optional
+### Optional <sup>[allowed]</sup>
 
 ```c++
 absl::optional
@@ -398,7 +398,7 @@
 [Discussion thread](https://groups.google.com/a/chromium.org/g/cxx/c/zUGqagX1NFU)
 ***
 
-### Status
+### Status <sup>[allowed]</sup>
 
 ```c++
 absl::Status
@@ -425,7 +425,7 @@
 [Discussion thread](https://groups.google.com/a/chromium.org/g/cxx/c/ImdFCSZ-NMA)
 ***
 
-### Variant
+### Variant <sup>[allowed]</sup>
 
 ```c++
 absl::variant
@@ -445,7 +445,7 @@
 
 The following Abseil library features are not allowed in the Chromium codebase.
 
-### Any
+### Any <sup>[banned]</sup>
 
 ```c++
 absl::any a = int{5};
@@ -463,7 +463,7 @@
 build. ([Bug](https://crbug.com/1096380))
 ***
 
-### Command line flags
+### Command line flags <sup>[banned]</sup>
 
 ```c++
 ABSL_FLAG(bool, logs, false, "print logs to stderr");
@@ -481,7 +481,7 @@
 build. ([Bug](https://crbug.com/1096380)) Use `base::CommandLine` instead.
 ***
 
-### Span
+### Span <sup>[banned]</sup>
 
 ```c++
 absl::Span
@@ -497,7 +497,7 @@
 `base::span`.
 ***
 
-### string_view
+### string_view <sup>[banned]</sup>
 
 ```c++
 absl::string_view
@@ -519,7 +519,7 @@
 See the top of this page on how to propose moving a feature from this list into
 the allowed or banned sections.
 
-### 128bit integer
+### 128bit integer <sup>[tbd]</sup>
 
 ```c++
 uint64_t a;
@@ -537,7 +537,7 @@
 None
 ***
 
-### bind_front
+### bind_front <sup>[tbd]</sup>
 
 ```c++
 absl::bind_front
@@ -554,7 +554,7 @@
 Overlaps with `base::Bind`.
 ***
 
-### Cleanup
+### Cleanup <sup>[tbd]</sup>
 
 ```c++
 FILE* sink_file = fopen(sink_path, "w");
@@ -572,7 +572,7 @@
 Similar to `defer` in Golang.
 ***
 
-### Containers
+### Containers <sup>[tbd]</sup>
 
 ```c++
 absl::flat_hash_map
@@ -599,7 +599,7 @@
 Supplements `base/containers/`.
 ***
 
-### Container utilities
+### Container utilities <sup>[tbd]</sup>
 
 ```c++
 auto it = absl::c_find(container, value);
@@ -616,7 +616,7 @@
 Overlaps with `base/ranges/algorithm.h`.
 ***
 
-### FunctionRef
+### FunctionRef <sup>[tbd]</sup>
 
 ```c++
 absl::FunctionRef
@@ -633,7 +633,7 @@
 None
 ***
 
-### Random
+### Random <sup>[tbd]</sup>
 
 ```c++
 absl::BitGen bitgen;
@@ -649,7 +649,7 @@
 Overlaps with `base/rand_util.h`.
 ***
 
-### StatusOr
+### StatusOr <sup>[tbd]</sup>
 
 ```c++
 absl::StatusOr<T>
@@ -666,7 +666,7 @@
 None
 ***
 
-### String Formatting
+### String Formatting <sup>[tbd]</sup>
 
 ```c++
 absl::StrFormat
@@ -683,7 +683,7 @@
 None
 ***
 
-### Strings Library
+### Strings Library <sup>[tbd]</sup>
 
 ```c++
 absl::StrSplit
@@ -705,7 +705,7 @@
 Overlaps with `base/strings`.
 ***
 
-### Synchronization
+### Synchronization <sup>[tbd]</sup>
 
 ```c++
 absl::Mutex
@@ -721,7 +721,7 @@
 Overlaps with `Lock` in `base/synchronization/`.
 ***
 
-### Time library
+### Time library <sup>[tbd]</sup>
 
 ```c++
 absl::Duration
diff --git a/styleguide/styleguide.md b/styleguide/styleguide.md
index 139c2b7a..7949285 100644
--- a/styleguide/styleguide.md
+++ b/styleguide/styleguide.md
@@ -3,6 +3,7 @@
 ## Main style guides
 
 *   [Chromium C++ style guide](c++/c++.md)
+    *   [Modern C++ use](c++/c++11.md) for allowed/banned features.
     *   See also: [C++ Dos and Don'ts](c++/c++-dos-and-donts.md) for Chromium
         best-practices.
 *   [Chromium Objective-C style guide](objective-c/objective-c.md)
diff --git a/testing/buildbot/chromium.dawn.json b/testing/buildbot/chromium.dawn.json
index 67d2e85..492730c9 100644
--- a/testing/buildbot/chromium.dawn.json
+++ b/testing/buildbot/chromium.dawn.json
@@ -1573,7 +1573,7 @@
               "display_attached": "1",
               "gpu": "1002:6821",
               "hidpi": "1",
-              "os": "Mac-11.4",
+              "os": "Mac-12.0",
               "pool": "chromium.tests.gpu"
             }
           ],
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 55942ff..0badd93f 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -7089,55 +7089,6 @@
     "gtest_tests": [
       {
         "args": [
-          "angle_unittests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:2184-440.100",
-              "os": "Ubuntu-18.04.5|Ubuntu-18.04.6",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_unittests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/",
-        "use_isolated_scripts_api": true
-      },
-      {
-        "args": [
-          "angle_white_box_tests",
-          "--bot-mode"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:2184-440.100",
-              "os": "Ubuntu-18.04.5|Ubuntu-18.04.6",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/",
-        "use_isolated_scripts_api": true
-      },
-      {
-        "args": [
           "--use-cmd-decoder=passthrough",
           "--use-gl=angle",
           "--use-gpu-in-tests",
@@ -8324,83 +8275,6 @@
     "gtest_tests": [
       {
         "args": [
-          "angle_end2end_tests",
-          "--gtest_filter=-*Vulkan_SwiftShader*",
-          "--bot-mode",
-          "--max-processes=4"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:2184-440.100",
-              "os": "Ubuntu-18.04.5|Ubuntu-18.04.6",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "angle_end2end_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_end2end_tests/",
-        "use_isolated_scripts_api": true
-      },
-      {
-        "args": [
-          "angle_unittests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:2184-440.100",
-              "os": "Ubuntu-18.04.5|Ubuntu-18.04.6",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_unittests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/",
-        "use_isolated_scripts_api": true
-      },
-      {
-        "args": [
-          "angle_white_box_tests",
-          "--bot-mode"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:2184-440.100",
-              "os": "Ubuntu-18.04.5|Ubuntu-18.04.6",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/",
-        "use_isolated_scripts_api": true
-      },
-      {
-        "args": [
           "--use-cmd-decoder=passthrough",
           "--use-gl=angle",
           "--use-gpu-in-tests",
@@ -13814,10 +13688,346 @@
     ]
   },
   "Mac FYI Experimental Retina Release (AMD)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "angle_end2end_tests",
+          "--gtest_filter=-*Vulkan_SwiftShader*",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "angle_end2end_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_end2end_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "angle_unittests"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "angle_unittests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "--enable-gpu",
+          "--test-launcher-bot-mode",
+          "--test-launcher-jobs=1",
+          "--gtest_filter=TabCaptureApiPixelTest.EndToEnd*"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "tab_capture_end2end_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "browser_tests",
+        "test_id_prefix": "ninja://chrome/test:browser_tests/"
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=passthrough",
+          "--use-gl=angle",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=validating",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_tests_validating",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_unittests",
+        "test_id_prefix": "ninja://ui/gl:gl_unittests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gles2_conform_test",
+        "test_id_prefix": "ninja://gpu/gles2_conform_support:gles2_conform_test/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gpu_unittests",
+        "test_id_prefix": "ninja://gpu:gpu_unittests/"
+      },
+      {
+        "args": [
+          "--gtest_filter=*Detection*",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "services_unittests",
+        "test_id_prefix": "ninja://services:services_unittests/"
+      }
+    ],
     "isolated_scripts": [
       {
         "args": [
-          "noop_sleep",
+          "context_lost",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "context_lost_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "context_lost",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "context_lost_validating_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "depth_capture",
           "--show-stdout",
           "--browser=release",
           "--passthrough",
@@ -13829,7 +14039,11 @@
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "noop_sleep_tests",
+        "name": "depth_capture_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -13840,7 +14054,7 @@
               "display_attached": "1",
               "gpu": "1002:6821",
               "hidpi": "1",
-              "os": "Mac-11.4",
+              "os": "Mac-12.0",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -13849,6 +14063,834 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "gpu_process",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "gpu_process_launch_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "hardware_accelerated_feature",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "hardware_accelerated_feature_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "info_collection",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--expected-vendor-id",
+          "1002",
+          "--expected-device-id",
+          "6821"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "info_collection_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "maps",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_revision}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "maps_pixel_passthrough_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "maps",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_revision}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "maps_pixel_validating_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_validating_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_revision}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "pixel_skia_gold_passthrough_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_revision}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "pixel_skia_gold_validating_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "../../tools/perf/run_benchmark",
+          "--benchmarks=rendering.desktop"
+        ],
+        "isolate_name": "rendering_representative_perf_tests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "rendering_representative_perf_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:rendering_representative_perf_tests/"
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--dont-restore-color-profile-after-test"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "screenshot_sync_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--dont-restore-color-profile-after-test"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "screenshot_sync_validating_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "trace_test",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "trace_test",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough --force_high_performance_gpu",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl2_conformance_gl_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 20
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=metal --use-cmd-decoder=passthrough --force_high_performance_gpu",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl2_conformance_metal_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 20
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating --force_high_performance_gpu",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl2_conformance_validating_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 20
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough --force_high_performance_gpu",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_gl_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=metal --use-cmd-decoder=passthrough --force_high_performance_gpu",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_metal_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=swiftshader --use-cmd-decoder=passthrough --force_high_performance_gpu",
+          "--test-filter=conformance/rendering/gl-drawelements.html"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_swangle_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating --force_high_performance_gpu",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_validating_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "display_attached": "1",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-12.0",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
       }
     ]
   },
diff --git a/testing/buildbot/filters/android.emulator_p.chrome_public_test_apk.filter b/testing/buildbot/filters/android.emulator_p.chrome_public_test_apk.filter
index 20a723b4..0f75c2c 100644
--- a/testing/buildbot/filters/android.emulator_p.chrome_public_test_apk.filter
+++ b/testing/buildbot/filters/android.emulator_p.chrome_public_test_apk.filter
@@ -124,3 +124,27 @@
 
 # crbug.com/1187536
 -org.chromium.chrome.browser.customtabs.CustomTabExternalNavigationTest.testIntentPickerNotShownForNormalUrl
+
+# crbug.com/1272997
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantFormActionTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantChromeTabIntegrationTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantTriggerScriptIntegrationTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantBackButtonIntegrationTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantCollectUserDataIntegrationTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantGenericUiTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantChromeTabIntegrationTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantNavigationIntegrationTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantInterruptIntegrationTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantPersonalDataManagerTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantInputActionIntegrationTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantOverlayIntegrationTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantBottomsheetTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantKeyboardIntegrationTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUpdateClientSettingsIntegrationTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantTtsIntegrationTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantAutostartTest*
+-org.chromium.chrome.browser.autofill_assistant.AutofillAssistantPasswordManagerIntegrationTest*
+
+
+
+
diff --git a/testing/buildbot/filters/fuchsia.components_unittests.filter b/testing/buildbot/filters/fuchsia.components_unittests.filter
index d615bf0..a29c809 100644
--- a/testing/buildbot/filters/fuchsia.components_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.components_unittests.filter
@@ -6,7 +6,6 @@
 -BaseFileTest.RenameWithError*
 -BaseFileTest.Uninitialized*
 -BaseFileTest.WriteWith*
--Crash*
 -DownloadFile/DownloadFileTestWithRename.RenameError*
 -DownloadFile/DownloadFileTestWithRename.RenameWith*
 -DownloadPathReservationTrackerTest.UnwriteableDirectory
@@ -23,4 +22,3 @@
 -RealtimeReportingJobConfigurationTest.ValidatePayload
 -SslCastSocketTest.TestConnectEndToEndWithRealSSL
 -UIDevToolsServerTest.ConnectionToViewsServer
--VariationsCrashKeysTest.BasicFunctionality
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 14e51b4..40a714f 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -765,7 +765,7 @@
         'cpu': 'x86-64',
         'gpu': '1002:6821',
         'hidpi': '1',
-        'os': 'Mac-11.4',
+        'os': 'Mac-12.0',
         'pool': 'chromium.tests.gpu',
         'display_attached': '1',
       },
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 6410b5ca..470b76e 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -86,8 +86,6 @@
       # TODO(ynovikov) Investigate why the test breaks on older devices.
       'Android FYI Release (Nexus 5)',
       'Android FYI Release (Nexus 9)',
-      # Temporarily disabled due to bad NVIDIA driver upgrade crbug.com/950542
-      'Linux FYI Debug (NVIDIA)',
     ],
     'modifications': {
       # anglebug.com/5328 suspecting blue screen caused by multiprocess
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 3a221c77..c7452515 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -6541,9 +6541,6 @@
     ],
 
     'gpu_fyi_linux_debug_gtests': [
-      'gpu_angle_end2end_gtests',
-      'gpu_angle_unit_gtests',
-      'gpu_angle_white_box_gtests',
       'gpu_common_gtests_passthrough',
       'gpu_gles2_conform_gtests',
       'gpu_swiftshader_gtests',
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 72fe812..454c9f429c 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -4332,11 +4332,10 @@
           'limited_capacity_bot',
           'mac_retina_amd_gpu_experimental',
         ],
-        # Currently the experimental driver is identical to the stable
-        # driver. If it's upgraded, change these test_suites to be the same as
-        # 'Mac FYI Retina Release (AMD)'.
         'test_suites': {
-          'gpu_telemetry_tests': 'gpu_noop_sleep_telemetry_test',
+          'gtest_tests': 'gpu_fyi_mac_release_gtests',
+          'gpu_telemetry_tests': 'gpu_fyi_mac_release_telemetry_tests',
+          'isolated_scripts': 'rendering_desktop_representative_perf_tests_isolated_scripts',
         },
       },
       'Mac FYI Experimental Retina Release (NVIDIA)': {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index a31f4800..6d1a6657 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -5176,6 +5176,22 @@
             ]
         }
     ],
+    "MojoStructuredMetrics": [
+        {
+            "platforms": [
+                "chromeos",
+                "chromeos_lacros"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "UseCrosApiInterface"
+                    ]
+                }
+            ]
+        }
+    ],
     "MoreFrequentUmaUploads": [
         {
             "platforms": [
@@ -6218,28 +6234,6 @@
             ]
         }
     ],
-    "PowerScheduler2": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_ThrottleIdleAndNopAnimation",
-                    "params": {
-                        "include_charging": "false",
-                        "min_cputime_ratio": "0.5",
-                        "min_time_in_mode_ms": "500",
-                        "policy": "kThrottleIdleAndNopAnimation",
-                        "process_types": "browser,renderer"
-                    },
-                    "enable_features": [
-                        "PowerScheduler"
-                    ]
-                }
-            ]
-        }
-    ],
     "PreconnectOnDidFinishNavigation": [
         {
             "platforms": [
@@ -8971,28 +8965,6 @@
             ]
         }
     ],
-    "WebViewPowerScheduler2": [
-        {
-            "platforms": [
-                "android_webview"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_ThrottleIdleAndNopAnimation",
-                    "params": {
-                        "include_charging": "false",
-                        "min_cputime_ratio": "0.5",
-                        "min_time_in_mode_ms": "500",
-                        "policy": "kThrottleIdleAndNopAnimation",
-                        "process_types": "browser,renderer"
-                    },
-                    "enable_features": [
-                        "PowerScheduler"
-                    ]
-                }
-            ]
-        }
-    ],
     "WebViewThreadSafeMedia": [
         {
             "platforms": [
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 0ae53ce..d66ede9 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -770,6 +770,22 @@
       # https://www.chromestatus.com/feature/5684870116278272 for more details."
       deprecated optional string message
 
+  type ClientHintIssueReason extends string
+    enum
+      # Items in the accept-ch meta tag allow list must be valid origins.
+      # No special values (e.g. self, none, and *) are permitted.
+      MetaTagAllowListInvalidOrigin
+      # Only accept-ch meta tags in the original HTML sent from the server
+      # are respected. Any injected via javascript (or other means) are ignored.
+      MetaTagModifiedHTML
+
+  # This issue tracks client hints related issues. It's used to deprecate old
+  # features, encourage the use of new ones, and provide general guidance.
+  type ClientHintIssueDetails extends object
+    properties
+      SourceCodeLocation sourceCodeLocation
+      ClientHintIssueReason clientHintIssueReason
+
   # A unique identifier for the type of issue. Each type may use one of the
   # optional fields in InspectorIssueDetails to convey more specific
   # information about the kind of issue.
@@ -790,6 +806,7 @@
       WasmCrossOriginModuleSharingIssue
       GenericIssue
       DeprecationIssue
+      ClientHintIssue
 
   # This struct holds a list of optional fields with additional information
   # specific to the kind of issue. When adding a new issue code, please also
@@ -811,6 +828,7 @@
       optional WasmCrossOriginModuleSharingIssueDetails wasmCrossOriginModuleSharingIssue
       optional GenericIssueDetails genericIssueDetails
       optional DeprecationIssueDetails deprecationIssueDetails
+      optional ClientHintIssueDetails clientHintIssueDetails
 
   # A unique id for a DevTools inspector issue. Allows other entities (e.g.
   # exceptions, CDP message, console messages, etc.) to reference an issue.
diff --git a/third_party/blink/renderer/DEPS b/third_party/blink/renderer/DEPS
index ed8d99a..e7e59ba09d 100644
--- a/third_party/blink/renderer/DEPS
+++ b/third_party/blink/renderer/DEPS
@@ -23,9 +23,9 @@
     "+base/ignore_result.h",
     "+base/location.h",
     "+base/logging.h",
+    "+base/memory/ptr_util.h",
     "+base/memory/raw_ptr.h",
     "+base/memory/ref_counted.h",
-    "+base/memory/ptr_util.h",
     "+base/memory/weak_ptr.h",
     "+base/metrics/field_trial_params.h",
     "+base/metrics/histogram.h",
@@ -36,19 +36,18 @@
     "+base/numerics/checked_math.h",
     "+base/numerics/clamped_math.h",
     "+base/numerics/ostream_operators.h",
-    "+base/numerics/ranges.h",
     "+base/numerics/safe_conversions.h",
     "+base/rand_util.h",
     "+base/ranges",
     "+base/sequence_checker.h",
-    "+base/task/sequenced_task_runner.h",
-    "+base/task/single_thread_task_runner.h",
     "+base/stl_util.h",
     "+base/strings/strcat.h",
     "+base/synchronization",
     "+base/sys_byteorder.h",
     "+base/system/sys_info.h",
     "+base/task/post_task.h",
+    "+base/task/sequenced_task_runner.h",
+    "+base/task/single_thread_task_runner.h",
     "+base/task/thread_pool.h",
     "+base/template_util.h",
     "+base/test/gmock_callback_support.h",
@@ -63,18 +62,18 @@
     "+base/time/time_delta_from_string.h",
     "+base/timer/elapsed_timer.h",
     "+base/timer/timer.h",
+    "+base/token.h",
     "+base/trace_event",
     "+base/types/pass_key.h",
     "+base/types/strong_alias.h",
-    "+base/token.h",
     "+build",
     "+cc/paint",
     "+components/crash/core/common/crash_key.h",
     "+components/power_scheduler",
     "+net/cookies",
     "+net/http/structured_headers.h",
-    "+skia/ext",
     "+services/network/public/mojom",
+    "+skia/ext",
     "+testing/gmock/include/gmock",
     "+testing/gtest/include/gtest",
     "+third_party/blink/public/platform",
@@ -85,7 +84,6 @@
     "-ui/base/l10n",
     "-ui/base/resource",
 
-    "+ui/base/cursor/mojom/cursor.mojom-blink.h",
     "+ui/base/cursor/mojom/cursor_type.mojom-blink.h",
     "+ui/base/dragdrop/mojom",
     "+ui/events/keycodes/dom",
diff --git a/third_party/blink/renderer/core/DEPS b/third_party/blink/renderer/core/DEPS
index 71a74437..4d300e5 100644
--- a/third_party/blink/renderer/core/DEPS
+++ b/third_party/blink/renderer/core/DEPS
@@ -1,22 +1,14 @@
 include_rules = [
     "+base/atomic_sequence_num.h",
     "+base/barrier_closure.h",
-    "+base/bits.h",
     "+base/cancelable_callback.h",
     "+base/files/file.h",
     "+base/guid.h",
     "+base/mac/foundation_util.h",
-    "+base/mac/mac_util.h",
-    "+base/mac/scoped_cftyperef.h",
-    "+base/mac/scoped_nsobject.h",
-    "+base/mac/scoped_objc_class_swizzler.h",
     "+base/memory/scoped_policy.h",
     "+base/memory/scoped_refptr.h",
-    "+base/metrics/field_trial_params.h",
-    "+base/numerics/ranges.h",
     "+base/profiler/sample_metadata.h",
     "+base/strings/stringprintf.h",
-    "+base/synchronization/waitable_event.h",
     "+base/task/sequence_manager/task_time_observer.h",
     "+base/test/bind.h",
     "+base/test/simple_test_tick_clock.h",
@@ -24,13 +16,9 @@
     "+base/time",
     "+base/unguessable_token.h",
     "+base/values.h",
-    "+build/mac",
-    "+build/win",
-    "+ui/gfx/animation/keyframe/animation_curve.h",
     "+cc/animation/animation_host.h",
-    "+cc/animation/scroll_offset_animations.h",
     "+cc/animation/scroll_offset_animation_curve.h",
-    "+cc/animation/scroll_state.h",
+    "+cc/animation/scroll_offset_animations.h",
     "+cc/animation/scroll_timeline.h",
     "+cc/base/features.h",
     "+cc/base/region.h",
@@ -38,28 +26,20 @@
     "+cc/input/event_listener_properties.h",
     "+cc/input/main_thread_scrolling_reason.h",
     "+cc/input/overscroll_behavior.h",
-    "+cc/input/scroll_utils.h",
-    "+cc/input/scrollbar.h",
     "+cc/input/scroll_snap_data.h",
     "+cc/input/scroll_state.h",
+    "+cc/input/scroll_utils.h",
+    "+cc/input/scrollbar.h",
     "+cc/input/snap_fling_controller.h",
     "+cc/input/snap_selection_strategy.h",
     "+cc/layers/content_layer_client.h",
     "+cc/layers/heads_up_display_layer.h",
     "+cc/layers/layer.h",
-    "+cc/layers/layer_client.h",
-    "+cc/layers/layer_position_constraint.h",
-    "+cc/layers/layer_sticky_position_constraint.h",
     "+cc/layers/picture_layer.h",
     "+cc/layers/scrollbar_layer_base.h",
     "+cc/layers/surface_layer.h",
     "+cc/metrics/begin_main_frame_metrics.h",
     "+cc/metrics/frame_sequence_tracker_collection.h",
-    "+cc/paint/display_item_list.h",
-    "+cc/paint/paint_canvas.h",
-    "+cc/paint/paint_flags.h",
-    "+cc/paint/paint_image.h",
-    "+cc/paint/paint_worklet_input.h",
     "+cc/trees/browser_controls_params.h",
     "+cc/trees/layer_tree_host.h",
     "+cc/trees/paint_holding_commit_trigger.h",
@@ -69,32 +49,24 @@
     "+components/performance_manager/public/mojom/web_memory.mojom-blink.h",
     "+gpu/config/gpu_feature_info.h",
     "-inspector/v8",
-    "+inspector/v8/public",
     "+mojo/public/cpp/base",
     "+mojo/public/cpp/bindings",
     "+mojo/public/cpp/system",
     "+mojo/public/mojom/base",
     "+services/data_decoder/public/mojom/resource_snapshot_for_web_bundle.mojom-blink.h",
-    "+services/device/public/mojom/device_posture_provider.mojom-blink.h",
     "+services/device/public/mojom/device_posture_provider.mojom-blink-forward.h",
+    "+services/device/public/mojom/device_posture_provider.mojom-blink.h",
     "+services/device/public/mojom/wake_lock.mojom-blink.h",
     "+services/metrics/public",
     "+services/network/public/cpp/cors/cors_error_status.h",
     "+services/network/public/cpp/cross_origin_embedder_policy.h",
     "+services/network/public/cpp/features.h",
-    "+services/network/public/cpp/ip_address_space_util.h",
     "+services/network/public/cpp/is_potentially_trustworthy.h",
     "+services/network/public/cpp/request_destination.h",
     "+services/network/public/cpp/request_mode.h",
     "+services/network/public/cpp/web_sandbox_flags.h",
-    "+services/network/public/cpp/shared_url_loader_factory.h",
-    "+services/resource_coordinator/public/mojom/coordination_unit.mojom-blink.h",
     "+services/service_manager/public",
     "+skia/public/mojom",
-    "+skia/ext/image_operations.h",
-    "+skia/ext/legacy_display_globals.h",
-    "+skia/ext/skia_utils_base.h",
-    "+skia/ext/skia_utils_mac.h",
     "+third_party/blink/public/common",
     "+third_party/blink/public/mojom",
     "+third_party/blink/public/public_buildflags.h",
@@ -104,25 +76,23 @@
     "+third_party/blink/renderer/core",
     "-third_party/blink/renderer/modules",
     "+third_party/skia/include",
-    "+ui/accessibility/ax_enums.mojom-blink.h",
     "+ui/accessibility/ax_enums.mojom-blink-forward.h",
+    "+ui/accessibility/ax_enums.mojom-blink.h",
     "+ui/accessibility/ax_event.h",
     "+ui/accessibility/ax_event_intent.h",
     "+ui/accessibility/ax_mode.h",
     "+ui/base/cursor/cursor.h",
     "+ui/base/ime/ime_text_span.h",
-    "+ui/base/ime/mojom/ime_types.mojom-blink.h",
     "+ui/base/ime/mojom/ime_types.mojom-blink-forward.h",
+    "+ui/base/ime/mojom/ime_types.mojom-blink.h",
     "+ui/base/ime/mojom/text_input_state.mojom-blink.h",
-    "+ui/base/ime/mojom/virtual_keyboard_types.mojom-blink.h",
-    "+ui/base/ime/mojom/virtual_keyboard_types.mojom-blink-forward.h",
     "+ui/base/mojom/attributed_string.mojom-blink.h",
     "+ui/base/pointer/pointer_device.h",
     "+ui/base/resource/resource_scale_factor.h",
     "+ui/base/ui_base_features.h",
-    "+ui/display/mojom/display.mojom-blink.h",
     "+ui/display/screen_info.h",
     "+ui/display/screen_infos.h",
+    "+ui/gfx/animation/keyframe/animation_curve.h",
     "+ui/gfx/geometry",
     "+ui/gfx/range/range.h",
     "-web",
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 3c01114e..5aba3806 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -4592,8 +4592,10 @@
     if (doc.GetPage()) {
       doc.GetPage()->GetFocusController().SetFocusedElement(nullptr,
                                                             doc.GetFrame());
-      doc.GetPage()->GetChromeClient().ClearKeyboardTriggeredTooltip(
-          *doc.GetFrame());
+      if (doc.GetFrame()) {
+        doc.GetPage()->GetChromeClient().ClearKeyboardTriggeredTooltip(
+            *doc.GetFrame());
+      }
     } else {
       doc.ClearFocusedElement();
     }
diff --git a/third_party/blink/renderer/core/exported/web_performance.cc b/third_party/blink/renderer/core/exported/web_performance.cc
index 29b15a35..e4f981a 100644
--- a/third_party/blink/renderer/core/exported/web_performance.cc
+++ b/third_party/blink/renderer/core/exported/web_performance.cc
@@ -187,6 +187,13 @@
   return private_->timing()->FirstContentfulPaintAsMonotonicTime();
 }
 
+base::TimeTicks
+WebPerformance::FirstContentfulPaintRenderedButNotPresentedAsMonotonicTime()
+    const {
+  return private_->timing()
+      ->FirstContentfulPaintRenderedButNotPresentedAsMonotonicTime();
+}
+
 double WebPerformance::FirstMeaningfulPaint() const {
   return MillisecondsToSeconds(private_->timing()->FirstMeaningfulPaint());
 }
diff --git a/third_party/blink/renderer/core/html/client_hints_util.cc b/third_party/blink/renderer/core/html/client_hints_util.cc
index 6a34e62..928c165 100644
--- a/third_party/blink/renderer/core/html/client_hints_util.cc
+++ b/third_party/blink/renderer/core/html/client_hints_util.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/public/common/client_hints/client_hints.h"
 #include "third_party/blink/public/common/permissions_policy/permissions_policy.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/inspector/inspector_audits_issue.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -22,6 +23,14 @@
     ClientHintsPreferences::Context* context,
     bool is_http_equiv,
     bool is_preload_or_sync_parser) {
+  // If it's not http-equiv and it's not a preload-or-sync-parser visible
+  // meta tag, then we need to warn the dev that javascript injected the tag.
+  if (!is_http_equiv && !is_preload_or_sync_parser && local_dom_window &&
+      RuntimeEnabledFeatures::ClientHintThirdPartyDelegationEnabled()) {
+    AuditsIssue::ReportClientHintIssue(
+        local_dom_window, ClientHintIssueReason::kMetaTagModifiedHTML);
+  }
+
   // If no hints were set, this is an http-equiv tag, this tag was added by
   // javascript, the `local_dom_window` is missing, or the feature is disabled,
   // there's nothing more to do.
@@ -34,15 +43,22 @@
   }
 
   // Note: .Ascii() would convert tab to ?, which is undesirable.
-  absl::optional<network::ClientHintToDelegatedThirdPartiesMap> parsed_ch =
+  absl::optional<network::ClientHintToDelegatedThirdPartiesHeader> parsed_ch =
       network::ParseClientHintToDelegatedThirdPartiesHeader(
           header_value.Latin1());
 
+  // If invalid origins were seen in the allow list we need to warn the dev.
+  if (parsed_ch.value().had_invalid_origins) {
+    AuditsIssue::ReportClientHintIssue(
+        local_dom_window,
+        ClientHintIssueReason::kMetaTagAllowListInvalidOrigin);
+  }
+
   // Build vector of client hint permission policies to update.
   auto* const current_policy =
       local_dom_window->GetSecurityContext().GetPermissionsPolicy();
   ParsedPermissionsPolicy container_policy;
-  for (const auto& pair : parsed_ch.value()) {
+  for (const auto& pair : parsed_ch.value().map) {
     const auto& policy_name = GetClientHintToPolicyFeatureMap().at(pair.first);
 
     // We need to retain any preexisting settings, just adding new origins.
diff --git a/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc b/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
index c378f4e..2041f4d 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
@@ -349,7 +349,8 @@
     }
     listbox_part_->hide();
 
-    DispatchInputChangeEventsIfNeeded();
+    if (SelectedOption() != selected_option_when_listbox_opened_)
+      DispatchChangeEvent();
   }
 }
 
@@ -637,15 +638,25 @@
   }
 }
 
-void HTMLSelectMenuElement::DispatchInputChangeEventsIfNeeded() {
-  if (SelectedOption() != selected_option_when_listbox_opened_) {
-    Event* input_event = Event::CreateBubble(event_type_names::kInput);
-    input_event->SetComposed(true);
-    DispatchScopedEvent(*input_event);
-    DispatchScopedEvent(*Event::CreateBubble(event_type_names::kChange));
+void HTMLSelectMenuElement::DispatchInputAndChangeEventsIfNeeded() {
+  DispatchInputEvent();
+  if (!open()) {
+    // Only fire change if the listbox is already closed, because if it's open
+    // we'll  fire change later when the listbox closes.
+    DispatchChangeEvent();
   }
 }
 
+void HTMLSelectMenuElement::DispatchInputEvent() {
+  Event* input_event = Event::CreateBubble(event_type_names::kInput);
+  input_event->SetComposed(true);
+  DispatchScopedEvent(*input_event);
+}
+
+void HTMLSelectMenuElement::DispatchChangeEvent() {
+  DispatchScopedEvent(*Event::CreateBubble(event_type_names::kChange));
+}
+
 void HTMLSelectMenuElement::OptionPartInserted(
     HTMLOptionElement* new_option_part) {
   if (!IsValidOptionPart(new_option_part, /*show_warning=*/true)) {
@@ -773,6 +784,7 @@
       auto* element = DynamicTo<HTMLOptionElement>(node);
       SetSelectedOption(element);
       element->focus();
+      DispatchInputAndChangeEventsIfNeeded();
       return;
     }
   }
@@ -785,6 +797,7 @@
       auto* element = DynamicTo<HTMLOptionElement>(node);
       SetSelectedOption(element);
       element->focus();
+      DispatchInputAndChangeEventsIfNeeded();
       return;
     }
   }
@@ -840,7 +853,10 @@
         DynamicTo<HTMLOptionElement>(event->currentTarget()->ToNode());
     DCHECK(target_element);
     DCHECK(select_menu_element_->option_parts_.Contains(target_element));
-    select_menu_element_->SetSelectedOption(target_element);
+    if (target_element != select_menu_element_->SelectedOption()) {
+      select_menu_element_->SetSelectedOption(target_element);
+      select_menu_element_->DispatchInputEvent();
+    }
     select_menu_element_->CloseListbox();
   } else if (event->type() == event_type_names::kKeydown) {
     bool handled = false;
@@ -853,7 +869,10 @@
             DynamicTo<HTMLOptionElement>(event->currentTarget()->ToNode());
         DCHECK(target_element);
         DCHECK(select_menu_element_->option_parts_.Contains(target_element));
-        select_menu_element_->SetSelectedOption(target_element);
+        if (target_element != select_menu_element_->SelectedOption()) {
+          select_menu_element_->SetSelectedOption(target_element);
+          select_menu_element_->DispatchInputEvent();
+        }
         select_menu_element_->CloseListbox();
         handled = true;
         break;
diff --git a/third_party/blink/renderer/core/html/forms/html_select_menu_element.h b/third_party/blink/renderer/core/html/forms/html_select_menu_element.h
index d860dca5..0d0e1fc 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_menu_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_select_menu_element.h
@@ -86,7 +86,9 @@
   void OptionPartInserted(HTMLOptionElement*);
   void OptionPartRemoved(HTMLOptionElement*);
   void ResetOptionParts();
-  void DispatchInputChangeEventsIfNeeded();
+  void DispatchInputAndChangeEventsIfNeeded();
+  void DispatchInputEvent();
+  void DispatchChangeEvent();
 
   bool IsValidButtonPart(const Node* node, bool show_warning) const;
   bool IsValidListboxPart(const Node* node, bool show_warning) const;
diff --git a/third_party/blink/renderer/core/inspector/inspector_audits_issue.cc b/third_party/blink/renderer/core/inspector/inspector_audits_issue.cc
index 20622f0..4f5a8f9 100644
--- a/third_party/blink/renderer/core/inspector/inspector_audits_issue.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_audits_issue.cc
@@ -480,6 +480,42 @@
   execution_context->AddInspectorIssue(AuditsIssue(std::move(issue)));
 }
 
+namespace {
+
+protocol::Audits::ClientHintIssueReason ClientHintIssueReasonToProtocol(
+    ClientHintIssueReason reason) {
+  switch (reason) {
+    case ClientHintIssueReason::kMetaTagAllowListInvalidOrigin:
+      return protocol::Audits::ClientHintIssueReasonEnum::
+          MetaTagAllowListInvalidOrigin;
+    case ClientHintIssueReason::kMetaTagModifiedHTML:
+      return protocol::Audits::ClientHintIssueReasonEnum::MetaTagModifiedHTML;
+  }
+}
+
+}  // namespace
+
+// static
+void AuditsIssue::ReportClientHintIssue(LocalDOMWindow* local_dom_window,
+                                        ClientHintIssueReason reason) {
+  auto source_location = SourceLocation::Capture(local_dom_window);
+  auto client_hint_issue_details =
+      protocol::Audits::ClientHintIssueDetails::create()
+          .setSourceCodeLocation(CreateProtocolLocation(*source_location))
+          .setClientHintIssueReason(ClientHintIssueReasonToProtocol(reason))
+          .build();
+  auto issue_details =
+      protocol::Audits::InspectorIssueDetails::create()
+          .setClientHintIssueDetails(std::move(client_hint_issue_details))
+          .build();
+  auto issue =
+      protocol::Audits::InspectorIssue::create()
+          .setCode(protocol::Audits::InspectorIssueCodeEnum::ClientHintIssue)
+          .setDetails(std::move(issue_details))
+          .build();
+  local_dom_window->AddInspectorIssue(AuditsIssue(std::move(issue)));
+}
+
 AuditsIssue AuditsIssue::CreateBlockedByResponseIssue(
     network::mojom::BlockedByResponseReason reason,
     uint64_t identifier,
diff --git a/third_party/blink/renderer/core/inspector/inspector_audits_issue.h b/third_party/blink/renderer/core/inspector/inspector_audits_issue.h
index 6bb1c2d..b95eb81 100644
--- a/third_party/blink/renderer/core/inspector/inspector_audits_issue.h
+++ b/third_party/blink/renderer/core/inspector/inspector_audits_issue.h
@@ -25,6 +25,7 @@
 class ExecutionContext;
 class LocalFrame;
 class ResourceError;
+class LocalDOMWindow;
 class LocalFrame;
 class SecurityPolicyViolationEventInit;
 class SourceLocation;
@@ -65,6 +66,11 @@
   kMixedContentWarning,
 };
 
+enum class ClientHintIssueReason {
+  kMetaTagAllowListInvalidOrigin,
+  kMetaTagModifiedHTML,
+};
+
 // |AuditsIssue| is a thin wrapper around the Audits::InspectorIssue
 // protocol class.
 //
@@ -140,6 +146,9 @@
   static void ReportDeprecationIssue(ExecutionContext* execution_context,
                                      const String& message);
 
+  static void ReportClientHintIssue(LocalDOMWindow* local_dom_window,
+                                    ClientHintIssueReason reason);
+
   static AuditsIssue CreateBlockedByResponseIssue(
       network::mojom::BlockedByResponseReason reason,
       uint64_t identifier,
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
index d06dfe6..215f418 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -959,7 +959,9 @@
       if (child->HasInlineFragments())
         continue;
 
-      if (!child->IsLayoutInline()) {
+      const auto* layout_inline_child = DynamicTo<LayoutInline>(child);
+
+      if (!layout_inline_child) {
         // We end up here for collapsed text nodes. Just clear the paint flags.
         for (const LayoutObject* fragmentless = child; fragmentless;
              fragmentless = fragmentless->NextInPreOrder(child)) {
@@ -970,13 +972,23 @@
         continue;
       }
 
-      // We have to enter culled inlines for every block fragment where any of
-      // their children has a representation.
-      if (!parent_fragment->HasItems())
-        continue;
-      if (!parent_fragment->Items()->IsContainerForCulledInline(
-              To<LayoutInline>(*child), &is_first_for_node, &is_last_for_node))
-        continue;
+      if (layout_inline_child->FirstChild()) {
+        // We have to enter culled inlines for every block fragment where any of
+        // their children has a representation.
+        if (!parent_fragment->HasItems())
+          continue;
+
+        if (!parent_fragment->Items()->IsContainerForCulledInline(
+                *layout_inline_child, &is_first_for_node, &is_last_for_node))
+          continue;
+      } else {
+        // Childless and culled. This can happen for AREA elements, if nothing
+        // else. Enter it when at the last container fragment.
+        if (parent_fragment->BreakToken())
+          continue;
+        is_first_for_node = true;
+        is_last_for_node = true;
+      }
 
       // Inlines will pass their containing block fragment (and its incoming
       // break token).
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_encoder_test.cc b/third_party/blink/renderer/modules/webcodecs/audio_encoder_test.cc
index c99ea16e..628fadb 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_encoder_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_encoder_test.cc
@@ -75,6 +75,13 @@
   auto* encoder = CreateEncoder(script_state, init, es);
   ASSERT_FALSE(es.HadException());
 
+  // Simulate backgrounding to enable reclamation.
+  if (!encoder->is_backgrounded_for_testing()) {
+    encoder->SimulateLifecycleStateForTesting(
+        scheduler::SchedulingLifecycleState::kHidden);
+    DCHECK(encoder->is_backgrounded_for_testing());
+  }
+
   auto* config = CreateConfig();
   encoder->configure(config, es);
   ASSERT_FALSE(es.HadException());
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_template.cc b/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
index 9bbf938..92a708c 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
@@ -68,7 +68,8 @@
 DecoderTemplate<Traits>::DecoderTemplate(ScriptState* script_state,
                                          const InitType* init,
                                          ExceptionState& exception_state)
-    : ExecutionContextLifecycleObserver(ExecutionContext::From(script_state)),
+    : ReclaimableCodec(ExecutionContext::From(script_state)),
+      ExecutionContextLifecycleObserver(ExecutionContext::From(script_state)),
       script_state_(script_state),
       state_(V8CodecState::Enum::kUnconfigured),
       trace_counter_id_(g_sequence_num_for_counters.GetNext()) {
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_template_test.cc b/third_party/blink/renderer/modules/webcodecs/decoder_template_test.cc
index 36cbf25c..473bf08 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_template_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_template_test.cc
@@ -185,6 +185,13 @@
   ASSERT_TRUE(decoder);
   ASSERT_FALSE(v8_scope.GetExceptionState().HadException());
 
+  // Simulate backgrounding to enable reclamation.
+  if (!decoder->is_backgrounded_for_testing()) {
+    decoder->SimulateLifecycleStateForTesting(
+        scheduler::SchedulingLifecycleState::kHidden);
+    DCHECK(decoder->is_backgrounded_for_testing());
+  }
+
   // Configure the decoder.
   decoder->configure(this->CreateConfig(), v8_scope.GetExceptionState());
   ASSERT_FALSE(v8_scope.GetExceptionState().HadException());
diff --git a/third_party/blink/renderer/modules/webcodecs/encoder_base.cc b/third_party/blink/renderer/modules/webcodecs/encoder_base.cc
index b33fab8..a800ed5 100644
--- a/third_party/blink/renderer/modules/webcodecs/encoder_base.cc
+++ b/third_party/blink/renderer/modules/webcodecs/encoder_base.cc
@@ -56,7 +56,8 @@
 EncoderBase<Traits>::EncoderBase(ScriptState* script_state,
                                  const InitType* init,
                                  ExceptionState& exception_state)
-    : ExecutionContextLifecycleObserver(ExecutionContext::From(script_state)),
+    : ReclaimableCodec(ExecutionContext::From(script_state)),
+      ExecutionContextLifecycleObserver(ExecutionContext::From(script_state)),
       state_(V8CodecState::Enum::kUnconfigured),
       script_state_(script_state),
       trace_counter_id_(g_sequence_num_for_counters.GetNext()) {
diff --git a/third_party/blink/renderer/modules/webcodecs/reclaimable_codec.cc b/third_party/blink/renderer/modules/webcodecs/reclaimable_codec.cc
index 6ff7cbc7..a73e171c 100644
--- a/third_party/blink/renderer/modules/webcodecs/reclaimable_codec.cc
+++ b/third_party/blink/renderer/modules/webcodecs/reclaimable_codec.cc
@@ -15,19 +15,71 @@
 const base::Feature kReclaimInactiveWebCodecs{"ReclaimInactiveWebCodecs",
                                               base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kOnlyReclaimBackgroundWebCodecs{
+    "OnlyReclaimBackgroundWebCodecs", base::FEATURE_ENABLED_BY_DEFAULT};
+
 constexpr base::TimeDelta ReclaimableCodec::kInactivityReclamationThreshold;
 constexpr base::TimeDelta ReclaimableCodec::kTimerPeriod;
 
-ReclaimableCodec::ReclaimableCodec()
+ReclaimableCodec::ReclaimableCodec(ExecutionContext* context)
     : tick_clock_(base::DefaultTickClock::GetInstance()),
       last_activity_(tick_clock_->NowTicks()),
       activity_timer_(Thread::Current()->GetTaskRunner(),
                       this,
-                      &ReclaimableCodec::ActivityTimerFired) {}
+                      &ReclaimableCodec::ActivityTimerFired) {
+  DCHECK(context);
+  if (base::FeatureList::IsEnabled(kOnlyReclaimBackgroundWebCodecs)) {
+    // Do this last, it will immediately re-enter via OnLifecycleStateChanged().
+    observer_handle_ = context->GetScheduler()->AddLifecycleObserver(
+        FrameOrWorkerScheduler::ObserverType::kWorkerScheduler, this);
+  } else {
+    // Pretend we're always in the background to _always_ reclaim.
+    is_backgrounded_ = true;
+  }
+}
+
+void ReclaimableCodec::OnLifecycleStateChanged(
+    scheduler::SchedulingLifecycleState lifecycle_state) {
+  DVLOG(5) << __func__
+           << " lifecycle_state=" << static_cast<int>(lifecycle_state);
+  bool is_backgrounded =
+      lifecycle_state != scheduler::SchedulingLifecycleState::kNotThrottled;
+
+  // Several life cycle states map to "backgrounded", but we only want to
+  // observe the transition.
+  if (is_backgrounded == is_backgrounded_)
+    return;
+
+  is_backgrounded_ = is_backgrounded;
+
+  // Nothing to do when paused.
+  if (is_reclamation_paused_) {
+    DCHECK(!activity_timer_.IsActive());
+    return;
+  }
+
+  if (is_backgrounded_) {
+    // (Re)entered background, so start timer again from "now".
+    MarkCodecActive();
+    DCHECK(activity_timer_.IsActive());
+  } else {
+    // We're in foreground, so pause reclamation to improve UX.
+    PauseCodecReclamationInternal();
+  }
+}
 
 void ReclaimableCodec::MarkCodecActive() {
+  DVLOG(5) << __func__;
+  is_reclamation_paused_ = false;
   last_activity_ = tick_clock_->NowTicks();
   last_tick_was_inactive_ = false;
+
+  if (!is_backgrounded_) {
+    DCHECK(!activity_timer_.IsActive());
+    DVLOG(5) << __func__ << " Suppressing reclamation of foreground codec.";
+    return;
+  }
+
   StartTimer();
 }
 
@@ -40,19 +92,38 @@
   ActivityTimerFired(nullptr);
 }
 
+void ReclaimableCodec::SimulateLifecycleStateForTesting(
+    scheduler::SchedulingLifecycleState state) {
+  OnLifecycleStateChanged(state);
+}
+
 void ReclaimableCodec::PauseCodecReclamation() {
+  DVLOG(5) << __func__;
+  is_reclamation_paused_ = true;
+  PauseCodecReclamationInternal();
+}
+
+void ReclaimableCodec::PauseCodecReclamationInternal() {
+  DVLOG(5) << __func__;
   activity_timer_.Stop();
 }
 
 void ReclaimableCodec::StartTimer() {
+  DCHECK(is_backgrounded_);
+  DCHECK(!is_reclamation_paused_);
+
   if (activity_timer_.IsActive())
     return;
 
-  if (base::FeatureList::IsEnabled(kReclaimInactiveWebCodecs))
+  if (base::FeatureList::IsEnabled(kReclaimInactiveWebCodecs)) {
+    DVLOG(5) << __func__ << " Starting timer.";
     activity_timer_.StartRepeating(kTimerPeriod, FROM_HERE);
+  }
 }
 
 void ReclaimableCodec::ActivityTimerFired(TimerBase*) {
+  DCHECK(is_backgrounded_);
+  DCHECK(!is_reclamation_paused_);
   DCHECK(base::FeatureList::IsEnabled(kReclaimInactiveWebCodecs));
 
   auto time_inactive = tick_clock_->NowTicks() - last_activity_;
diff --git a/third_party/blink/renderer/modules/webcodecs/reclaimable_codec.h b/third_party/blink/renderer/modules/webcodecs/reclaimable_codec.h
index fbfe113..4057372 100644
--- a/third_party/blink/renderer/modules/webcodecs/reclaimable_codec.h
+++ b/third_party/blink/renderer/modules/webcodecs/reclaimable_codec.h
@@ -5,10 +5,15 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_RECLAIMABLE_CODEC_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_RECLAIMABLE_CODEC_H_
 
+#include "base/feature_list.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h"
 #include "third_party/blink/renderer/platform/timer.h"
 
+#include <memory>
+
 namespace base {
 class TickClock;
 }  // namespace base
@@ -17,9 +22,14 @@
 
 class DOMException;
 
-class MODULES_EXPORT ReclaimableCodec : public GarbageCollectedMixin {
+extern const MODULES_EXPORT base::Feature kReclaimInactiveWebCodecs;
+extern const MODULES_EXPORT base::Feature kOnlyReclaimBackgroundWebCodecs;
+
+class MODULES_EXPORT ReclaimableCodec
+    : public GarbageCollectedMixin,
+      public FrameOrWorkerScheduler::Observer {
  public:
-  ReclaimableCodec();
+  explicit ReclaimableCodec(ExecutionContext*);
 
   // GarbageCollectedMixin override.
   void Trace(Visitor*) const override;
@@ -28,8 +38,11 @@
     return activity_timer_.IsActive();
   }
 
+  bool is_backgrounded_for_testing() { return is_backgrounded_; }
+
   void SimulateCodecReclaimedForTesting();
   void SimulateActivityTimerFiredForTesting();
+  void SimulateLifecycleStateForTesting(scheduler::SchedulingLifecycleState);
 
   void set_tick_clock_for_testing(const base::TickClock* clock) {
     tick_clock_ = clock;
@@ -41,6 +54,10 @@
   static constexpr base::TimeDelta kTimerPeriod =
       kInactivityReclamationThreshold / 2;
 
+  // Notified when throttling state is changed. May be called consecutively
+  // with the same value.
+  void OnLifecycleStateChanged(scheduler::SchedulingLifecycleState) override;
+
  protected:
   // Pushes back the time at which |this| can be reclaimed due to inactivity.
   // Starts a inactivity reclamation timer, if it isn't already running.
@@ -57,6 +74,7 @@
  private:
   void ActivityTimerFired(TimerBase*);
   void StartTimer();
+  void PauseCodecReclamationInternal();
 
   // This is used to make sure that there are two consecutive ticks of the
   // timer, before we reclaim for inactivity. This prevents immediately
@@ -67,6 +85,19 @@
 
   base::TimeTicks last_activity_;
   HeapTaskRunnerTimer<ReclaimableCodec> activity_timer_;
+
+  // True iff document.visibilityState of the associated page is "hidden".
+  // This includes being in bg of tab strip, minimized, or (depending on OS)
+  // covered by other windows.
+  bool is_backgrounded_ = false;
+
+  // True if PauseCodecReclamation() has been called more recently than
+  // MarkCodecActive(), or if the codec is already reclaimed.
+  bool is_reclamation_paused_ = true;
+
+  // Handle to unhook from FrameOrWorkerScheduler upon destruction.
+  std::unique_ptr<FrameOrWorkerScheduler::LifecycleObserverHandle>
+      observer_handle_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/reclaimable_codec_test.cc b/third_party/blink/renderer/modules/webcodecs/reclaimable_codec_test.cc
index 05dd2719..2e1e157 100644
--- a/third_party/blink/renderer/modules/webcodecs/reclaimable_codec_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/reclaimable_codec_test.cc
@@ -4,11 +4,13 @@
 
 #include "third_party/blink/renderer/modules/webcodecs/reclaimable_codec.h"
 
+#include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/time/default_tick_clock.h"
 #include "media/base/test_helpers.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 
 namespace blink {
@@ -19,6 +21,9 @@
     : public GarbageCollected<FakeReclaimableCodec>,
       public ReclaimableCodec {
  public:
+  explicit FakeReclaimableCodec(ExecutionContext* context)
+      : ReclaimableCodec(context) {}
+
   void SimulateActivity() {
     MarkCodecActive();
     reclaimed_ = false;
@@ -41,14 +46,72 @@
 
 }  // namespace
 
-class ReclaimableCodecTest : public testing::Test {
+// Testing w/ flags allowing only reclamation of background codecs.
+class ReclaimBackgroundOnlyTest : public testing::Test {
  public:
-  ReclaimableCodecTest() = default;
-  ~ReclaimableCodecTest() override = default;
+  ReclaimBackgroundOnlyTest() {
+    std::vector<base::Feature> enabled_features{
+        kReclaimInactiveWebCodecs, kOnlyReclaimBackgroundWebCodecs};
+    std::vector<base::Feature> disabled_features{};
+    feature_list_.InitWithFeatures(enabled_features, disabled_features);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
 };
 
-TEST_F(ReclaimableCodecTest, InactivityTimerStartStops) {
-  auto* codec = MakeGarbageCollected<FakeReclaimableCodec>();
+// Testing w/ flags allowing reclamation of both foreground and background
+// codecs.
+class ReclaimForegroundSameAsBackgroundTest : public testing::Test {
+ public:
+  ReclaimForegroundSameAsBackgroundTest() {
+    std::vector<base::Feature> enabled_features{kReclaimInactiveWebCodecs};
+    std::vector<base::Feature> disabled_features{
+        kOnlyReclaimBackgroundWebCodecs};
+    feature_list_.InitWithFeatures(enabled_features, disabled_features);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Testing kill-switch scenario w/ all flags disabled.
+class ReclaimDisabledTest : public testing::Test {
+ public:
+  ReclaimDisabledTest() {
+    std::vector<base::Feature> enabled_features{};
+    std::vector<base::Feature> disabled_features{
+        kReclaimInactiveWebCodecs, kOnlyReclaimBackgroundWebCodecs};
+    feature_list_.InitWithFeatures(enabled_features, disabled_features);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+enum class TestParam {
+  // Instructs test to use SimulateLifecycleStateForTesting(kHidden) to simulate
+  // a backgrounding scenario.
+  kSimulateBackgrounding,
+  // Instructs test to expect that codec is already backgrounded and to refrain
+  // from simulating backgrounding as above.
+  kExpectAlreadyInBackground,
+};
+
+void TestBackgroundInactivityTimerStartStops(TestParam background_type) {
+  V8TestingScope v8_scope;
+
+  auto* codec = MakeGarbageCollected<FakeReclaimableCodec>(
+      v8_scope.GetExecutionContext());
+
+  if (background_type == TestParam::kSimulateBackgrounding) {
+    EXPECT_FALSE(codec->is_backgrounded_for_testing());
+    codec->SimulateLifecycleStateForTesting(
+        scheduler::SchedulingLifecycleState::kHidden);
+  } else {
+    DCHECK_EQ(background_type, TestParam::kExpectAlreadyInBackground);
+    EXPECT_TRUE(codec->is_backgrounded_for_testing());
+  }
 
   // Codecs should not be reclaimable for inactivity until first activity.
   EXPECT_FALSE(codec->IsReclamationTimerActiveForTesting());
@@ -73,8 +136,20 @@
   EXPECT_TRUE(codec->IsReclamationTimerActiveForTesting());
 }
 
-TEST_F(ReclaimableCodecTest, InactivityTimerWorks) {
-  auto* codec = MakeGarbageCollected<FakeReclaimableCodec>();
+void TestBackgroundInactivityTimerWorks(TestParam background_type) {
+  V8TestingScope v8_scope;
+
+  auto* codec = MakeGarbageCollected<FakeReclaimableCodec>(
+      v8_scope.GetExecutionContext());
+
+  if (background_type == TestParam::kSimulateBackgrounding) {
+    EXPECT_FALSE(codec->is_backgrounded_for_testing());
+    codec->SimulateLifecycleStateForTesting(
+        scheduler::SchedulingLifecycleState::kHidden);
+  } else {
+    DCHECK_EQ(background_type, TestParam::kExpectAlreadyInBackground);
+    EXPECT_TRUE(codec->is_backgrounded_for_testing());
+  }
 
   // Codecs should not be reclaimable for inactivity until first activity.
   EXPECT_FALSE(codec->IsReclamationTimerActiveForTesting());
@@ -107,4 +182,234 @@
   codec->set_tick_clock_for_testing(base::DefaultTickClock::GetInstance());
 }
 
+TEST_F(ReclaimBackgroundOnlyTest, BackgroundInactivityTimerStartStops) {
+  // Only background reclamation permitted, so simulate backgrouding.
+  TestBackgroundInactivityTimerStartStops(TestParam::kSimulateBackgrounding);
+}
+
+TEST_F(ReclaimBackgroundOnlyTest, BackgroundInactivityTimerWorks) {
+  // Only background reclamation permitted, so simulate backgrouding.
+  TestBackgroundInactivityTimerWorks(TestParam::kSimulateBackgrounding);
+}
+
+TEST_F(ReclaimForegroundSameAsBackgroundTest,
+       BackgroundInactivityTimerStartStops) {
+  // Foreground codecs are treated as always backgrounded w/ these feature flags
+  TestBackgroundInactivityTimerStartStops(
+      TestParam::kExpectAlreadyInBackground);
+}
+
+TEST_F(ReclaimForegroundSameAsBackgroundTest, BackgroundInactivityTimerWorks) {
+  // Foreground codecs are treated as always backgrounded w/ these feature flags
+  TestBackgroundInactivityTimerWorks(TestParam::kExpectAlreadyInBackground);
+}
+
+TEST_F(ReclaimBackgroundOnlyTest, ForegroundInactivityTimerNeverStarts) {
+  V8TestingScope v8_scope;
+
+  auto* codec = MakeGarbageCollected<FakeReclaimableCodec>(
+      v8_scope.GetExecutionContext());
+
+  // Test codec should start in foreground when kOnlyReclaimBackgroundWebCodecs
+  // enabled.
+  EXPECT_FALSE(codec->is_backgrounded_for_testing());
+
+  // Codecs should not be reclaimable for inactivity until first activity.
+  EXPECT_FALSE(codec->IsReclamationTimerActiveForTesting());
+
+  base::SimpleTestTickClock tick_clock;
+  codec->set_tick_clock_for_testing(&tick_clock);
+
+  // First activity should not start timer while we remain in foreground.
+  codec->SimulateActivity();
+  EXPECT_FALSE(codec->IsReclamationTimerActiveForTesting());
+  EXPECT_FALSE(codec->is_backgrounded_for_testing());
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Advancing time by any amount shouldn't change the above.
+  tick_clock.Advance(ReclaimableCodec::kTimerPeriod * 100);
+  EXPECT_FALSE(codec->IsReclamationTimerActiveForTesting());
+  EXPECT_FALSE(codec->is_backgrounded_for_testing());
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Activity still shouldn't start the timer as we remain in foreground.
+  codec->SimulateActivity();
+  EXPECT_FALSE(codec->IsReclamationTimerActiveForTesting());
+  EXPECT_FALSE(codec->is_backgrounded_for_testing());
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Restore default tick clock since |codec| is a garbage collected object that
+  // may outlive the scope of this function.
+  codec->set_tick_clock_for_testing(base::DefaultTickClock::GetInstance());
+}
+
+TEST_F(ReclaimBackgroundOnlyTest, ForegroundCodecReclaimedOnceBackgrounded) {
+  V8TestingScope v8_scope;
+
+  auto* codec = MakeGarbageCollected<FakeReclaimableCodec>(
+      v8_scope.GetExecutionContext());
+
+  // Test codec should start in foreground when kOnlyReclaimBackgroundWebCodecs
+  // enabled.
+  EXPECT_FALSE(codec->is_backgrounded_for_testing());
+
+  // Codecs should not be reclaimable for inactivity until first activity.
+  EXPECT_FALSE(codec->IsReclamationTimerActiveForTesting());
+
+  base::SimpleTestTickClock tick_clock;
+  codec->set_tick_clock_for_testing(&tick_clock);
+
+  // First activity should not start timer while we remain in foreground.
+  codec->SimulateActivity();
+  EXPECT_FALSE(codec->IsReclamationTimerActiveForTesting());
+  EXPECT_FALSE(codec->is_backgrounded_for_testing());
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Entering background should start timer.
+  codec->SimulateLifecycleStateForTesting(
+      scheduler::SchedulingLifecycleState::kHidden);
+  EXPECT_TRUE(codec->IsReclamationTimerActiveForTesting());
+  EXPECT_TRUE(codec->is_backgrounded_for_testing());
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Advancing 1 period shouldn't reclaim (it takes 2).
+  tick_clock.Advance(ReclaimableCodec::kTimerPeriod);
+  codec->SimulateActivityTimerFiredForTesting();
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Re-entering foreground should stop the timer.
+  codec->SimulateLifecycleStateForTesting(
+      scheduler::SchedulingLifecycleState::kNotThrottled);
+  EXPECT_FALSE(codec->IsReclamationTimerActiveForTesting());
+  EXPECT_FALSE(codec->is_backgrounded_for_testing());
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Advancing any amount of time shouldn't reclaim while in foreground.
+  tick_clock.Advance(ReclaimableCodec::kTimerPeriod * 100);
+  EXPECT_FALSE(codec->IsReclamationTimerActiveForTesting());
+  EXPECT_FALSE(codec->is_backgrounded_for_testing());
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Re-entering background should again start the timer.
+  codec->SimulateLifecycleStateForTesting(
+      scheduler::SchedulingLifecycleState::kHidden);
+  EXPECT_TRUE(codec->IsReclamationTimerActiveForTesting());
+  EXPECT_TRUE(codec->is_backgrounded_for_testing());
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Fire newly backgrounded to ensure first tick isn't treated as idle.
+  codec->SimulateActivityTimerFiredForTesting();
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Timer should be fresh such that one period is not enough to reclaim.
+  tick_clock.Advance(ReclaimableCodec::kTimerPeriod);
+  codec->SimulateActivityTimerFiredForTesting();
+  EXPECT_TRUE(codec->is_backgrounded_for_testing());
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Advancing twice through the period should finally reclaim.
+  tick_clock.Advance(ReclaimableCodec::kTimerPeriod);
+  codec->SimulateActivityTimerFiredForTesting();
+  EXPECT_TRUE(codec->is_backgrounded_for_testing());
+  EXPECT_TRUE(codec->reclaimed());
+
+  // Restore default tick clock since |codec| is a garbage collected object that
+  // may outlive the scope of this function.
+  codec->set_tick_clock_for_testing(base::DefaultTickClock::GetInstance());
+}
+
+TEST_F(ReclaimBackgroundOnlyTest, RepeatLifecycleEventsDontBreakState) {
+  V8TestingScope v8_scope;
+
+  auto* codec = MakeGarbageCollected<FakeReclaimableCodec>(
+      v8_scope.GetExecutionContext());
+
+  // Test codec should start in foreground when kOnlyReclaimBackgroundWebCodecs
+  // enabled.
+  EXPECT_FALSE(codec->is_backgrounded_for_testing());
+
+  // Duplicate kNotThrottled (foreground) shouldn't affect codec state.
+  codec->SimulateLifecycleStateForTesting(
+      scheduler::SchedulingLifecycleState::kNotThrottled);
+  EXPECT_FALSE(codec->is_backgrounded_for_testing());
+
+  // Codecs should not be reclaimable for inactivity until first activity.
+  EXPECT_FALSE(codec->IsReclamationTimerActiveForTesting());
+
+  base::SimpleTestTickClock tick_clock;
+  codec->set_tick_clock_for_testing(&tick_clock);
+
+  // First activity should not start timer while we remain in foreground.
+  codec->SimulateActivity();
+  EXPECT_FALSE(codec->IsReclamationTimerActiveForTesting());
+  EXPECT_FALSE(codec->is_backgrounded_for_testing());
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Entering background should start timer.
+  codec->SimulateLifecycleStateForTesting(
+      scheduler::SchedulingLifecycleState::kHidden);
+  EXPECT_TRUE(codec->IsReclamationTimerActiveForTesting());
+  EXPECT_TRUE(codec->is_backgrounded_for_testing());
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Advancing 1 period shouldn't reclaim (it takes 2).
+  tick_clock.Advance(ReclaimableCodec::kTimerPeriod);
+  codec->SimulateActivityTimerFiredForTesting();
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Further background lifecycle progression shouldn't affect codec state.
+  codec->SimulateLifecycleStateForTesting(
+      scheduler::SchedulingLifecycleState::kThrottled);
+  EXPECT_TRUE(codec->IsReclamationTimerActiveForTesting());
+  EXPECT_TRUE(codec->is_backgrounded_for_testing());
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Further background lifecycle progression shouldn't affect codec state.
+  codec->SimulateLifecycleStateForTesting(
+      scheduler::SchedulingLifecycleState::kStopped);
+  EXPECT_TRUE(codec->IsReclamationTimerActiveForTesting());
+  EXPECT_TRUE(codec->is_backgrounded_for_testing());
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Advancing one final time through the period should finally reclaim.
+  tick_clock.Advance(ReclaimableCodec::kTimerPeriod);
+  codec->SimulateActivityTimerFiredForTesting();
+  EXPECT_TRUE(codec->is_backgrounded_for_testing());
+  EXPECT_TRUE(codec->reclaimed());
+
+  // Restore default tick clock since |codec| is a garbage collected object that
+  // may outlive the scope of this function.
+  codec->set_tick_clock_for_testing(base::DefaultTickClock::GetInstance());
+}
+
+TEST_F(ReclaimDisabledTest, ReclamationKillSwitch) {
+  V8TestingScope v8_scope;
+
+  auto* codec = MakeGarbageCollected<FakeReclaimableCodec>(
+      v8_scope.GetExecutionContext());
+
+  // Test codec should start in background when kOnlyReclaimBackgroundWebCodecs
+  // is disabled.
+  EXPECT_TRUE(codec->is_backgrounded_for_testing());
+
+  // Codecs should not be reclaimable for inactivity until first activity.
+  EXPECT_FALSE(codec->IsReclamationTimerActiveForTesting());
+
+  base::SimpleTestTickClock tick_clock;
+  codec->set_tick_clock_for_testing(&tick_clock);
+
+  // Reclamation disabled, so activity should not start the timer.
+  codec->SimulateActivity();
+  EXPECT_FALSE(codec->IsReclamationTimerActiveForTesting());
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Advancing any period should not be enough to reclaim the codec.
+  tick_clock.Advance(ReclaimableCodec::kTimerPeriod * 10);
+  EXPECT_FALSE(codec->reclaimed());
+
+  // Restore default tick clock since |codec| is a garbage collected object that
+  // may outlive the scope of this function.
+  codec->set_tick_clock_for_testing(base::DefaultTickClock::GetInstance());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder_test.cc b/third_party/blink/renderer/modules/webcodecs/video_encoder_test.cc
index b24d8cb..e53fafd9 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_encoder_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_encoder_test.cc
@@ -149,6 +149,13 @@
   auto* encoder = CreateEncoder(script_state, init, es);
   ASSERT_FALSE(es.HadException());
 
+  // Simulate backgrounding to enable reclamation.
+  if (!encoder->is_backgrounded_for_testing()) {
+    encoder->SimulateLifecycleStateForTesting(
+        scheduler::SchedulingLifecycleState::kHidden);
+    DCHECK(encoder->is_backgrounded_for_testing());
+  }
+
   auto* config = CreateConfig();
   encoder->configure(config, es);
   ASSERT_FALSE(es.HadException());
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
index 8dd4c7bf..a96e81c 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
@@ -657,7 +657,7 @@
                                   const WGPUExtent3D& copy_size,
                                   const WGPUImageCopyTexture& destination,
                                   const WGPUTextureFormat dest_texture_format,
-                                  bool premultiplied_alpha,
+                                  bool dst_premultiplied_alpha,
                                   bool flipY) {
   // Prepare for uploading CPU data.
   gfx::Rect image_data_rect(origin.x, origin.y, copy_size.width,
@@ -690,7 +690,8 @@
 
     if (!CopyBytesFromImageBitmapForWebGPU(
             image, base::span<uint8_t>(static_cast<uint8_t*>(data), size),
-            image_data_rect, dest_texture_format, premultiplied_alpha, flipY)) {
+            image_data_rect, dest_texture_format, dst_premultiplied_alpha,
+            flipY)) {
       // Release the buffer.
       GetProcs().bufferRelease(buffer);
       return false;
@@ -732,7 +733,7 @@
                                   const WGPUExtent3D& copy_size,
                                   const WGPUImageCopyTexture& destination,
                                   const WGPUTextureFormat dest_texture_format,
-                                  bool premultiplied_alpha,
+                                  bool dst_premultiplied_alpha,
                                   bool flipY) {
   // Check src/dst texture formats are supported by CopyTextureForBrowser
   SkImageInfo image_info = image->PaintImageForCurrentFrame().GetSkImageInfo();
@@ -769,10 +770,12 @@
     options.flipY = true;
   }
 
-  options.alphaOp = image->IsPremultiplied() == premultiplied_alpha
-                        ? WGPUAlphaOp_DontChange
-                        : premultiplied_alpha ? WGPUAlphaOp_Premultiply
-                                              : WGPUAlphaOp_Unpremultiply;
+  options.srcAlphaMode = image->IsPremultiplied()
+                             ? WGPUAlphaMode_Premultiplied
+                             : WGPUAlphaMode_Unpremultiplied;
+  options.dstAlphaMode = dst_premultiplied_alpha
+                             ? WGPUAlphaMode_Premultiplied
+                             : WGPUAlphaMode_Unpremultiplied;
 
   GetProcs().queueCopyTextureForBrowser(GetHandle(), &src, &destination,
                                         &copy_size, &options);
diff --git a/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.h b/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.h
index 0d64c4e1..006d723 100644
--- a/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_bounded_reference_space.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_BOUNDED_REFERENCE_SPACE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_BOUNDED_REFERENCE_SPACE_H_
 
+#include <memory>
+
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/core/geometry/dom_point_read_only.h"
 #include "third_party/blink/renderer/modules/xr/xr_reference_space.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_canvas_input_provider.cc b/third_party/blink/renderer/modules/xr/xr_canvas_input_provider.cc
index 90d3aef..f08b52cc 100644
--- a/third_party/blink/renderer/modules/xr/xr_canvas_input_provider.cc
+++ b/third_party/blink/renderer/modules/xr/xr_canvas_input_provider.cc
@@ -20,7 +20,7 @@
 
 class XRCanvasInputEventListener : public NativeEventListener {
  public:
-  XRCanvasInputEventListener(XRCanvasInputProvider* input_provider)
+  explicit XRCanvasInputEventListener(XRCanvasInputProvider* input_provider)
       : input_provider_(input_provider) {}
 
   void Invoke(ExecutionContext* execution_context, Event* event) override {
diff --git a/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.cc b/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.cc
index 614b5ea..6b9e5cb 100644
--- a/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.cc
+++ b/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.cc
@@ -28,7 +28,7 @@
       return 4;
   }
 }
-}
+}  // namespace
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/xr/xr_cube_map.cc b/third_party/blink/renderer/modules/xr/xr_cube_map.cc
index 5db04bef..a1823eb 100644
--- a/third_party/blink/renderer/modules/xr/xr_cube_map.cc
+++ b/third_party/blink/renderer/modules/xr/xr_cube_map.cc
@@ -8,7 +8,6 @@
 #include "device/vr/public/mojom/vr_service.mojom-blink.h"
 #include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h"
 #include "third_party/blink/renderer/modules/webgl/webgl_texture.h"
-#include "third_party/blink/renderer/modules/xr/xr_cube_map.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
 
diff --git a/third_party/blink/renderer/modules/xr/xr_depth_manager.cc b/third_party/blink/renderer/modules/xr/xr_depth_manager.cc
index 86f8e3b..7b4ce3f0 100644
--- a/third_party/blink/renderer/modules/xr/xr_depth_manager.cc
+++ b/third_party/blink/renderer/modules/xr/xr_depth_manager.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/modules/xr/xr_depth_manager.h"
 
+#include <utility>
+
 #include "base/trace_event/trace_event.h"
 #include "third_party/blink/renderer/modules/xr/xr_cpu_depth_information.h"
 #include "third_party/blink/renderer/modules/xr/xr_session.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.cc b/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.cc
index fde4fa7..749f5032 100644
--- a/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.h"
 
+#include <utility>
+
 #include "third_party/blink/renderer/bindings/core/v8/v8_fullscreen_options.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_exit_fullscreen_observer.cc b/third_party/blink/renderer/modules/xr/xr_exit_fullscreen_observer.cc
index c53c84e6..b5230dd 100644
--- a/third_party/blink/renderer/modules/xr/xr_exit_fullscreen_observer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_exit_fullscreen_observer.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/modules/xr/xr_exit_fullscreen_observer.h"
 
+#include <utility>
+
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/frame/viewport_data.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
index 23ceb7b..d49eb1c 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/modules/xr/xr_frame_provider.h"
 
+#include <utility>
+
 #include "build/build_config.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_request_callback_collection.h b/third_party/blink/renderer/modules/xr/xr_frame_request_callback_collection.h
index c76deb2..37d03f5 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_request_callback_collection.h
+++ b/third_party/blink/renderer/modules/xr/xr_frame_request_callback_collection.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_FRAME_REQUEST_CALLBACK_COLLECTION_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_FRAME_REQUEST_CALLBACK_COLLECTION_H_
 
+#include <memory>
+
 #include "third_party/blink/renderer/platform/bindings/name_client.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_grip_space.h b/third_party/blink/renderer/modules/xr/xr_grip_space.h
index 8d35c61..e81b3f8 100644
--- a/third_party/blink/renderer/modules/xr/xr_grip_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_grip_space.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_GRIP_SPACE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_GRIP_SPACE_H_
 
+#include <string>
+
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/modules/xr/xr_space.h"
 
diff --git a/third_party/blink/renderer/modules/xr/xr_hand.cc b/third_party/blink/renderer/modules/xr/xr_hand.cc
index f83d60c9..5bfd9df 100644
--- a/third_party/blink/renderer/modules/xr/xr_hand.cc
+++ b/third_party/blink/renderer/modules/xr/xr_hand.cc
@@ -4,6 +4,9 @@
 
 #include "third_party/blink/renderer/modules/xr/xr_hand.h"
 
+#include <memory>
+#include <utility>
+
 #include "third_party/blink/renderer/modules/xr/xr_input_source.h"
 #include "third_party/blink/renderer/modules/xr/xr_joint_space.h"
 #include "third_party/blink/renderer/modules/xr/xr_utils.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_input_source.h b/third_party/blink/renderer/modules/xr/xr_input_source.h
index 6bc690e..fda663b 100644
--- a/third_party/blink/renderer/modules/xr/xr_input_source.h
+++ b/third_party/blink/renderer/modules/xr/xr_input_source.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_INPUT_SOURCE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_INPUT_SOURCE_H_
 
+#include <memory>
+
 #include "device/vr/public/mojom/vr_service.mojom-blink.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/modules/gamepad/gamepad.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_joint_space.cc b/third_party/blink/renderer/modules/xr/xr_joint_space.cc
index ead930a..24cb004 100644
--- a/third_party/blink/renderer/modules/xr/xr_joint_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_joint_space.cc
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
+#include <string>
+#include <utility>
+
 #include "third_party/blink/renderer/modules/xr/xr_joint_space.h"
 #include "third_party/blink/renderer/modules/xr/xr_hand.h"
 #include "third_party/blink/renderer/modules/xr/xr_space.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_joint_space.h b/third_party/blink/renderer/modules/xr/xr_joint_space.h
index b8018f23..f14c756 100644
--- a/third_party/blink/renderer/modules/xr/xr_joint_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_joint_space.h
@@ -5,6 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_JOINT_SPACE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_JOINT_SPACE_H_
 
+#include <memory>
+#include <string>
+
 #include "device/vr/public/mojom/vr_service.mojom-blink.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/modules/xr/xr_hand.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_object_space.h b/third_party/blink/renderer/modules/xr/xr_object_space.h
index d39bc1d..d537dc4 100644
--- a/third_party/blink/renderer/modules/xr/xr_object_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_object_space.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_OBJECT_SPACE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_OBJECT_SPACE_H_
 
+#include <string>
+
 #include "device/vr/public/mojom/vr_service.mojom-blink-forward.h"
 #include "third_party/blink/renderer/modules/xr/xr_space.h"
 #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_reference_space.cc b/third_party/blink/renderer/modules/xr/xr_reference_space.cc
index 71933f6..1eaf218 100644
--- a/third_party/blink/renderer/modules/xr/xr_reference_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_reference_space.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/xr/xr_reference_space.h"
 
 #include <sstream>
+#include <string>
 
 #include "device/vr/public/mojom/vr_service.mojom-blink.h"
 #include "third_party/blink/renderer/modules/xr/xr_pose.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_reference_space.h b/third_party/blink/renderer/modules/xr/xr_reference_space.h
index e7b20f8b..42bd15f 100644
--- a/third_party/blink/renderer/modules/xr/xr_reference_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_reference_space.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_REFERENCE_SPACE_H_
 
 #include <memory>
+#include <string>
 
 #include "device/vr/public/mojom/vr_service.mojom-blink.h"
 #include "third_party/blink/renderer/modules/xr/xr_space.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_render_state.h b/third_party/blink/renderer/modules/xr/xr_render_state.h
index 373f795f..befe3dae 100644
--- a/third_party/blink/renderer/modules/xr/xr_render_state.h
+++ b/third_party/blink/renderer/modules/xr/xr_render_state.h
@@ -20,7 +20,7 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  XRRenderState(bool immersive);
+  explicit XRRenderState(bool immersive);
   ~XRRenderState() override = default;
 
   // Near and far depths are used when computing projection matrices for the
diff --git a/third_party/blink/renderer/modules/xr/xr_rigid_transform_test.cc b/third_party/blink/renderer/modules/xr/xr_rigid_transform_test.cc
index 0e31b61..02bc8f74 100644
--- a/third_party/blink/renderer/modules/xr/xr_rigid_transform_test.cc
+++ b/third_party/blink/renderer/modules/xr/xr_rigid_transform_test.cc
@@ -4,14 +4,15 @@
 
 #include "third_party/blink/renderer/modules/xr/xr_rigid_transform.h"
 
-#include "third_party/blink/renderer/modules/xr/xr_test_utils.h"
-#include "third_party/blink/renderer/modules/xr/xr_utils.h"
+#include <algorithm>
 
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.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/modules/xr/xr_test_utils.h"
+#include "third_party/blink/renderer/modules/xr/xr_utils.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index 6f2b2a9..e5399d13 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <string>
 #include <utility>
 
 #include "base/auto_reset.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_session.h b/third_party/blink/renderer/modules/xr/xr_session.h
index a773ce2..3461aae 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.h
+++ b/third_party/blink/renderer/modules/xr/xr_session.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_SESSION_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_SESSION_H_
 
+#include <memory>
+
 #include "base/containers/span.h"
 #include "device/vr/public/mojom/vr_service.mojom-blink.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_space.h b/third_party/blink/renderer/modules/xr/xr_space.h
index 98bcee0..bf3bc1814 100644
--- a/third_party/blink/renderer/modules/xr/xr_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_space.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_SPACE_H_
 
 #include <memory>
+#include <string>
 
 #include "device/vr/public/mojom/vr_service.mojom-blink.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_system.cc b/third_party/blink/renderer/modules/xr/xr_system.cc
index 7474adf..53b68ef 100644
--- a/third_party/blink/renderer/modules/xr/xr_system.cc
+++ b/third_party/blink/renderer/modules/xr/xr_system.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/modules/xr/xr_system.h"
 
+#include <algorithm>
+#include <memory>
 #include <utility>
 
 #include "base/containers/contains.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_target_ray_space.cc b/third_party/blink/renderer/modules/xr/xr_target_ray_space.cc
index c521022f0..372cff7 100644
--- a/third_party/blink/renderer/modules/xr/xr_target_ray_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_target_ray_space.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/modules/xr/xr_target_ray_space.h"
 
+#include <string>
 #include <utility>
 
 #include "third_party/blink/renderer/modules/xr/xr_input_source.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_target_ray_space.h b/third_party/blink/renderer/modules/xr/xr_target_ray_space.h
index 902b2b7..5a212fa 100644
--- a/third_party/blink/renderer/modules/xr/xr_target_ray_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_target_ray_space.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_TARGET_RAY_SPACE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_TARGET_RAY_SPACE_H_
 
+#include <string>
+
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/modules/xr/xr_space.h"
 
diff --git a/third_party/blink/renderer/modules/xr/xr_transient_input_hit_test_source.cc b/third_party/blink/renderer/modules/xr/xr_transient_input_hit_test_source.cc
index 30e2199b..b944e1c 100644
--- a/third_party/blink/renderer/modules/xr/xr_transient_input_hit_test_source.cc
+++ b/third_party/blink/renderer/modules/xr/xr_transient_input_hit_test_source.cc
@@ -43,8 +43,9 @@
     const HashMap<uint32_t, Vector<device::mojom::blink::XRHitResultPtr>>&
         hit_test_results,
     XRInputSourceArray* input_source_array) {
-  // TODO: Be smarter about the update - it's possible to add new resulst /
-  // remove the ones that were removed & update the ones that are being changed.
+  // TODO(bialpio): Be smarter about the update. It's possible to add new
+  // results or remove the ones that were removed & update the ones that are
+  // being changed.
   current_frame_results_.clear();
 
   // If we don't know anything about input sources, we won't be able to
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
index 2aa66bd..be52eaa 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
@@ -4,6 +4,9 @@
 
 #include "third_party/blink/renderer/modules/xr/xr_webgl_layer.h"
 
+#include <algorithm>
+#include <utility>
+
 #include "base/cxx17_backports.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/trace_event/trace_event.h"
@@ -23,8 +26,6 @@
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/size.h"
 
-#include <algorithm>
-
 namespace blink {
 
 namespace {
@@ -113,8 +114,8 @@
 
     // Clamp the developer-requested framebuffer size to ensure it's not too
     // small to see or unreasonably large.
-    // TODO: Would be best to have the max value communicated from the service
-    // rather than limited to the native res.
+    // TODO(bajones): Would be best to have the max value communicated from the
+    // service rather than limited to the native res.
     framebuffer_scale = base::clamp(initializer->framebufferScaleFactor(),
                                     kFramebufferMinScale, max_scale);
   }
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 6f713dd..15ae230c 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -678,6 +678,8 @@
     "fonts/shaping/glyph_bounds_accumulator.h",
     "fonts/shaping/harfbuzz_face.cc",
     "fonts/shaping/harfbuzz_face.h",
+    "fonts/shaping/harfbuzz_face_from_typeface.cc",
+    "fonts/shaping/harfbuzz_face_from_typeface.h",
     "fonts/shaping/harfbuzz_font_cache.cc",
     "fonts/shaping/harfbuzz_font_cache.h",
     "fonts/shaping/harfbuzz_font_data.h",
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc
index 92a3785..f87ea9ca 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc
@@ -42,6 +42,7 @@
 #include "third_party/blink/renderer/platform/fonts/font_global_context.h"
 #include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face_from_typeface.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_data.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
@@ -50,13 +51,11 @@
 #include "third_party/blink/renderer/platform/resolution_units.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
-#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/harfbuzz-ng/utils/hb_scoped.h"
 #include "third_party/skia/include/core/SkPaint.h"
 #include "third_party/skia/include/core/SkPath.h"
 #include "third_party/skia/include/core/SkPoint.h"
 #include "third_party/skia/include/core/SkRect.h"
-#include "third_party/skia/include/core/SkStream.h"
 #include "third_party/skia/include/core/SkTypeface.h"
 
 namespace blink {
@@ -363,48 +362,19 @@
                         WTF::Partitions::FastFree);
 }
 
-#if !defined(OS_MAC)
-static void DeleteTypefaceStream(void* stream_asset_ptr) {
-  SkStreamAsset* stream_asset =
-      reinterpret_cast<SkStreamAsset*>(stream_asset_ptr);
-  delete stream_asset;
-}
-#endif
-
 HbScoped<hb_face_t> HarfBuzzFace::CreateFace() {
   HbScoped<hb_face_t> face;
 
-  SkTypeface* typeface = platform_data_->Typeface();
+  sk_sp<SkTypeface> typeface = sk_ref_sp(platform_data_->Typeface());
   CHECK(typeface);
-  // The attempt of doing zero copy-mmaped memory access to the font blobs does
-  // not work efficiently on Mac, since what is returned from
-  // typeface->openStream is a synthesized font assembled from copying all font
-  // tables on Mac. See the implementation of SkTypeface_Mac::onOpenStream.
 #if !defined(OS_MAC)
-  int ttc_index = 0;
-  std::unique_ptr<SkStreamAsset> tf_stream(typeface->openStream(&ttc_index));
-  if (tf_stream && tf_stream->getMemoryBase()) {
-    const void* tf_memory = tf_stream->getMemoryBase();
-    size_t tf_size = tf_stream->getLength();
-    HbScoped<hb_blob_t> face_blob(
-        hb_blob_create(reinterpret_cast<const char*>(tf_memory),
-                       SafeCast<unsigned int>(tf_size), HB_MEMORY_MODE_READONLY,
-                       tf_stream.release(), DeleteTypefaceStream));
-    // hb_face_create always succeeds.
-    // Use hb_face_count to retrieve the number of recognized faces in the blob.
-    // hb_face_create_for_tables may still create a working hb_face.
-    // See https://github.com/harfbuzz/harfbuzz/issues/248 .
-    unsigned int num_hb_faces = hb_face_count(face_blob.get());
-    if (0 < num_hb_faces && static_cast<unsigned>(ttc_index) < num_hb_faces) {
-      face.reset(hb_face_create(face_blob.get(), ttc_index));
-    }
-  }
+  face = HbFaceFromSkTypeface(typeface);
 #endif
 
   // Fallback to table copies if there is no in-memory access.
   if (!face) {
-    face.reset(hb_face_create_for_tables(HarfBuzzSkiaGetTable,
-                                         platform_data_->Typeface(), nullptr));
+    face.reset(hb_face_create_for_tables(HarfBuzzSkiaGetTable, typeface.get(),
+                                         nullptr));
   }
 
   DCHECK(face);
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face_from_typeface.cc b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face_from_typeface.cc
new file mode 100644
index 0000000..05c13c5
--- /dev/null
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face_from_typeface.cc
@@ -0,0 +1,46 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face_from_typeface.h"
+
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/skia/include/core/SkStream.h"
+
+namespace {
+static void DeleteTypefaceStream(void* stream_asset_ptr) {
+  SkStreamAsset* stream_asset =
+      reinterpret_cast<SkStreamAsset*>(stream_asset_ptr);
+  delete stream_asset;
+}
+}  // namespace
+
+namespace blink {
+
+HbScoped<hb_face_t> HbFaceFromSkTypeface(sk_sp<SkTypeface> typeface) {
+  HbScoped<hb_face_t> return_face(nullptr);
+  int ttc_index = 0;
+
+  // Have openStream() write the ttc index of this typeface within the stream to
+  // the ttc_index parameter, so that we can check it below against the count of
+  // faces within the buffer, as HarfBuzz counts it.
+  std::unique_ptr<SkStreamAsset> tf_stream(typeface->openStream(&ttc_index));
+  if (tf_stream && tf_stream->getMemoryBase()) {
+    const void* tf_memory = tf_stream->getMemoryBase();
+    size_t tf_size = tf_stream->getLength();
+    HbScoped<hb_blob_t> face_blob(
+        hb_blob_create(reinterpret_cast<const char*>(tf_memory),
+                       SafeCast<unsigned int>(tf_size), HB_MEMORY_MODE_READONLY,
+                       tf_stream.release(), DeleteTypefaceStream));
+    // hb_face_create always succeeds.
+    // Use hb_face_count to retrieve the number of recognized faces in the blob.
+    // hb_face_create_for_tables may still create a working hb_face.
+    // See https://github.com/harfbuzz/harfbuzz/issues/248 .
+    unsigned int num_hb_faces = hb_face_count(face_blob.get());
+    if (0 < num_hb_faces && static_cast<unsigned>(ttc_index) < num_hb_faces) {
+      return_face.reset(hb_face_create(face_blob.get(), ttc_index));
+    }
+  }
+  return return_face;
+}
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face_from_typeface.h b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face_from_typeface.h
new file mode 100644
index 0000000..6fc8c09
--- /dev/null
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face_from_typeface.h
@@ -0,0 +1,32 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARFBUZZ_FACE_FROM_TYPEFACE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARFBUZZ_FACE_FROM_TYPEFACE_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+
+#include "third_party/harfbuzz-ng/utils/hb_scoped.h"
+
+#include <hb.h>
+
+namespace blink {
+
+// Creates a scoped HarfBuzz hb_face_t based on accessing the underlying SkData
+// of the SkTypeface (using SkTypeface::openStream() and
+// SkStream::getMemoryBase().
+//
+// Warning regarding usage on Mac: Using this for FreeType-backed SkTypeface
+// objects on Mac is okay. Do not use this on Mac for CoreText-backed SkTypeface
+// objects. For those, accessing the font blob does not work efficiently since
+// what is returned from typeface->openStream is a synthesized font assembled
+// from copying all font tables on Mac into newly allocated memory, causing a
+// potentially quite large allocations (in the megabytes range). See the
+// implementation of SkTypeface_Mac::onOpenStream.
+PLATFORM_EXPORT HbScoped<hb_face_t> HbFaceFromSkTypeface(
+    sk_sp<SkTypeface> typeface);
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARFBUZZ_FACE_FROM_TYPEFACE_H_
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.cc b/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.cc
index 69fcebe..cf00a3d68 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.cc
@@ -106,7 +106,7 @@
     base::span<uint8_t> dst,
     const gfx::Rect& rect,
     const WGPUTextureFormat destination_format,
-    bool premultipliedAlpha,
+    bool dst_premultiplied_alpha,
     bool flipY) {
   DCHECK(image);
   DCHECK_GT(dst.size(), static_cast<size_t>(0));
@@ -133,7 +133,7 @@
   // TODO(crbug.com/1217153): Convert to user-provided color space.
   SkImageInfo info = SkImageInfo::Make(
       rect.width(), rect.height(), sk_color_type,
-      premultipliedAlpha ? kPremul_SkAlphaType : kUnpremul_SkAlphaType,
+      dst_premultiplied_alpha ? kPremul_SkAlphaType : kUnpremul_SkAlphaType,
       paint_image.GetSkImageInfo().refColorSpace());
 
   if (!flipY && read_all_channels) {
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.h b/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.h
index a2373dfc..e06ef73 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.h
@@ -33,7 +33,7 @@
                                   base::span<uint8_t> dst,
                                   const gfx::Rect& rect,
                                   const WGPUTextureFormat destination_format,
-                                  bool premultipliedAlpha,
+                                  bool dst_premultiplied_alpha,
                                   bool flipY);
 
 uint64_t PLATFORM_EXPORT
diff --git a/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc b/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc
index 3460458..325ec52 100644
--- a/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc
@@ -71,7 +71,7 @@
     }
   } else if (is_preload_or_sync_parser) {
     // Note: .Ascii() would convert tab to ?, which is undesirable.
-    absl::optional<network::ClientHintToDelegatedThirdPartiesMap> parsed_ch =
+    absl::optional<network::ClientHintToDelegatedThirdPartiesHeader> parsed_ch =
         network::ParseClientHintToDelegatedThirdPartiesHeader(
             header_value.Latin1());
 
@@ -79,7 +79,7 @@
       return false;
 
     // Update first-party permissions for each client hint.
-    for (const auto& pair : parsed_ch.value()) {
+    for (const auto& pair : parsed_ch.value().map) {
       enabled_hints_.SetIsEnabled(pair.first, true);
     }
   }
diff --git a/third_party/blink/web_tests/FlagExpectations/skia-vulkan-native b/third_party/blink/web_tests/FlagExpectations/skia-vulkan-native
index 191530b..f255f89 100644
--- a/third_party/blink/web_tests/FlagExpectations/skia-vulkan-native
+++ b/third_party/blink/web_tests/FlagExpectations/skia-vulkan-native
@@ -64,3 +64,9 @@
 # These tests have large image differences, need investigation.
 crbug.com/993384 css3/filters/effect-reference-colorspace.html [ Failure Pass ]
 crbug.com/993384 css3/filters/effect-reference-subregion.html [ Failure Pass ]
+
+# Producing minor differences between NVIDIA and Intel GPUs with no good way to
+# accept both outputs.
+crbug.com/1278181 [ Linux ] images/color-profile-animate.html [ Failure ]
+crbug.com/1278181 [ Linux ] images/color-profile-image-filter-all.html [ Failure ]
+crbug.com/1278181 [ Linux ] images/color-profile-svg-fill-text.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 46e2206..4d7d4464 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -41,7 +41,14 @@
 
 # WPT HTTP/2 is not fully supported by run_web_tests.
 crbug.com/1048761 external/wpt/infrastructure/server/http2-websocket.sub.h2.any.html [ Failure ]
-crbug.com/1048761 external/wpt/infrastructure/server/http2-websocket.sub.h2.any.worker.html [ Failure ]
+crbug.com/1048761 [ Fuchsia ] external/wpt/infrastructure/server/http2-websocket.sub.h2.any.worker.html [ Failure ]
+crbug.com/1048761 [ Linux ] external/wpt/infrastructure/server/http2-websocket.sub.h2.any.worker.html [ Failure ]
+crbug.com/1048761 [ Mac10.12 ] external/wpt/infrastructure/server/http2-websocket.sub.h2.any.worker.html [ Failure ]
+crbug.com/1048761 [ Mac10.13 ] external/wpt/infrastructure/server/http2-websocket.sub.h2.any.worker.html [ Failure ]
+crbug.com/1048761 [ Mac10.14 ] external/wpt/infrastructure/server/http2-websocket.sub.h2.any.worker.html [ Failure ]
+crbug.com/1048761 [ Mac10.15 ] external/wpt/infrastructure/server/http2-websocket.sub.h2.any.worker.html [ Failure ]
+crbug.com/1048761 [ Mac11 ] external/wpt/infrastructure/server/http2-websocket.sub.h2.any.worker.html [ Failure ]
+crbug.com/1048761 [ Win ] external/wpt/infrastructure/server/http2-websocket.sub.h2.any.worker.html [ Failure ]
 crbug.com/1048761 external/wpt/infrastructure/server/wpt-server-wpt-flags.sub.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/infrastructure/server/wpt-server-wpt-flags.sub.html?wpt_flags=https [ Failure ]
 
@@ -2808,6 +2815,21 @@
 crbug.com/626703 [ Fuchsia ] external/wpt/dom/historical.html [ Skip ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-sizing/table-child-percentage-height-with-border-box.html [ Failure ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/fetch/api/basic/status.h2.any.worker.html [ Timeout ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/infrastructure/server/http2-websocket.sub.h2.any.worker.html [ Timeout ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/websockets/Close-1000-reason.any.worker.html?wpt_flags=h2 [ Timeout ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/websockets/Close-server-initiated-close.any.worker.html?wpt_flags=h2 [ Timeout ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/websockets/Create-valid-url-array-protocols.any.worker.html?wpt_flags=h2 [ Timeout ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/websockets/Send-binary-arraybufferview-float64.any.html?wpt_flags=h2 [ Timeout ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.html?wpt_flags=h2 [ Timeout ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.worker.html?wpt_flags=h2 [ Timeout ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/websockets/Send-binary-arraybufferview-uint32-offset.any.html?wpt_flags=h2 [ Timeout ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/websockets/Send-unpaired-surrogates.any.worker.html?wpt_flags=h2 [ Timeout ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/websockets/bufferedAmount-unchanged-by-sync-xhr.any.sharedworker.html?wpt_flags=h2 [ Timeout ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/websockets/constructor/022.html?wpt_flags=h2 [ Timeout ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/websockets/interfaces/WebSocket/events/018.html?wpt_flags=h2 [ Timeout ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/websockets/unload-a-document/005.html?wpt_flags=h2 [ Skip Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/color-contrast-001.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/xyz-d65-004.html [ Failure ]
@@ -2823,7 +2845,7 @@
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-line-height-001b.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-line-height-003c.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-line-height-004d.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-position-property-001.html [ Timeout Failure ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-position-property-001.html [ Failure Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-position-property-001b.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-position-property-002.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-position-property-003.html [ Failure ]
@@ -2841,7 +2863,7 @@
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-property-003.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-property-004a.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-style-property-005.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-style-property-012a.html [ Failure Crash ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-style-property-012a.html [ Crash Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-style-property-013.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-style-property-013a.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-style-property-014a.html [ Failure ]
@@ -2850,7 +2872,7 @@
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-style-property-017a.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-emphasis-style-property-019.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-text-decor/text-shadow/quirks-decor-noblur.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-007.html [ Failure Crash ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-007.html [ Crash Failure ]
 crbug.com/626703 [ Mac11-arm64 ] virtual/system-color-compute/external/wpt/css/css-color/color-contrast-001.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/css/css-color/color-contrast-001.html [ Failure ]
@@ -6463,13 +6485,6 @@
 crbug.com/1126709 [ Mac ] virtual/threaded/external/wpt/animation-worklet/worklet-animation-local-time-null-1.https.html [ Failure Pass ]
 crbug.com/1126709 [ Win ] virtual/threaded/external/wpt/animation-worklet/worklet-animation-local-time-null-1.https.html [ Failure Pass ]
 
-# Sheriff 2020-09-17
-### virtual/scroll-unification/fast/scrolling/scrollbars/
-virtual/scroll-unification/fast/scrolling/scrollbars/mouse-scrolling-on-div-scrollbar.html [ Failure ]
-virtual/scroll-unification/fast/scrolling/scrollbars/scrollbar-occluded-by-div.html [ Failure ]
-virtual/scroll-unification/fast/scrolling/scrollbars/scrollbar-rtl-manipulation.html [ Failure ]
-virtual/scroll-unification/fast/scrolling/scrollbars/dsf-ready/mouse-interactions-dsf-2.html [ Failure ]
-
 # Sheriff 2020-09-21
 crbug.com/1130500 [ Debug Mac10.13 ] virtual/plz-dedicated-worker/external/wpt/xhr/xhr-timeout-longtask.any.worker.html [ Failure Pass ]
 
@@ -7030,7 +7045,14 @@
 # can't handle this difference with -expected.txt files, so we have to list them
 # here.
 crbug.com/1048761 external/wpt/websockets/Close-1000-reason.any.html?wpt_flags=h2 [ Failure ]
-crbug.com/1048761 external/wpt/websockets/Close-1000-reason.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Fuchsia ] external/wpt/websockets/Close-1000-reason.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Linux ] external/wpt/websockets/Close-1000-reason.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.12 ] external/wpt/websockets/Close-1000-reason.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.13 ] external/wpt/websockets/Close-1000-reason.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.14 ] external/wpt/websockets/Close-1000-reason.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.15 ] external/wpt/websockets/Close-1000-reason.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac11 ] external/wpt/websockets/Close-1000-reason.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Win ] external/wpt/websockets/Close-1000-reason.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Close-1000-verify-code.any.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Close-1000-verify-code.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Close-1000.any.html?wpt_flags=h2 [ Failure ]
@@ -7058,13 +7080,27 @@
 crbug.com/1048761 external/wpt/websockets/Close-reason-unpaired-surrogates.any.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Close-reason-unpaired-surrogates.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Close-server-initiated-close.any.html?wpt_flags=h2 [ Failure ]
-crbug.com/1048761 external/wpt/websockets/Close-server-initiated-close.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Fuchsia ] external/wpt/websockets/Close-server-initiated-close.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Linux ] external/wpt/websockets/Close-server-initiated-close.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.12 ] external/wpt/websockets/Close-server-initiated-close.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.13 ] external/wpt/websockets/Close-server-initiated-close.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.14 ] external/wpt/websockets/Close-server-initiated-close.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.15 ] external/wpt/websockets/Close-server-initiated-close.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac11 ] external/wpt/websockets/Close-server-initiated-close.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Win ] external/wpt/websockets/Close-server-initiated-close.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Close-undefined.any.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Close-undefined.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Create-extensions-empty.any.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Create-extensions-empty.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Create-valid-url-array-protocols.any.html?wpt_flags=h2 [ Failure ]
-crbug.com/1048761 external/wpt/websockets/Create-valid-url-array-protocols.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Fuchsia ] external/wpt/websockets/Create-valid-url-array-protocols.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Linux ] external/wpt/websockets/Create-valid-url-array-protocols.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.12 ] external/wpt/websockets/Create-valid-url-array-protocols.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.13 ] external/wpt/websockets/Create-valid-url-array-protocols.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.14 ] external/wpt/websockets/Create-valid-url-array-protocols.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.15 ] external/wpt/websockets/Create-valid-url-array-protocols.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac11 ] external/wpt/websockets/Create-valid-url-array-protocols.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Win ] external/wpt/websockets/Create-valid-url-array-protocols.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Create-valid-url-binaryType-blob.any.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Create-valid-url-binaryType-blob.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Create-valid-url-protocol-setCorrectly.any.html?wpt_flags=h2 [ Failure ]
@@ -7085,17 +7121,45 @@
 crbug.com/1048761 external/wpt/websockets/Send-binary-arraybuffer.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Send-binary-arraybufferview-float32.any.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Send-binary-arraybufferview-float32.any.worker.html?wpt_flags=h2 [ Failure ]
-crbug.com/1048761 external/wpt/websockets/Send-binary-arraybufferview-float64.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Fuchsia ] external/wpt/websockets/Send-binary-arraybufferview-float64.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Linux ] external/wpt/websockets/Send-binary-arraybufferview-float64.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.12 ] external/wpt/websockets/Send-binary-arraybufferview-float64.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.13 ] external/wpt/websockets/Send-binary-arraybufferview-float64.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.14 ] external/wpt/websockets/Send-binary-arraybufferview-float64.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.15 ] external/wpt/websockets/Send-binary-arraybufferview-float64.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac11 ] external/wpt/websockets/Send-binary-arraybufferview-float64.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Win ] external/wpt/websockets/Send-binary-arraybufferview-float64.any.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Send-binary-arraybufferview-float64.any.worker.html?wpt_flags=h2 [ Failure ]
-crbug.com/1048761 external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.html?wpt_flags=h2 [ Failure ]
-crbug.com/1048761 external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Fuchsia ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Linux ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.12 ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.13 ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.14 ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.15 ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac11 ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Win ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Fuchsia ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Linux ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.12 ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.13 ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.14 ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.15 ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac11 ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Win ] external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Send-binary-arraybufferview-int32.any.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Send-binary-arraybufferview-int32.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Send-binary-arraybufferview-int8.any.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Send-binary-arraybufferview-int8.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Send-binary-arraybufferview-uint16-offset-length.any.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Send-binary-arraybufferview-uint16-offset-length.any.worker.html?wpt_flags=h2 [ Failure ]
-crbug.com/1048761 external/wpt/websockets/Send-binary-arraybufferview-uint32-offset.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Fuchsia ] external/wpt/websockets/Send-binary-arraybufferview-uint32-offset.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Linux ] external/wpt/websockets/Send-binary-arraybufferview-uint32-offset.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.12 ] external/wpt/websockets/Send-binary-arraybufferview-uint32-offset.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.13 ] external/wpt/websockets/Send-binary-arraybufferview-uint32-offset.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.14 ] external/wpt/websockets/Send-binary-arraybufferview-uint32-offset.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.15 ] external/wpt/websockets/Send-binary-arraybufferview-uint32-offset.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac11 ] external/wpt/websockets/Send-binary-arraybufferview-uint32-offset.any.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Win ] external/wpt/websockets/Send-binary-arraybufferview-uint32-offset.any.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Send-binary-arraybufferview-uint32-offset.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Send-binary-arraybufferview-uint8-offset-length.any.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Send-binary-arraybufferview-uint8-offset-length.any.worker.html?wpt_flags=h2 [ Failure ]
@@ -7113,7 +7177,14 @@
 crbug.com/1048761 external/wpt/websockets/Send-unicode-data.any.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Send-unicode-data.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/Send-unpaired-surrogates.any.html?wpt_flags=h2 [ Failure ]
-crbug.com/1048761 external/wpt/websockets/Send-unpaired-surrogates.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Fuchsia ] external/wpt/websockets/Send-unpaired-surrogates.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Linux ] external/wpt/websockets/Send-unpaired-surrogates.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.12 ] external/wpt/websockets/Send-unpaired-surrogates.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.13 ] external/wpt/websockets/Send-unpaired-surrogates.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.14 ] external/wpt/websockets/Send-unpaired-surrogates.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.15 ] external/wpt/websockets/Send-unpaired-surrogates.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac11 ] external/wpt/websockets/Send-unpaired-surrogates.any.worker.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Win ] external/wpt/websockets/Send-unpaired-surrogates.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/binaryType-wrong-value.any.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/binaryType-wrong-value.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/extended-payload-length.html?wpt_flags=h2 [ Failure ]
@@ -7125,7 +7196,14 @@
 crbug.com/1048761 external/wpt/websockets/interfaces/WebSocket/bufferedAmount/bufferedAmount-blob.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/interfaces/WebSocket/bufferedAmount/bufferedAmount-large.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/interfaces/WebSocket/bufferedAmount/bufferedAmount-unicode.html?wpt_flags=h2 [ Failure ]
-crbug.com/1048761 external/wpt/websockets/interfaces/WebSocket/events/018.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Fuchsia ] external/wpt/websockets/interfaces/WebSocket/events/018.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Linux ] external/wpt/websockets/interfaces/WebSocket/events/018.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.12 ] external/wpt/websockets/interfaces/WebSocket/events/018.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.13 ] external/wpt/websockets/interfaces/WebSocket/events/018.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.14 ] external/wpt/websockets/interfaces/WebSocket/events/018.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac10.15 ] external/wpt/websockets/interfaces/WebSocket/events/018.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Mac11 ] external/wpt/websockets/interfaces/WebSocket/events/018.html?wpt_flags=h2 [ Failure ]
+crbug.com/1048761 [ Win ] external/wpt/websockets/interfaces/WebSocket/events/018.html?wpt_flags=h2 [ Failure ]
 crbug.com/1048761 external/wpt/websockets/interfaces/WebSocket/send/006.html?wpt_flags=h2 [ Failure ]
 
 # Sheriff on 2021-05-26
@@ -7910,3 +7988,7 @@
 
 # Sheriff 2021-12-13
 crbug.com/1279586 [ Mac ] external/wpt/IndexedDB/idbobjectstore-rename-abort.html [ Failure ]
+
+# Sheriff 2021-12-15
+crbug.com/1280387 [ Mac ] external/wpt/dom/abort/event.any.html [ Failure ]
+crbug.com/1280387 [ Mac ] external/wpt/dom/abort/event.any.worker.html [ Failure ]
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 d016803..e222dc8 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
@@ -115,6 +115,13 @@
        {}
       ]
      ],
+     "multicol-with-text-change-role-relayout-crash.html": [
+      "d1ecd16dcb07af8f253045748b94290d2c485cfb",
+      [
+       null,
+       {}
+      ]
+     ],
      "serialize-with-no-document.html": [
       "f7719cf2ff1838b1125e08c5f86cf57910732c0e",
       [
@@ -270189,7 +270196,7 @@
       []
      ],
      "variable-definition-expected.txt": [
-      "3cccbb476ae7375e6a089eb8d8d4ebc043745e16",
+      "032ffaca31259370dfc911a808c72500fbda4e15",
       []
      ],
      "variable-external-font-face-01-ref.html": [
@@ -270232,10 +270239,6 @@
       "a612d33f093fd96de346e46ab49892df07bfd5dc",
       []
      ],
-     "variable-reference-expected.txt": [
-      "904ca589c18a9f4506588121f1450dc65ccee8ce",
-      []
-     ],
      "variable-reference-visited-ref.html": [
       "8647be19a8ccb033bc569fb9d1e1e444428f1655",
       []
@@ -275707,11 +275710,11 @@
     ],
     "abort": {
      "event.any-expected.txt": [
-      "4d7cd785dcd39836212986a8dad09d878597431f",
+      "e5730d03ed1eff8458351b1d9c1b5440d25fd134",
       []
      ],
      "event.any.worker-expected.txt": [
-      "4d7cd785dcd39836212986a8dad09d878597431f",
+      "e5730d03ed1eff8458351b1d9c1b5440d25fd134",
       []
      ]
     },
@@ -339188,37 +339191,12 @@
        ]
       ]
      },
-     "blocklisted-service-in-filter.https.window.js": [
-      "e17f7aac3b099e86323f54cd806dba158de6e0af",
+     "blocklisted-service-in-filter.https.html": [
+      "cb2f989a47778b8b8387e3d7fe3316b9c7b0f40e",
       [
-       "bluetooth/requestDevice/blocklisted-service-in-filter.https.window.html",
+       null,
        {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testharness.js"
-         ],
-         [
-          "script",
-          "/resources/testharnessreport.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
+        "testdriver": true
        }
       ]
      ],
@@ -378363,7 +378341,7 @@
       ]
      ],
      "variable-definition.html": [
-      "5ad71f7f82a03fe405c83e6ccbb26ba5a165741a",
+      "0f9cefc5e93c2391f6882a8fd5d074e27ac73668",
       [
        null,
        {}
@@ -384172,7 +384150,7 @@
       ]
      ],
      "event.any.js": [
-      "21c901c9fe9bcde84898754de33d62acb164a8b1",
+      "34af8ee5c560ae23aea4bb61ecf7420049fa411e",
       [
        "dom/abort/event.any.html",
        {}
@@ -384181,6 +384159,13 @@
        "dom/abort/event.any.worker.html",
        {}
       ]
+     ],
+     "reason-constructor.html": [
+      "0515165a0f6788ff66526ff04414040e5d1c61ba",
+      [
+       null,
+       {}
+      ]
      ]
     },
     "attributes-are-nodes.html": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/area-crash.html b/third_party/blink/web_tests/external/wpt/css/css-break/area-crash.html
new file mode 100644
index 0000000..d47327c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/area-crash.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1279525">
+<div style="columns:2;">
+  <area></area>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/dom/abort/event.any-expected.txt b/third_party/blink/web_tests/external/wpt/dom/abort/event.any-expected.txt
index 4d7cd78..e5730d03 100644
--- a/third_party/blink/web_tests/external/wpt/dom/abort/event.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/dom/abort/event.any-expected.txt
@@ -13,5 +13,7 @@
 FAIL throwIfAborted() should throw abort.reason if signal aborted assert_throws_exactly: function "() => signal.throwIfAborted()" threw object "TypeError: signal.throwIfAborted is not a function" but we expected it to throw object "Error: boom"
 FAIL throwIfAborted() should throw primitive abort.reason if signal aborted assert_throws_exactly: function "() => signal.throwIfAborted()" threw object "TypeError: signal.throwIfAborted is not a function" but we expected it to throw "hello"
 FAIL throwIfAborted() should not throw if signal not aborted controller.signal.throwIfAborted is not a function
+PASS AbortSignal.reason returns the same DOMException
+PASS AbortController.signal.reason returns the same DOMException
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/dom/abort/event.any.js b/third_party/blink/web_tests/external/wpt/dom/abort/event.any.js
index 21c901c..34af8ee 100644
--- a/third_party/blink/web_tests/external/wpt/dom/abort/event.any.js
+++ b/third_party/blink/web_tests/external/wpt/dom/abort/event.any.js
@@ -158,4 +158,33 @@
   controller.signal.throwIfAborted();
 }, "throwIfAborted() should not throw if signal not aborted");
 
+test(t => {
+  const signal = AbortSignal.abort();
+
+  assert_true(
+    signal.reason instanceof DOMException,
+    "signal.reason is a DOMException"
+  );
+  assert_equals(
+    signal.reason,
+    signal.reason,
+    "signal.reason returns the same DOMException"
+  );
+}, "AbortSignal.reason returns the same DOMException");
+
+test(t => {
+  const controller = new AbortController();
+  controller.abort();
+
+  assert_true(
+    controller.signal.reason instanceof DOMException,
+    "signal.reason is a DOMException"
+  );
+  assert_equals(
+    controller.signal.reason,
+    controller.signal.reason,
+    "signal.reason returns the same DOMException"
+  );
+}, "AbortController.signal.reason returns the same DOMException");
+
 done();
diff --git a/third_party/blink/web_tests/external/wpt/dom/abort/event.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/dom/abort/event.any.worker-expected.txt
index 4d7cd78..e5730d03 100644
--- a/third_party/blink/web_tests/external/wpt/dom/abort/event.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/dom/abort/event.any.worker-expected.txt
@@ -13,5 +13,7 @@
 FAIL throwIfAborted() should throw abort.reason if signal aborted assert_throws_exactly: function "() => signal.throwIfAborted()" threw object "TypeError: signal.throwIfAborted is not a function" but we expected it to throw object "Error: boom"
 FAIL throwIfAborted() should throw primitive abort.reason if signal aborted assert_throws_exactly: function "() => signal.throwIfAborted()" threw object "TypeError: signal.throwIfAborted is not a function" but we expected it to throw "hello"
 FAIL throwIfAborted() should not throw if signal not aborted controller.signal.throwIfAborted is not a function
+PASS AbortSignal.reason returns the same DOMException
+PASS AbortController.signal.reason returns the same DOMException
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/dom/abort/reason-constructor.html b/third_party/blink/web_tests/external/wpt/dom/abort/reason-constructor.html
new file mode 100644
index 0000000..0515165a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/dom/abort/reason-constructor.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>AbortSignal.reason constructor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="iframe"></iframe>
+<script>
+  test(() => {
+    const aborted = iframe.contentWindow.AbortSignal.abort();
+    assert_equals(aborted.reason.constructor, iframe.contentWindow.DOMException, "DOMException is using the correct global");
+  }, "AbortSignal.reason.constructor should be from iframe");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-events.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-events.tentative.html
index 99a1bee..827ebc3 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-events.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-events.tentative.html
@@ -36,6 +36,11 @@
   <option>same</option>
 </selectmenu>
 
+<selectmenu id="selectMenu4">
+  <option>one</option>
+  <option id="selectMenu4-option2">two</option>
+</selectmenu>
+
 <script>
 
   function clickOn(element) {
@@ -137,7 +142,8 @@
     await clickOn(selectMenu);
     assert_true(selectMenu.open);
     await test_driver.send_keys(selectMenu, KEY_CODE_MAP.ArrowDown);
-    assert_equals(input_event_count, 0, "input event shouldn't fire until popup is closed");
+    assert_equals(selectMenu.value, "two", "value should change when user switches options with arrow key");
+    assert_equals(input_event_count, 1, "input event should fire when user switches options with arrow key");
     assert_equals(change_event_count, 0, "change event shouldn't fire until popup is closed");
 
     await test_driver.send_keys(selectMenu, KEY_CODE_MAP.Enter);
@@ -171,4 +177,31 @@
     assert_equals(input_event_count, 1, "input event should have fired");
     assert_equals(change_event_count, 1, "change event should have fired");
   }, "<selectmenu> should fire input and change events even when new selected option has the same value as the old");
+
+  promise_test(async () => {
+    const selectMenu = document.getElementById("selectMenu4");
+    const selectMenuOption2 = document.getElementById("selectMenu4-option2");
+    let input_event_count = 0;
+    let change_event_count = 0;
+
+    selectMenu.addEventListener("input", (e) => {
+      assert_true(e.composed, "input event should be composed");
+      assert_equals(input_event_count, 0, "input event should not fire twice");
+      assert_equals(change_event_count, 0, "input event should not fire before change");
+      input_event_count++;
+    });
+
+    selectMenu.addEventListener("change", (e) => {
+      assert_false(e.composed, "change event should not be composed");
+      assert_equals(input_event_count, 1, "change event should fire after input");
+      assert_equals(change_event_count, 0, "change event should not fire twice");
+      change_event_count++;
+    });
+
+    await clickOn(selectMenu);
+    assert_true(selectMenu.open);
+    await clickOn(selectMenuOption2);
+    assert_equals(input_event_count, 1, "input event shouldn't fire when selected option didn't change");
+    assert_equals(change_event_count, 1, "change event shouldn't fire when selected option didn't change");
+  }, "<selectmenu> should fire input and change events when option in listbox is clicked");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js b/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js
index d3806c2..8a541cae 100644
--- a/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js
+++ b/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js
@@ -64,12 +64,13 @@
     this.mockVRService_ = new MockVRService();
   }
 
+  // WebXR Test API
   simulateDeviceConnection(init_params) {
-    return Promise.resolve(this.mockVRService_.addRuntime(init_params));
+    return Promise.resolve(this.mockVRService_._addRuntime(init_params));
   }
 
   disconnectAllDevices() {
-    this.mockVRService_.removeAllRuntimes();
+    this.mockVRService_._removeAllRuntimes();
     return Promise.resolve();
   }
 
@@ -102,6 +103,7 @@
     test_driver.click(button);
   }
 
+  // Helper method leveraged by chrome-specific setups.
   Debug(name, msg) {
     console.log(new Date().toISOString() + ' DEBUG[' + name + '] ' + msg);
   }
@@ -122,8 +124,8 @@
     this.interceptor_.start();
   }
 
-  // Test methods
-  addRuntime(fakeDeviceInit) {
+  // WebXR Test API Implementation Helpers
+  _addRuntime(fakeDeviceInit) {
     const runtime = new MockRuntime(fakeDeviceInit, this);
     this.runtimes_.push(runtime);
 
@@ -134,7 +136,7 @@
     return runtime;
   }
 
-  removeAllRuntimes() {
+  _removeAllRuntimes() {
     if (this.client_) {
       this.client_.onDeviceChanged();
     }
@@ -142,7 +144,7 @@
     this.runtimes_ = [];
   }
 
-  removeRuntime(device) {
+  _removeRuntime(device) {
     const index = this.runtimes_.indexOf(device);
     if (index >= 0) {
       this.runtimes_.splice(index, 1);
@@ -152,6 +154,7 @@
     }
   }
 
+  // VRService overrides
   setClient(client) {
     if (this.client_) {
       throw new Error("setClient should only be called once");
@@ -164,7 +167,7 @@
     const requests = [];
     // Request a session from all the runtimes.
     for (let i = 0; i < this.runtimes_.length; i++) {
-      requests[i] = this.runtimes_[i].requestRuntimeSession(sessionOptions);
+      requests[i] = this.runtimes_[i]._requestRuntimeSession(sessionOptions);
     }
 
     return Promise.all(requests).then((results) => {
@@ -191,15 +194,11 @@
     });
   }
 
-  exitPresent() {
-    return Promise.resolve();
-  }
-
   supportsSession(sessionOptions) {
     const requests = [];
     // Check supports on all the runtimes.
     for (let i = 0; i < this.runtimes_.length; i++) {
-      requests[i] = this.runtimes_[i].runtimeSupportsSession(sessionOptions);
+      requests[i] = this.runtimes_[i]._runtimeSupportsSession(sessionOptions);
     }
 
     return Promise.all(requests).then((results) => {
@@ -215,11 +214,16 @@
     });
   }
 
+  exitPresent() {
+    return Promise.resolve();
+  }
+
   setFramesThrottled(throttled) {
     this.setFramesThrottledImpl(throttled);
   }
 
-  // May be overridden by specific tests.
+  // We cannot override the mojom interceptors via the prototype; so this method
+  // and the above indirection exist to allow overrides by internal code.
   setFramesThrottledImpl(throttled) {}
 
   // Only handles asynchronous calls to makeXrCompatible. Synchronous calls are
@@ -247,6 +251,7 @@
     this.anchorOrigin_ = XRMathHelper.identity();
   }
 
+  // WebXR Test API (Anchors Extension)
   get deleted() {
     return this.deleted_;
   }
@@ -267,7 +272,7 @@
 
   stopTracking() {
     if(!this.deleted_) {
-      this.device_.deleteAnchorController(this.id_);
+      this.device_._deleteAnchorController(this.id_);
 
       this.deleted_ = true;
       this.dirty_ = true;
@@ -296,38 +301,21 @@
     return this.paused_;
   }
 
-  markProcessed() {
+  _markProcessed() {
     this.dirty_ = false;
   }
 
-  getAnchorOrigin() {
+  _getAnchorOrigin() {
     return this.anchorOrigin_;
   }
 }
 
-// Internal only for now, needs to be moved into WebXR Test API.
-class FakeXRHitTestSourceController {
-  constructor(id) {
-    this.id_ = id;
-    this.deleted_ = false;
-  }
-
-  get deleted() {
-    return this.deleted_;
-  }
-
-  // Internal setter:
-  set deleted(value) {
-    this.deleted_ = value;
-  }
-}
-
 // Implements XRFrameDataProvider and XRPresentationProvider. Maintains a mock
 // for XRPresentationProvider. Implements FakeXRDevice test API.
 class MockRuntime {
   // Mapping from string feature names to the corresponding mojo types.
   // This is exposed as a member for extensibility.
-  static featureToMojoMap = {
+  static _featureToMojoMap = {
     'viewer': vrMojom.XRSessionFeature.REF_SPACE_VIEWER,
     'local': vrMojom.XRSessionFeature.REF_SPACE_LOCAL,
     'local-floor': vrMojom.XRSessionFeature.REF_SPACE_LOCAL_FLOOR,
@@ -340,19 +328,19 @@
     'depth-sensing': vrMojom.XRSessionFeature.DEPTH,
   };
 
-  static sessionModeToMojoMap = {
+  static _sessionModeToMojoMap = {
     "inline": vrMojom.XRSessionMode.kInline,
     "immersive-vr": vrMojom.XRSessionMode.kImmersiveVr,
     "immersive-ar": vrMojom.XRSessionMode.kImmersiveAr,
   };
 
-  static environmentBlendModeToMojoMap = {
+  static _environmentBlendModeToMojoMap = {
     "opaque": vrMojom.XREnvironmentBlendMode.kOpaque,
     "alpha-blend": vrMojom.XREnvironmentBlendMode.kAlphaBlend,
     "additive": vrMojom.XREnvironmentBlendMode.kAdditive,
   };
 
-  static interactionModeToMojoMap = {
+  static _interactionModeToMojoMap = {
     "screen-space": vrMojom.XRInteractionMode.kScreenSpace,
     "world-space": vrMojom.XRInteractionMode.kWorldSpace,
   };
@@ -416,9 +404,9 @@
     // anything from the deviceInit
     if (this.supportedModes_.includes(vrMojom.XRSessionMode.kImmersiveVr) ||
         this.supportedModes_.includes(vrMojom.XRSessionMode.kImmersiveAr)) {
-      this.displayInfo_ = this.getImmersiveDisplayInfo();
+      this.displayInfo_ = this._getImmersiveDisplayInfo();
     } else if (this.supportedModes_.includes(vrMojom.XRSessionMode.kInline)) {
-      this.displayInfo_ = this.getNonImmersiveDisplayInfo();
+      this.displayInfo_ = this._getNonImmersiveDisplayInfo();
     } else {
       // This should never happen!
       console.error("Device has empty supported modes array!");
@@ -434,7 +422,7 @@
     }
 
     if (fakeDeviceInit.world) {
-      this.world_ = fakeDeviceInit.world;
+      this.setWorld(fakeDeviceInit.world);
     }
 
     if (fakeDeviceInit.depthSensingData) {
@@ -451,60 +439,16 @@
     this.setViews(fakeDeviceInit.views);
 
     // Need to support webVR which doesn't have a notion of features
-    this.setFeatures(fakeDeviceInit.supportedFeatures || []);
+    this._setFeatures(fakeDeviceInit.supportedFeatures || []);
   }
 
-  _convertModeToEnum(sessionMode) {
-    if (sessionMode in MockRuntime.sessionModeToMojoMap) {
-      return MockRuntime.sessionModeToMojoMap[sessionMode];
-    }
-
-    throw new TypeError("Unrecognized value for XRSessionMode enum: " + sessionMode);
-  }
-
-  _convertModesToEnum(sessionModes) {
-    return sessionModes.map(mode => this._convertModeToEnum(mode));
-  }
-
-  _convertBlendModeToEnum(blendMode) {
-    if (blendMode in MockRuntime.environmentBlendModeToMojoMap) {
-      return MockRuntime.environmentBlendModeToMojoMap[blendMode];
-    } else {
-      if (this.supportedModes_.includes(vrMojom.XRSessionMode.kImmersiveAr)) {
-        return vrMojom.XREnvironmentBlendMode.kAdditive;
-      } else if (this.supportedModes_.includes(
-            vrMojom.XRSessionMode.kImmersiveVr)) {
-        return vrMojom.XREnvironmentBlendMode.kOpaque;
-      }
-    }
-  }
-
-  _convertInteractionModeToEnum(interactionMode) {
-    if (interactionMode in MockRuntime.interactionModeToMojoMap) {
-      return MockRuntime.interactionModeToMojoMap[interactionMode];
-    } else {
-      return vrMojom.XRInteractionMode.kWorldSpace;
-    }
-  }
-
-  // Test API methods.
-  disconnect() {
-    this.service_.removeRuntime(this);
-    this.presentation_provider_.Close();
-    if (this.sessionClient_) {
-      this.sessionClient_.$.close();
-      this.sessionClient_ = null;
-    }
-
-    return Promise.resolve();
-  }
-
+  // WebXR Test API
   setViews(views) {
     if (views) {
       this.displayInfo_.views = [];
       this.viewOffsets_ = [];
       for (let i = 0; i < views.length; i++) {
-        this.displayInfo_.views[i] = this.getView(views[i]);
+        this.displayInfo_.views[i] = this._getView(views[i]);
         this.viewOffsets_[i] = composeGFXTransform(views[i].viewOffset);
       }
 
@@ -514,6 +458,17 @@
     }
   }
 
+  disconnect() {
+    this.service_._removeRuntime(this);
+    this.presentation_provider_._close();
+    if (this.sessionClient_) {
+      this.sessionClient_.$.close();
+      this.sessionClient_ = null;
+    }
+
+    return Promise.resolve();
+  }
+
   setViewerOrigin(origin, emulatedPosition = false) {
     const p = origin.position;
     const q = origin.orientation;
@@ -534,21 +489,23 @@
     this.pose_ = null;
   }
 
-  simulateVisibilityChange(visibilityState) {
-    let mojoState = null;
-    switch (visibilityState) {
-      case "visible":
-        mojoState = vrMojom.XRVisibilityState.VISIBLE;
-        break;
-      case "visible-blurred":
-        mojoState = vrMojom.XRVisibilityState.VISIBLE_BLURRED;
-        break;
-      case "hidden":
-        mojoState = vrMojom.XRVisibilityState.HIDDEN;
-        break;
+  setFloorOrigin(floorOrigin) {
+    if (!this.stageParameters_) {
+      this.stageParameters_ = default_stage_parameters;
+      this.stageParameters_.bounds = this.bounds_;
     }
-    if (mojoState && this.sessionClient_) {
-      this.sessionClient_.onVisibilityStateChanged(mojoState);
+
+    // floorOrigin is passed in as mojoFromFloor.
+    this.stageParameters_.mojoFromFloor =
+        {matrix: getMatrixFromTransform(floorOrigin)};
+
+    this._onStageParametersUpdated();
+  }
+
+  clearFloorOrigin() {
+    if (this.stageParameters_) {
+      this.stageParameters_ = null;
+      this._onStageParametersUpdated();
     }
   }
 
@@ -567,39 +524,32 @@
     // floorLevel transform is set, but we won't update them just yet.
     if (this.stageParameters_) {
       this.stageParameters_.bounds = this.bounds_;
-      this.onStageParametersUpdated();
+      this._onStageParametersUpdated();
     }
   }
 
-  setFloorOrigin(floorOrigin) {
-    if (!this.stageParameters_) {
-      this.stageParameters_ = default_stage_parameters;
-      this.stageParameters_.bounds = this.bounds_;
-    }
-
-    // floorOrigin is passed in as mojoFromFloor.
-    this.stageParameters_.mojoFromFloor =
-        {matrix: getMatrixFromTransform(floorOrigin)};
-
-    this.onStageParametersUpdated();
-  }
-
-  clearFloorOrigin() {
-    if (this.stageParameters_) {
-      this.stageParameters_ = null;
-      this.onStageParametersUpdated();
-    }
-  }
-
-  onStageParametersUpdated() {
-    // Indicate for the frame loop that the stage parameters have been updated.
-    this.stageParametersId_++;
-  }
-
   simulateResetPose() {
     this.send_mojo_space_reset_ = true;
   }
 
+  simulateVisibilityChange(visibilityState) {
+    let mojoState = null;
+    switch (visibilityState) {
+      case "visible":
+        mojoState = vrMojom.XRVisibilityState.VISIBLE;
+        break;
+      case "visible-blurred":
+        mojoState = vrMojom.XRVisibilityState.VISIBLE_BLURRED;
+        break;
+      case "hidden":
+        mojoState = vrMojom.XRVisibilityState.HIDDEN;
+        break;
+    }
+    if (mojoState && this.sessionClient_) {
+      this.sessionClient_.onVisibilityStateChanged(mojoState);
+    }
+  }
+
   simulateInputSourceConnection(fakeInputSourceInit) {
     const index = this.next_input_source_index_;
     this.next_input_source_index_++;
@@ -609,6 +559,16 @@
     return source;
   }
 
+  // WebXR Test API Hit Test extensions
+  setWorld(world) {
+    this.world_ = world;
+  }
+
+  clearWorld() {
+    this.world_ = null;
+  }
+
+  // WebXR Test API Anchor extensions
   setAnchorCreationCallback(callback) {
     this.anchor_creation_callback_ = callback;
   }
@@ -617,6 +577,7 @@
     this.hit_test_source_creation_callback_ = callback;
   }
 
+  // WebXR Test API Lighting estimation extensions
   setLightEstimate(fakeXrLightEstimateInit) {
     if (!fakeXrLightEstimateInit.sphericalHarmonicsCoefficients) {
       throw new TypeError("sphericalHarmonicsCoefficients must be set");
@@ -679,6 +640,7 @@
     }
   }
 
+  // WebXR Test API depth Sensing Extensions
   setDepthSensingData(depthSensingData) {
     for(const key of ["depthData", "normDepthBufferFromNormView", "rawValueToMeters", "width", "height"]) {
       if(!(key in depthSensingData)) {
@@ -705,9 +667,47 @@
     this.depthSensingDataDirty_ = true;
   }
 
-  // Helper methods
-  getNonImmersiveDisplayInfo() {
-    const displayInfo = this.getImmersiveDisplayInfo();
+  // Internal Implementation/Helper Methods
+  _convertModeToEnum(sessionMode) {
+    if (sessionMode in MockRuntime._sessionModeToMojoMap) {
+      return MockRuntime._sessionModeToMojoMap[sessionMode];
+    }
+
+    throw new TypeError("Unrecognized value for XRSessionMode enum: " + sessionMode);
+  }
+
+  _convertModesToEnum(sessionModes) {
+    return sessionModes.map(mode => this._convertModeToEnum(mode));
+  }
+
+  _convertBlendModeToEnum(blendMode) {
+    if (blendMode in MockRuntime._environmentBlendModeToMojoMap) {
+      return MockRuntime._environmentBlendModeToMojoMap[blendMode];
+    } else {
+      if (this.supportedModes_.includes(vrMojom.XRSessionMode.kImmersiveAr)) {
+        return vrMojom.XREnvironmentBlendMode.kAdditive;
+      } else if (this.supportedModes_.includes(
+            vrMojom.XRSessionMode.kImmersiveVr)) {
+        return vrMojom.XREnvironmentBlendMode.kOpaque;
+      }
+    }
+  }
+
+  _convertInteractionModeToEnum(interactionMode) {
+    if (interactionMode in MockRuntime._interactionModeToMojoMap) {
+      return MockRuntime._interactionModeToMojoMap[interactionMode];
+    } else {
+      return vrMojom.XRInteractionMode.kWorldSpace;
+    }
+  }
+
+  _onStageParametersUpdated() {
+    // Indicate for the frame loop that the stage parameters have been updated.
+    this.stageParametersId_++;
+  }
+
+  _getNonImmersiveDisplayInfo() {
+    const displayInfo = this._getImmersiveDisplayInfo();
 
     displayInfo.capabilities.canPresent = false;
     displayInfo.views = [];
@@ -716,7 +716,7 @@
   }
 
   // Function to generate some valid display information for the device.
-  getImmersiveDisplayInfo() {
+  _getImmersiveDisplayInfo() {
     const viewport_size = 20;
     return {
       displayName: 'FakeDevice',
@@ -760,7 +760,7 @@
 
   // This function converts between the matrix provided by the WebXR test API
   // and the internal data representation.
-  getView(fakeXRViewInit) {
+  _getView(fakeXRViewInit) {
     let fov = null;
 
     if (fakeXRViewInit.fieldOfView) {
@@ -817,10 +817,10 @@
     };
   }
 
-  setFeatures(supportedFeatures) {
+  _setFeatures(supportedFeatures) {
     function convertFeatureToMojom(feature) {
-      if (feature in MockRuntime.featureToMojoMap) {
-        return MockRuntime.featureToMojoMap[feature];
+      if (feature in MockRuntime._featureToMojoMap) {
+        return MockRuntime._featureToMojoMap[feature];
       } else {
         return vrMojom.XRSessionFeature.INVALID;
       }
@@ -837,23 +837,22 @@
   }
 
   // These methods are intended to be used by MockXRInputSource only.
-  addInputSource(source) {
+  _addInputSource(source) {
     if (!this.input_sources_.has(source.source_id_)) {
       this.input_sources_.set(source.source_id_, source);
     }
   }
 
-  removeInputSource(source) {
+  _removeInputSource(source) {
     this.input_sources_.delete(source.source_id_);
   }
 
   // These methods are intended to be used by FakeXRAnchorController only.
-  deleteAnchorController(controllerId) {
+  _deleteAnchorController(controllerId) {
     this.anchor_controllers_.delete(controllerId);
   }
 
   // Extension point for non-standard modules.
-
   _injectAdditionalFrameData(options, frameData) {
   }
 
@@ -879,7 +878,7 @@
         if (this.input_sources_.size > 0) {
           input_state = [];
           for (const input_source of this.input_sources_.values()) {
-            input_state.push(input_source.getInputSourceState());
+            input_state.push(input_source._getInputSourceState());
           }
         }
 
@@ -945,21 +944,6 @@
 
   setInputSourceButtonListener(listener) { listener.$.close(); }
 
-  // Note that if getEnvironmentProvider hasn't finished running yet this will
-  // be undefined. It's recommended that you allow a successful task to post
-  // first before attempting to close.
-  closeEnvironmentIntegrationProvider() {
-    if (this.environmentProviderReceiver_) {
-      this.environmentProviderReceiver_.$.close();
-    }
-  }
-
-  closeDataProvider() {
-    this.closeEnvironmentIntegrationProvider();
-    this.dataProviderReceiver_.$.close();
-    this.sessionOptions_ = null;
-  }
-
   // XREnvironmentIntegrationProvider implementation:
   subscribeToHitTest(nativeOriginInformation, entityTypes, ray) {
     if (!this.supportedModes_.includes(vrMojom.XRSessionMode.kImmersiveAr)) {
@@ -1132,8 +1116,8 @@
   detachAnchor(anchorId) {}
 
   // Utility function
-  requestRuntimeSession(sessionOptions) {
-    return this.runtimeSupportsSession(sessionOptions).then((result) => {
+  _requestRuntimeSession(sessionOptions) {
+    return this._runtimeSupportsSession(sessionOptions).then((result) => {
       // The JavaScript bindings convert c_style_names to camelCase names.
       const options = {
         transportMethod:
@@ -1146,8 +1130,8 @@
       let submit_frame_sink;
       if (result.supportsSession) {
         submit_frame_sink = {
-          clientReceiver: this.presentation_provider_.getClientReceiver(),
-          provider: this.presentation_provider_.bindProvider(sessionOptions),
+          clientReceiver: this.presentation_provider_._getClientReceiver(),
+          provider: this.presentation_provider_._bindProvider(sessionOptions),
           transportOptions: options
         };
 
@@ -1205,7 +1189,7 @@
     });
   }
 
-  runtimeSupportsSession(options) {
+  _runtimeSupportsSession(options) {
     let result = this.supportedModes_.includes(options.mode);
 
     if (options.requiredFeatures.includes(vrMojom.XRSessionFeature.DEPTH)
@@ -1261,10 +1245,10 @@
         if(!controller.paused) {
           anchorData.mojoFromAnchor = getPoseFromTransform(
               XRMathHelper.decomposeRigidTransform(
-                  controller.getAnchorOrigin()));
+                  controller._getAnchorOrigin()));
         }
 
-        controller.markProcessed();
+        controller._markProcessed();
 
         frameData.anchorsData.updatedAnchorsData.push(anchorData);
       }
@@ -1651,7 +1635,7 @@
     this.desc_dirty_ = true;
   }
 
-  // Webxr-test-api
+  // WebXR Test API
   setHandedness(handedness) {
     if (this.handedness_ != handedness) {
       this.desc_dirty_ = true;
@@ -1700,11 +1684,11 @@
   }
 
   disconnect() {
-    this.pairedDevice_.removeInputSource(this);
+    this.pairedDevice_._removeInputSource(this);
   }
 
   reconnect() {
-    this.pairedDevice_.addInputSource(this);
+    this.pairedDevice_._addInputSource(this);
   }
 
   startSelection() {
@@ -1743,7 +1727,7 @@
     }
 
     const supported_button_map = {};
-    this.gamepad_ = this.getEmptyGamepad();
+    this.gamepad_ = this._getEmptyGamepad();
     for (let i = 0; i < supportedButtons.length; i++) {
       const buttonType = supportedButtons[i].buttonType;
       this.supported_buttons_.push(buttonType);
@@ -1759,11 +1743,11 @@
     });
 
     // Now add the rest of our buttons
-    this.addGamepadButton(supported_button_map['grip']);
-    this.addGamepadButton(supported_button_map['touchpad']);
-    this.addGamepadButton(supported_button_map['thumbstick']);
-    this.addGamepadButton(supported_button_map['optional-button']);
-    this.addGamepadButton(supported_button_map['optional-thumbstick']);
+    this._addGamepadButton(supported_button_map['grip']);
+    this._addGamepadButton(supported_button_map['touchpad']);
+    this._addGamepadButton(supported_button_map['thumbstick']);
+    this._addGamepadButton(supported_button_map['optional-button']);
+    this._addGamepadButton(supported_button_map['optional-thumbstick']);
 
     // Finally, back-fill placeholder buttons/axes
     for (let i = 0; i < this.gamepad_.buttons.length; i++) {
@@ -1788,15 +1772,15 @@
       throw new Error("Tried to update state on an unsupported button");
     }
 
-    const buttonIndex = this.getButtonIndex(buttonState.buttonType);
-    const axesStartIndex = this.getAxesStartIndex(buttonState.buttonType);
+    const buttonIndex = this._getButtonIndex(buttonState.buttonType);
+    const axesStartIndex = this._getAxesStartIndex(buttonState.buttonType);
 
     if (buttonIndex == -1) {
       throw new Error("Unknown Button Type!");
     }
 
     // is this a 'squeeze' button?
-    if (buttonIndex === this.getButtonIndex('grip')) {
+    if (buttonIndex === this._getButtonIndex('grip')) {
       // squeeze
       if (buttonState.pressed) {
         this.primary_squeeze_pressed_ = true;
@@ -1819,8 +1803,13 @@
     }
   }
 
+  // DOM Overlay Extensions
+  setOverlayPointerPosition(x, y) {
+    this.overlay_pointer_position_ = {x: x, y: y};
+  }
+
   // Helpers for Mojom
-  getInputSourceState() {
+  _getInputSourceState() {
     const input_state = {};
 
     input_state.sourceId = this.source_id_;
@@ -1925,11 +1914,7 @@
     return input_state;
   }
 
-  setOverlayPointerPosition(x, y) {
-    this.overlay_pointer_position_ = {x: x, y: y};
-  }
-
-  getEmptyGamepad() {
+  _getEmptyGamepad() {
     // Mojo complains if some of the properties on Gamepad are null, so set
     // everything to reasonable defaults that tests can override.
     const gamepad = {
@@ -1957,13 +1942,13 @@
     return gamepad;
   }
 
-  addGamepadButton(buttonState) {
+  _addGamepadButton(buttonState) {
     if (buttonState == null) {
       return;
     }
 
-    const buttonIndex = this.getButtonIndex(buttonState.buttonType);
-    const axesStartIndex = this.getAxesStartIndex(buttonState.buttonType);
+    const buttonIndex = this._getButtonIndex(buttonState.buttonType);
+    const axesStartIndex = this._getAxesStartIndex(buttonState.buttonType);
 
     if (buttonIndex == -1) {
       throw new Error("Unknown Button Type!");
@@ -1983,7 +1968,7 @@
   }
 
   // General Helper methods
-  getButtonIndex(buttonType) {
+  _getButtonIndex(buttonType) {
     switch (buttonType) {
       case 'grip':
         return 1;
@@ -2000,7 +1985,7 @@
     }
   }
 
-  getAxesStartIndex(buttonType) {
+  _getAxesStartIndex(buttonType) {
     switch (buttonType) {
       case 'touchpad':
         return 0;
@@ -2019,6 +2004,22 @@
 }
 
 // Mojo helper classes
+class FakeXRHitTestSourceController {
+  constructor(id) {
+    this.id_ = id;
+    this.deleted_ = false;
+  }
+
+  get deleted() {
+    return this.deleted_;
+  }
+
+  // Internal setter:
+  set deleted(value) {
+    this.deleted_ = value;
+  }
+}
+
 class MockXRPresentationProvider {
   constructor() {
     this.receiver_ = null;
@@ -2026,7 +2027,7 @@
     this.missing_frame_count_ = 0;
   }
 
-  bindProvider() {
+  _bindProvider() {
     const provider = new vrMojom.XRPresentationProviderRemote();
 
     if (this.receiver_) {
@@ -2037,14 +2038,14 @@
     return provider;
   }
 
-  getClientReceiver() {
+  _getClientReceiver() {
     this.submitFrameClient_ = new vrMojom.XRPresentationClientRemote();
     return this.submitFrameClient_.$.bindNewPipeAndPassReceiver();
   }
 
+  // XRPresentationProvider mojo implementation
   updateLayerBounds(frameId, leftBounds, rightBounds, sourceSize) {}
 
-  // XRPresentationProvider mojo implementation
   submitFrameMissing(frameId, mailboxHolder, timeWaited) {
     this.missing_frame_count_++;
   }
@@ -2066,7 +2067,7 @@
   submitFrameDrawnIntoTexture(frameId, syncToken, timeWaited) {}
 
   // Utility methods
-  Close() {
+  _close() {
     if (this.receiver_) {
       this.receiver_.$.close();
     }
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/issues/client-hint-accept-ch-meta-tag-invalid-origin-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/issues/client-hint-accept-ch-meta-tag-invalid-origin-expected.txt
new file mode 100644
index 0000000..34fd661a
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/issues/client-hint-accept-ch-meta-tag-invalid-origin-expected.txt
@@ -0,0 +1,18 @@
+Test Invalid Origin
+Inspector issue: {
+    issue : {
+        code : ClientHintIssue
+        details : {
+            clientHintIssueDetails : {
+                clientHintIssueReason : MetaTagAllowListInvalidOrigin
+                sourceCodeLocation : {
+                    columnNumber : 0
+                    lineNumber : 0
+                    scriptId : <string>
+                    url : https://devtools.test:8443/inspector-protocol/resources/client-hint-accept-ch-meta-tag-invalid-origin.html
+                }
+            }
+        }
+    }
+}
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/issues/client-hint-accept-ch-meta-tag-invalid-origin.js b/third_party/blink/web_tests/http/tests/inspector-protocol/issues/client-hint-accept-ch-meta-tag-invalid-origin.js
new file mode 100644
index 0000000..89b1bb4c
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/issues/client-hint-accept-ch-meta-tag-invalid-origin.js
@@ -0,0 +1,10 @@
+(async function (testRunner) {
+  const { page, dp } = await testRunner.startBlank(`Test Invalid Origin`);
+  await dp.Network.enable();
+  await dp.Audits.enable();
+  const promise = dp.Audits.onceIssueAdded();
+  page.navigate('https://devtools.test:8443/inspector-protocol/resources/client-hint-accept-ch-meta-tag-invalid-origin.html');
+  const result = await promise;
+  testRunner.log(result.params, "Inspector issue: ");
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/issues/client-hint-accept-ch-meta-tag-javascript-injection-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/issues/client-hint-accept-ch-meta-tag-javascript-injection-expected.txt
new file mode 100644
index 0000000..664e143c
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/issues/client-hint-accept-ch-meta-tag-javascript-injection-expected.txt
@@ -0,0 +1,18 @@
+Test JS Injection
+Inspector issue: {
+    issue : {
+        code : ClientHintIssue
+        details : {
+            clientHintIssueDetails : {
+                clientHintIssueReason : MetaTagModifiedHTML
+                sourceCodeLocation : {
+                    columnNumber : 33
+                    lineNumber : 6
+                    scriptId : <string>
+                    url : https://devtools.test:8443/inspector-protocol/resources/client-hint-accept-ch-meta-tag-javascript-injection.html
+                }
+            }
+        }
+    }
+}
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/issues/client-hint-accept-ch-meta-tag-javascript-injection.js b/third_party/blink/web_tests/http/tests/inspector-protocol/issues/client-hint-accept-ch-meta-tag-javascript-injection.js
new file mode 100644
index 0000000..1649e71f
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/issues/client-hint-accept-ch-meta-tag-javascript-injection.js
@@ -0,0 +1,10 @@
+(async function (testRunner) {
+  const { page, dp } = await testRunner.startBlank(`Test JS Injection`);
+  await dp.Network.enable();
+  await dp.Audits.enable();
+  const promise = dp.Audits.onceIssueAdded();
+  page.navigate('https://devtools.test:8443/inspector-protocol/resources/client-hint-accept-ch-meta-tag-javascript-injection.html');
+  const result = await promise;
+  testRunner.log(result.params, "Inspector issue: ");
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-invalid-frame-url-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-invalid-frame-url-expected.txt
index c2fe1be..aa8dacf0 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-invalid-frame-url-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-invalid-frame-url-expected.txt
@@ -2,7 +2,7 @@
 Response for invalid target and invalid url: {
     error : {
         code : -32602
-        message : The url must be valid and have scheme http or https
+        message : The url must be valid
     }
     id : <number>
     sessionId : <string>
@@ -26,7 +26,7 @@
 Response for valid target and invalid url: {
     error : {
         code : -32602
-        message : The url must be valid and have scheme http or https
+        message : The url must be valid
     }
     id : <number>
     sessionId : <string>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/resources/client-hint-accept-ch-meta-tag-invalid-origin.html b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/client-hint-accept-ch-meta-tag-invalid-origin.html
new file mode 100644
index 0000000..59646ff
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/client-hint-accept-ch-meta-tag-invalid-origin.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html> 
+<html>
+  <head>
+    <meta name="Accept-CH" content="Sec-CH-DPR=(*)">
+  </head>
+  <body>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/resources/client-hint-accept-ch-meta-tag-javascript-injection.html b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/client-hint-accept-ch-meta-tag-javascript-injection.html
new file mode 100644
index 0000000..29fcd8d
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/client-hint-accept-ch-meta-tag-javascript-injection.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html> 
+<html>
+  <head>
+  </head>
+  <body>
+    <script type="text/javascript">
+        document.head.innerHTML += '<meta name="Accept-CH" content="Sec-CH-DPR=(https://foo.bar/)">';
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/dom/abort/event.any-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/dom/abort/event.any-expected.txt
new file mode 100644
index 0000000..e5730d03
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/dom/abort/event.any-expected.txt
@@ -0,0 +1,19 @@
+This is a testharness.js-based test.
+PASS AbortController abort() should fire event synchronously
+PASS controller.signal should always return the same object
+PASS controller.abort() should do nothing the second time it is called
+PASS event handler should not be called if added after controller.abort()
+PASS the abort event should have the right properties
+PASS AbortController abort(reason) should set signal.reason
+PASS aborting AbortController without reason creates an "AbortError" DOMException
+PASS AbortController abort(undefined) creates an "AbortError" DOMException
+PASS AbortController abort(null) should set signal.reason
+PASS static aborting signal should have right properties
+PASS static aborting signal with reason should set signal.reason
+FAIL throwIfAborted() should throw abort.reason if signal aborted assert_throws_exactly: function "() => signal.throwIfAborted()" threw object "TypeError: signal.throwIfAborted is not a function" but we expected it to throw object "Error: boom"
+FAIL throwIfAborted() should throw primitive abort.reason if signal aborted assert_throws_exactly: function "() => signal.throwIfAborted()" threw object "TypeError: signal.throwIfAborted is not a function" but we expected it to throw "hello"
+FAIL throwIfAborted() should not throw if signal not aborted controller.signal.throwIfAborted is not a function
+PASS AbortSignal.reason returns the same DOMException
+PASS AbortController.signal.reason returns the same DOMException
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/dom/abort/event.any.worker-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/dom/abort/event.any.worker-expected.txt
new file mode 100644
index 0000000..e5730d03
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/dom/abort/event.any.worker-expected.txt
@@ -0,0 +1,19 @@
+This is a testharness.js-based test.
+PASS AbortController abort() should fire event synchronously
+PASS controller.signal should always return the same object
+PASS controller.abort() should do nothing the second time it is called
+PASS event handler should not be called if added after controller.abort()
+PASS the abort event should have the right properties
+PASS AbortController abort(reason) should set signal.reason
+PASS aborting AbortController without reason creates an "AbortError" DOMException
+PASS AbortController abort(undefined) creates an "AbortError" DOMException
+PASS AbortController abort(null) should set signal.reason
+PASS static aborting signal should have right properties
+PASS static aborting signal with reason should set signal.reason
+FAIL throwIfAborted() should throw abort.reason if signal aborted assert_throws_exactly: function "() => signal.throwIfAborted()" threw object "TypeError: signal.throwIfAborted is not a function" but we expected it to throw object "Error: boom"
+FAIL throwIfAborted() should throw primitive abort.reason if signal aborted assert_throws_exactly: function "() => signal.throwIfAborted()" threw object "TypeError: signal.throwIfAborted is not a function" but we expected it to throw "hello"
+FAIL throwIfAborted() should not throw if signal not aborted controller.signal.throwIfAborted is not a function
+PASS AbortSignal.reason returns the same DOMException
+PASS AbortController.signal.reason returns the same DOMException
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/external/wpt/dom/abort/event.any-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.14/external/wpt/dom/abort/event.any-expected.txt
new file mode 100644
index 0000000..4d7cd78
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/external/wpt/dom/abort/event.any-expected.txt
@@ -0,0 +1,17 @@
+This is a testharness.js-based test.
+PASS AbortController abort() should fire event synchronously
+PASS controller.signal should always return the same object
+PASS controller.abort() should do nothing the second time it is called
+PASS event handler should not be called if added after controller.abort()
+PASS the abort event should have the right properties
+PASS AbortController abort(reason) should set signal.reason
+PASS aborting AbortController without reason creates an "AbortError" DOMException
+PASS AbortController abort(undefined) creates an "AbortError" DOMException
+PASS AbortController abort(null) should set signal.reason
+PASS static aborting signal should have right properties
+PASS static aborting signal with reason should set signal.reason
+FAIL throwIfAborted() should throw abort.reason if signal aborted assert_throws_exactly: function "() => signal.throwIfAborted()" threw object "TypeError: signal.throwIfAborted is not a function" but we expected it to throw object "Error: boom"
+FAIL throwIfAborted() should throw primitive abort.reason if signal aborted assert_throws_exactly: function "() => signal.throwIfAborted()" threw object "TypeError: signal.throwIfAborted is not a function" but we expected it to throw "hello"
+FAIL throwIfAborted() should not throw if signal not aborted controller.signal.throwIfAborted is not a function
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/external/wpt/dom/abort/event.any.worker-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.14/external/wpt/dom/abort/event.any.worker-expected.txt
new file mode 100644
index 0000000..4d7cd78
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/external/wpt/dom/abort/event.any.worker-expected.txt
@@ -0,0 +1,17 @@
+This is a testharness.js-based test.
+PASS AbortController abort() should fire event synchronously
+PASS controller.signal should always return the same object
+PASS controller.abort() should do nothing the second time it is called
+PASS event handler should not be called if added after controller.abort()
+PASS the abort event should have the right properties
+PASS AbortController abort(reason) should set signal.reason
+PASS aborting AbortController without reason creates an "AbortError" DOMException
+PASS AbortController abort(undefined) creates an "AbortError" DOMException
+PASS AbortController abort(null) should set signal.reason
+PASS static aborting signal should have right properties
+PASS static aborting signal with reason should set signal.reason
+FAIL throwIfAborted() should throw abort.reason if signal aborted assert_throws_exactly: function "() => signal.throwIfAborted()" threw object "TypeError: signal.throwIfAborted is not a function" but we expected it to throw object "Error: boom"
+FAIL throwIfAborted() should throw primitive abort.reason if signal aborted assert_throws_exactly: function "() => signal.throwIfAborted()" threw object "TypeError: signal.throwIfAborted is not a function" but we expected it to throw "hello"
+FAIL throwIfAborted() should not throw if signal not aborted controller.signal.throwIfAborted is not a function
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/wpt_internal/webxr/resources/xr-internal-device-mocking.js b/third_party/blink/web_tests/wpt_internal/webxr/resources/xr-internal-device-mocking.js
index e5bdd08..78a155f 100644
--- a/third_party/blink/web_tests/wpt_internal/webxr/resources/xr-internal-device-mocking.js
+++ b/third_party/blink/web_tests/wpt_internal/webxr/resources/xr-internal-device-mocking.js
@@ -13,6 +13,23 @@
   return this.presentation_provider_.missing_frame_count_;
 };
 
+
+
+// Note that if getEnvironmentProvider hasn't finished running yet this will be
+// undefined. It's recommended that you allow a successful task to post first
+// before attempting to close.
+MockRuntime.prototype.closeEnvironmentIntegrationProvider = function() {
+  if (this.environmentProviderReceiver_) {
+    this.environmentProviderReceiver_.$.close();
+  }
+};
+
+MockRuntime.prototype.closeDataProvider = function() {
+  this.closeEnvironmentIntegrationProvider();
+  this.dataProviderReceiver_.$.close();
+  this.sessionOptions_ = null;
+};
+
 MockRuntime.prototype._injectAdditionalFrameData_preLightEstimation = MockRuntime.prototype._injectAdditionalFrameData;
 MockRuntime.prototype._injectAdditionalFrameData = function(options, frameData) {
   this._injectAdditionalFrameData_preLightEstimation(options, frameData);
diff --git a/third_party/harfbuzz-ng/BUILD.gn b/third_party/harfbuzz-ng/BUILD.gn
index 43541ae..c46820fc 100644
--- a/third_party/harfbuzz-ng/BUILD.gn
+++ b/third_party/harfbuzz-ng/BUILD.gn
@@ -142,6 +142,7 @@
       "src/src/hb-ot-color-sbix-table.hh",
       "src/src/hb-ot-color-svg-table.hh",
       "src/src/hb-ot-color.cc",
+      "src/src/hb-ot-color.h",
       "src/src/hb-ot-deprecated.h",
       "src/src/hb-ot-face-table-list.hh",
       "src/src/hb-ot-face.cc",
@@ -282,8 +283,6 @@
       "src/src/hb-gobject.h",
       "src/src/hb-graphite2.cc",
       "src/src/hb-graphite2.h",
-      "src/src/hb-ot-color.cc",
-      "src/src/hb-ot-color.h",
       "src/src/hb-ot-shape-complex-arabic-win1256.hh",
       "src/src/hb-style.cc",
       "src/src/hb-style.h",
diff --git a/third_party/tflite/README.chromium b/third_party/tflite/README.chromium
index 45507e6d..9305f1e4 100644
--- a/third_party/tflite/README.chromium
+++ b/third_party/tflite/README.chromium
@@ -1,8 +1,8 @@
 Name: TensorFlow Lite
 Short Name: tflite
 URL: https://github.com/tensorflow/tensorflow
-Version: aa79adb41c2f60c81d5d46d994c061abab686bb1
-Date: 2021/12/06
+Version: dd57f5328f37a81197b0dadd052e05c9d9461b16
+Date: 2021/12/14
 License: Apache 2.0
 License File: LICENSE
 Security Critical: Yes
diff --git a/tools/android/test_health/java_test_utils.py b/tools/android/test_health/java_test_utils.py
index 74b6b55..fd870ffe 100644
--- a/tools/android/test_health/java_test_utils.py
+++ b/tools/android/test_health/java_test_utils.py
@@ -26,6 +26,7 @@
                       'src').resolve(strict=True)
 if str(_JAVALANG_SRC_PATH) not in sys.path:
     sys.path.insert(1, str(_JAVALANG_SRC_PATH))
+import javalang
 from javalang.tree import (Annotation, ClassDeclaration, CompilationUnit,
                            MethodDeclaration, PackageDeclaration)
 
@@ -49,7 +50,68 @@
     """The number of test cases annotated with @FlakyTest."""
 
 
-def get_java_package_name(java_ast: CompilationUnit) -> Optional[str]:
+def get_java_test_health(test_path: pathlib.Path) -> JavaTestHealth:
+    """Gets test health information for a Java test.
+
+    The Java test health information includes the Java package, if applicable,
+    that the test belongs to and the number of test cases marked as disabled,
+    conditionally-disabled and flaky.
+
+    Args:
+        test_path:
+            The path to a Java test class.
+
+    Returns:
+        Test health information obtained from the abstract syntax tree (AST).
+    """
+    java_file_contents: str = test_path.resolve(strict=True).read_text()
+
+    try:
+        java_ast: CompilationUnit = javalang.parse.parse(java_file_contents)
+    except javalang.parser.JavaSyntaxError as syntax_error:
+        raise JavaSyntaxError(syntax_error.description,
+                              line_num=syntax_error.at.position.line,
+                              column_num=syntax_error.at.position.column,
+                              file_path=test_path,
+                              java_src_code=java_file_contents) from None
+
+    return _get_java_test_health(java_ast)
+
+
+def _get_java_test_health(java_ast: CompilationUnit) -> JavaTestHealth:
+    """Gets test health information from the AST of a Java test.
+
+    The Java test health information includes the Java package, if applicable,
+    that the test belongs to and the number of test cases marked as disabled,
+    conditionally-disabled and flaky.
+
+    Args:
+        java_ast:
+            The abstract syntax tree (AST) of the Java test.
+
+    Returns:
+        Java test health information based on the test's AST.
+    """
+    annotation_counter = collections.Counter()
+
+    java_classes: List[ClassDeclaration] = java_ast.types
+    for java_class in java_classes:
+        # TODO(crbug.com/1254072): Count test cases in the class instead.
+        annotation_counter.update(_count_annotations(java_class.annotations))
+
+        java_methods: List[MethodDeclaration] = java_class.methods
+        for java_method in java_methods:
+            annotation_counter.update(
+                _count_annotations(java_method.annotations))
+
+    return JavaTestHealth(
+        java_package=_get_java_package_name(java_ast),
+        disabled_tests_count=annotation_counter[_DISABLED_TEST_ANNOTATION],
+        disable_if_tests_count=annotation_counter[_DISABLE_IF_TEST_ANNOTATION],
+        flaky_tests_count=annotation_counter[_FLAKY_TEST_ANNOTATION])
+
+
+def _get_java_package_name(java_ast: CompilationUnit) -> Optional[str]:
     """Gets the Java package name, if specified, from an abstract syntax tree.
 
     If the abstract syntax tree (AST) does not contain a package declaration in
@@ -67,40 +129,6 @@
     return package.name if package else None
 
 
-def get_java_test_health(java_ast: CompilationUnit) -> JavaTestHealth:
-    """Gets test health information from the AST of a Java test.
-
-    The Java test health information includes the Java package, if applicable,
-    that the test belongs to and the number of test cases marked as disabled,
-    conditionally-disabled and flaky.
-
-    Args:
-        java_ast:
-            The abstract syntax tree (AST) of the Java test.
-
-    Returns:
-        Java test health information based on the test's AST.
-    """
-
-    annotation_counter = collections.Counter()
-
-    java_classes: list[ClassDeclaration] = java_ast.types
-    for java_class in java_classes:
-        # TODO(crbug.com/1254072): Count test cases in the class instead.
-        annotation_counter.update(_count_annotations(java_class.annotations))
-
-        java_methods: list[MethodDeclaration] = java_class.methods
-        for java_method in java_methods:
-            annotation_counter.update(
-                _count_annotations(java_method.annotations))
-
-    return JavaTestHealth(
-        java_package=get_java_package_name(java_ast),
-        disabled_tests_count=annotation_counter[_DISABLED_TEST_ANNOTATION],
-        disable_if_tests_count=annotation_counter[_DISABLE_IF_TEST_ANNOTATION],
-        flaky_tests_count=annotation_counter[_FLAKY_TEST_ANNOTATION])
-
-
 def _count_annotations(annotations: List[Annotation]) -> collections.Counter:
     counter = collections.Counter()
 
@@ -113,3 +141,29 @@
             counter[_FLAKY_TEST_ANNOTATION] += 1
 
     return counter
+
+
+class JavaSyntaxError(SyntaxError):
+    """A syntax error found when parsing Java source code."""
+
+    def __init__(self, error_message, *, line_num: int, column_num: int,
+                 file_path: pathlib.Path, java_src_code: str):
+        """Instantiates a JavaParseError.
+
+        Args:
+            msg:
+                A description of the Java syntax error.
+            line_num:
+                The line containing the start of the error.
+            column_num:
+                The starting column in the line containing the error.
+            filename:
+                The filename of the Java file containing the error.
+            java_src_code:
+                The source code of the Java file containing the error.
+        """
+        super().__init__(error_message)
+        self.lineno = line_num
+        self.offset = column_num
+        self.filename = str(file_path.relative_to(_CHROMIUM_SRC_PATH))
+        self.text = java_src_code.splitlines()[line_num - 1]
diff --git a/tools/android/test_health/java_test_utils_unittest.py b/tools/android/test_health/java_test_utils_unittest.py
index b30027e7..b755e8e 100755
--- a/tools/android/test_health/java_test_utils_unittest.py
+++ b/tools/android/test_health/java_test_utils_unittest.py
@@ -18,42 +18,22 @@
 
 _CHROMIUM_SRC_PATH = git_metadata_utils.get_chromium_src_path()
 
-_SIX_SRC_PATH = (_CHROMIUM_SRC_PATH / 'third_party' / 'six' /
-                 'src').resolve(strict=True)
-# six is a dependency of javalang
-sys.path.insert(0, str(_SIX_SRC_PATH))
-
-_JAVALANG_SRC_PATH = (_CHROMIUM_SRC_PATH / 'third_party' / 'javalang' /
-                      'src').resolve(strict=True)
-if str(_JAVALANG_SRC_PATH) not in sys.path:
-    sys.path.append(str(_JAVALANG_SRC_PATH))
-import javalang
-
 _TEST_FILES_PATH = (pathlib.Path(__file__).parents[0] / 'testdata' /
                     'javatests' / 'org' / 'chromium' / 'chrome' / 'browser' /
                     'test_health').resolve(strict=True)
 
-_HEALTHY_TEST_SRC = (_TEST_FILES_PATH / 'healthy_tests' /
-                     'SampleTest.java').resolve(strict=True).read_text()
-_HEALTHY_TEST_AST = javalang.parse.parse(_HEALTHY_TEST_SRC)
-_HEALTHY_NO_PKG_TEST_SRC = (_TEST_FILES_PATH / 'healthy_tests' /
-                            'SampleNoPackageTest.java').resolve(
-                                strict=True).read_text()
-_HEALTHY_NO_PKG_TEST_AST = javalang.parse.parse(_HEALTHY_NO_PKG_TEST_SRC)
-_UNHEALTHY_TEST_SRC = (_TEST_FILES_PATH / 'unhealthy_tests' /
-                       'SampleTest.java').resolve(strict=True).read_text()
-_UNHEALTHY_TEST_AST = javalang.parse.parse(_UNHEALTHY_TEST_SRC)
-_DISABLED_TEST_SRC = (_TEST_FILES_PATH / 'disabled_tests' /
-                      'SampleDisabledTest.java').resolve(
-                          strict=True).read_text()
-_DISABLED_TEST_AST = javalang.parse.parse(_DISABLED_TEST_SRC)
-_DISABLE_IF_TEST_SRC = (_TEST_FILES_PATH / 'disabled_tests' /
-                        'SampleDisableIfTest.java').resolve(
-                            strict=True).read_text()
-_DISABLE_IF_TEST_AST = javalang.parse.parse(_DISABLE_IF_TEST_SRC)
-_FLAKY_TEST_SRC = (_TEST_FILES_PATH / 'flaky_tests' /
-                   'SampleFlakyTest.java').resolve(strict=True).read_text()
-_FLAKY_TEST_AST = javalang.parse.parse(_FLAKY_TEST_SRC)
+_HEALTHY_TEST_PATH = _TEST_FILES_PATH / 'healthy_tests' / 'SampleTest.java'
+_HEALTHY_NO_PKG_TEST_PATH = (_TEST_FILES_PATH / 'healthy_tests' /
+                             'SampleNoPackageTest.java')
+_UNHEALTHY_TEST_PATH = (_TEST_FILES_PATH / 'unhealthy_tests' /
+                        'SampleTest.java')
+_INVALID_SYNTAX_TEST_PATH = (_TEST_FILES_PATH / 'unhealthy_tests' /
+                             'InvalidSyntaxTest.java')
+_DISABLED_TEST_PATH = (_TEST_FILES_PATH / 'disabled_tests' /
+                       'SampleDisabledTest.java')
+_DISABLE_IF_TEST_PATH = (_TEST_FILES_PATH / 'disabled_tests' /
+                         'SampleDisableIfTest.java')
+_FLAKY_TEST_PATH = _TEST_FILES_PATH / 'flaky_tests' / 'SampleFlakyTest.java'
 
 _BASE_JAVA_PACKAGE = 'org.chromium.chrome.browser.test_health'
 _JAVA_PACKAGE_HEALTHY_TESTS = _BASE_JAVA_PACKAGE + '.healthy_tests'
@@ -62,34 +42,29 @@
 _JAVA_PACKAGE_FLAKY_TESTS = _BASE_JAVA_PACKAGE + '.flaky_tests'
 
 
-class TestJavaPackageName(unittest.TestCase):
-    """Tests for the get_java_package_name function."""
-
-    def test_get_java_package_name(self):
-        java_package = java_test_utils.get_java_package_name(_HEALTHY_TEST_AST)
-
-        self.assertEqual(_JAVA_PACKAGE_HEALTHY_TESTS, java_package)
-
-    def test_get_java_package_name_no_package(self):
-        java_package = java_test_utils.get_java_package_name(
-            _HEALTHY_NO_PKG_TEST_AST)
-
-        self.assertIsNone(java_package)
-
-
 class TestJavaTestHealthStats(unittest.TestCase):
     """Tests for the get_java_test_health_stats function."""
 
     def test_get_java_test_health_stats_healthy_tests(self):
-        test_health = java_test_utils.get_java_test_health(_HEALTHY_TEST_AST)
+        test_health = java_test_utils.get_java_test_health(_HEALTHY_TEST_PATH)
 
         self.assertEqual(_JAVA_PACKAGE_HEALTHY_TESTS, test_health.java_package)
         self.assertEqual(0, test_health.disabled_tests_count)
         self.assertEqual(0, test_health.disable_if_tests_count)
         self.assertEqual(0, test_health.flaky_tests_count)
 
+    def test_get_java_test_health_stats_healthy_tests_no_java_package(self):
+        test_health = java_test_utils.get_java_test_health(
+            _HEALTHY_NO_PKG_TEST_PATH)
+
+        self.assertIsNone(test_health.java_package)
+        self.assertEqual(0, test_health.disabled_tests_count)
+        self.assertEqual(0, test_health.disable_if_tests_count)
+        self.assertEqual(0, test_health.flaky_tests_count)
+
     def test_get_java_test_health_stats_unhealthy_tests(self):
-        test_health = java_test_utils.get_java_test_health(_UNHEALTHY_TEST_AST)
+        test_health = java_test_utils.get_java_test_health(
+            _UNHEALTHY_TEST_PATH)
 
         self.assertEqual(_JAVA_PACKAGE_UNHEALTHY_TESTS,
                          test_health.java_package)
@@ -98,7 +73,7 @@
         self.assertEqual(1, test_health.flaky_tests_count)
 
     def test_get_java_test_health_stats_disabled_tests(self):
-        test_health = java_test_utils.get_java_test_health(_DISABLED_TEST_AST)
+        test_health = java_test_utils.get_java_test_health(_DISABLED_TEST_PATH)
 
         self.assertEqual(_JAVA_PACKAGE_DISABLED_TESTS,
                          test_health.java_package)
@@ -106,9 +81,9 @@
         self.assertEqual(0, test_health.disable_if_tests_count)
         self.assertEqual(0, test_health.flaky_tests_count)
 
-    def test_get_java_test_health_stats_disableif_tests(self):
+    def test_get_java_test_health_stats_disable_if_tests(self):
         test_health = java_test_utils.get_java_test_health(
-            _DISABLE_IF_TEST_AST)
+            _DISABLE_IF_TEST_PATH)
 
         self.assertEqual(_JAVA_PACKAGE_DISABLED_TESTS,
                          test_health.java_package)
@@ -117,13 +92,28 @@
         self.assertEqual(0, test_health.flaky_tests_count)
 
     def test_get_java_test_health_stats_flaky_tests(self):
-        test_health = java_test_utils.get_java_test_health(_FLAKY_TEST_AST)
+        test_health = java_test_utils.get_java_test_health(_FLAKY_TEST_PATH)
 
         self.assertEqual(_JAVA_PACKAGE_FLAKY_TESTS, test_health.java_package)
         self.assertEqual(0, test_health.disabled_tests_count)
         self.assertEqual(0, test_health.disable_if_tests_count)
         self.assertEqual(2, test_health.flaky_tests_count)
 
+    def test_get_java_test_health_invalid_test_syntax(self):
+        expected_filename = str(
+            _INVALID_SYNTAX_TEST_PATH.relative_to(_CHROMIUM_SRC_PATH))
+        expected_text = ('        values = Arrays.stream(STRING_ARRAY_2D)'
+                         '.map(String[] ::clone).toArray(String[][] ::new);')
+
+        with self.assertRaises(java_test_utils.JavaSyntaxError) as error_cm:
+            java_test_utils.get_java_test_health(_INVALID_SYNTAX_TEST_PATH)
+
+        self.assertEqual("Expected '.'", error_cm.exception.msg)
+        self.assertEqual(expected_filename, error_cm.exception.filename)
+        self.assertEqual(30, error_cm.exception.lineno)
+        self.assertEqual(64, error_cm.exception.offset)
+        self.assertEqual(expected_text, error_cm.exception.text)
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/tools/android/test_health/testdata/javatests/org/chromium/chrome/browser/test_health/unhealthy_tests/InvalidSyntaxTest.java b/tools/android/test_health/testdata/javatests/org/chromium/chrome/browser/test_health/unhealthy_tests/InvalidSyntaxTest.java
new file mode 100644
index 0000000..88739d4
--- /dev/null
+++ b/tools/android/test_health/testdata/javatests/org/chromium/chrome/browser/test_health/unhealthy_tests/InvalidSyntaxTest.java
@@ -0,0 +1,34 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.test_health.unhealthy_tests;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+
+import java.util.Arrays;
+
+/** A Java test with invalid syntax. */
+@SmallTest
+@RunWith(BaseJUnit4ClassRunner.class)
+public class InvalidSyntaxTest {
+    private static final String[][] STRING_ARRAY_2D =
+            new String[][] {new String[] {"hello", "world"}, new String[] {"foo", "bar"}};
+
+    @Test
+    public void testMethodReferenceFromArrayType() {
+        String[][] values;
+
+        // The javalang Python module doesn't support method references for array types:
+        // https://github.com/c2nes/javalang/blob/566963547575e93d305871d9cb26ce47ff1a036e/javalang/test/test_java_8_syntax.py#L198-L204
+        values = Arrays.stream(STRING_ARRAY_2D).map(String[] ::clone).toArray(String[][] ::new);
+
+        Assert.assertEquals(STRING_ARRAY_2D, values);
+    }
+}
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index b948f9b..69f10c5 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -18079,6 +18079,25 @@
   <int value="2" label="The system clock is known to be inaccurate"/>
 </enum>
 
+<enum name="CrosSwitchAccessError">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Preference type"/>
+  <int value="2" label="Untranslated string"/>
+  <int value="3" label="Invalid color"/>
+  <int value="4" label="Next undefined"/>
+  <int value="5" label="Previous undefined"/>
+  <int value="6" label="Null child"/>
+  <int value="7" label="No children"/>
+  <int value="8" label="Malformed desktop"/>
+  <int value="9" label="Missing location"/>
+  <int value="10" label="Missing keyboard"/>
+  <int value="11" label="Row too short"/>
+  <int value="12" label="Missing base node"/>
+  <int value="13" label="Next invalid"/>
+  <int value="14" label="Previous invalid"/>
+  <int value="15" label="Invalid selection bounds"/>
+</enum>
+
 <enum name="CrosSystemTrayFirstInteraction">
   <int value="0" label="Quick Settings"/>
   <int value="1" label="Message Center"/>
@@ -20980,6 +20999,8 @@
   <int value="58" label="QuirksModeIssue::QuirksMode"/>
   <int value="59" label="QuirksModeIssue::LimitedQuirksMode"/>
   <int value="60" label="DeprecationIssue"/>
+  <int value="61" label="ClientHintIssue::MetaTagAllowListInvalidOrigin"/>
+  <int value="62" label="ClientHintIssue::MetaTagModifiedHTML"/>
 </enum>
 
 <enum name="DevToolsIssuesPanelIssueExpanded">
@@ -27537,6 +27558,7 @@
   <int value="932" label="UserAgentReduction"/>
   <int value="933" label="OriginAgentClusterDefaultEnabled"/>
   <int value="934" label="DeviceLoginWebUILazyLoading"/>
+  <int value="935" label="ProjectorEnabled"/>
 </enum>
 
 <enum name="EnterprisePolicyDeviceIdValidity">
@@ -47122,6 +47144,266 @@
   <int value="6" label="Migration stopped"/>
 </enum>
 
+<enum name="KeyCode">
+  <int value="0" label="UNKNOWN"/>
+  <int value="1" label="KEYCODE_1"/>
+  <int value="2" label="KEYCODE_2"/>
+  <int value="3" label="KEYCODE_3"/>
+  <int value="4" label="KEYCODE_4"/>
+  <int value="5" label="KEYCODE_5"/>
+  <int value="6" label="KEYCODE_6"/>
+  <int value="7" label="KEYCODE_7"/>
+  <int value="8" label="BACKSPACE"/>
+  <int value="9" label="TAB"/>
+  <int value="10" label="KEYCODE_10"/>
+  <int value="11" label="KEYCODE_11"/>
+  <int value="12" label="CLEAR"/>
+  <int value="13" label="RETURN"/>
+  <int value="14" label="KEYCODE_14"/>
+  <int value="15" label="KEYCODE_15"/>
+  <int value="16" label="SHIFT"/>
+  <int value="17" label="CONTROL"/>
+  <int value="18" label="ALT"/>
+  <int value="19" label="PAUSE"/>
+  <int value="20" label="CAPITAL"/>
+  <int value="21" label="KANA"/>
+  <int value="22" label="KEYCODE_22"/>
+  <int value="23" label="JUNJA"/>
+  <int value="24" label="FINAL"/>
+  <int value="25" label="HANJA"/>
+  <int value="26" label="KEYCODE_26"/>
+  <int value="27" label="ESCAPE"/>
+  <int value="28" label="CONVERT"/>
+  <int value="29" label="NONCONVERT"/>
+  <int value="30" label="ACCEPT"/>
+  <int value="31" label="MODECHANGE"/>
+  <int value="32" label="SPACE"/>
+  <int value="33" label="PRIOR"/>
+  <int value="34" label="NEXT"/>
+  <int value="35" label="END"/>
+  <int value="36" label="HOME"/>
+  <int value="37" label="LEFT"/>
+  <int value="38" label="UP"/>
+  <int value="39" label="RIGHT"/>
+  <int value="40" label="DOWN"/>
+  <int value="41" label="SELECT"/>
+  <int value="42" label="PRINT"/>
+  <int value="43" label="EXECUTE"/>
+  <int value="44" label="SNAPSHOT"/>
+  <int value="45" label="INSERT"/>
+  <int value="46" label="KEY_DELETE"/>
+  <int value="47" label="HELP"/>
+  <int value="48" label="NUM_0"/>
+  <int value="49" label="NUM_1"/>
+  <int value="50" label="NUM_2"/>
+  <int value="51" label="NUM_3"/>
+  <int value="52" label="NUM_4"/>
+  <int value="53" label="NUM_5"/>
+  <int value="54" label="NUM_6"/>
+  <int value="55" label="NUM_7"/>
+  <int value="56" label="NUM_8"/>
+  <int value="57" label="NUM_9"/>
+  <int value="58" label="KEYCODE_58"/>
+  <int value="59" label="KEYCODE_59"/>
+  <int value="60" label="KEYCODE_60"/>
+  <int value="61" label="KEYCODE_61"/>
+  <int value="62" label="KEYCODE_62"/>
+  <int value="63" label="KEYCODE_63"/>
+  <int value="64" label="KEYCODE_64"/>
+  <int value="65" label="A"/>
+  <int value="66" label="B"/>
+  <int value="67" label="C"/>
+  <int value="68" label="D"/>
+  <int value="69" label="E"/>
+  <int value="70" label="F"/>
+  <int value="71" label="G"/>
+  <int value="72" label="H"/>
+  <int value="73" label="I"/>
+  <int value="74" label="J"/>
+  <int value="75" label="K"/>
+  <int value="76" label="L"/>
+  <int value="77" label="M"/>
+  <int value="78" label="N"/>
+  <int value="79" label="O"/>
+  <int value="80" label="P"/>
+  <int value="81" label="Q"/>
+  <int value="82" label="R"/>
+  <int value="83" label="S"/>
+  <int value="84" label="T"/>
+  <int value="85" label="U"/>
+  <int value="86" label="V"/>
+  <int value="87" label="W"/>
+  <int value="88" label="X"/>
+  <int value="89" label="Y"/>
+  <int value="90" label="Z"/>
+  <int value="91" label="LWIN"/>
+  <int value="92" label="RWIN"/>
+  <int value="93" label="APPS"/>
+  <int value="94" label="KEYCODE_94"/>
+  <int value="95" label="SLEEP"/>
+  <int value="96" label="NUMPAD0"/>
+  <int value="97" label="NUMPAD1"/>
+  <int value="98" label="NUMPAD2"/>
+  <int value="99" label="NUMPAD3"/>
+  <int value="100" label="NUMPAD4"/>
+  <int value="101" label="NUMPAD5"/>
+  <int value="102" label="NUMPAD6"/>
+  <int value="103" label="NUMPAD7"/>
+  <int value="104" label="NUMPAD8"/>
+  <int value="105" label="NUMPAD9"/>
+  <int value="106" label="MULTIPLY"/>
+  <int value="107" label="ADD"/>
+  <int value="108" label="SEPARATOR"/>
+  <int value="109" label="SUBTRACT"/>
+  <int value="110" label="DECIMAL"/>
+  <int value="111" label="DIVIDE"/>
+  <int value="112" label="F1"/>
+  <int value="113" label="F2"/>
+  <int value="114" label="F3"/>
+  <int value="115" label="F4"/>
+  <int value="116" label="F5"/>
+  <int value="117" label="F6"/>
+  <int value="118" label="F7"/>
+  <int value="119" label="F8"/>
+  <int value="120" label="F9"/>
+  <int value="121" label="F10"/>
+  <int value="122" label="F11"/>
+  <int value="123" label="F12"/>
+  <int value="124" label="F13"/>
+  <int value="125" label="F14"/>
+  <int value="126" label="F15"/>
+  <int value="127" label="F16"/>
+  <int value="128" label="F17"/>
+  <int value="129" label="F18"/>
+  <int value="130" label="F19"/>
+  <int value="131" label="F20"/>
+  <int value="132" label="F21"/>
+  <int value="133" label="F22"/>
+  <int value="134" label="F23"/>
+  <int value="135" label="F24"/>
+  <int value="136" label="KEYCODE_136"/>
+  <int value="137" label="KEYCODE_137"/>
+  <int value="138" label="KEYCODE_138"/>
+  <int value="139" label="KEYCODE_139"/>
+  <int value="140" label="KEYCODE_140"/>
+  <int value="141" label="KEYCODE_141"/>
+  <int value="142" label="KEYCODE_142"/>
+  <int value="143" label="KEYCODE_143"/>
+  <int value="144" label="NUMLOCK"/>
+  <int value="145" label="SCROLL"/>
+  <int value="146" label="KEYCODE_146"/>
+  <int value="147" label="KEYCODE_147"/>
+  <int value="148" label="KEYCODE_148"/>
+  <int value="149" label="KEYCODE_149"/>
+  <int value="150" label="KEYCODE_150"/>
+  <int value="151" label="WLAN"/>
+  <int value="152" label="POWER"/>
+  <int value="153" label="ASSISTANT"/>
+  <int value="154" label="KEYCODE_154"/>
+  <int value="155" label="KEYCODE_155"/>
+  <int value="156" label="KEYCODE_156"/>
+  <int value="157" label="KEYCODE_157"/>
+  <int value="158" label="KEYCODE_158"/>
+  <int value="159" label="KEYCODE_159"/>
+  <int value="160" label="LSHIFT"/>
+  <int value="161" label="RSHIFT"/>
+  <int value="162" label="LCONTROL"/>
+  <int value="163" label="RCONTROL"/>
+  <int value="164" label="LMENU"/>
+  <int value="165" label="RMENU"/>
+  <int value="166" label="BROWSER_BACK"/>
+  <int value="167" label="BROWSER_FORWARD"/>
+  <int value="168" label="BROWSER_REFRESH"/>
+  <int value="169" label="BROWSER_STOP"/>
+  <int value="170" label="BROWSER_SEARCH"/>
+  <int value="171" label="BROWSER_FAVORITES"/>
+  <int value="172" label="BROWSER_HOME"/>
+  <int value="173" label="VOLUME_MUTE"/>
+  <int value="174" label="VOLUME_DOWN"/>
+  <int value="175" label="VOLUME_UP"/>
+  <int value="176" label="MEDIA_NEXT_TRACK"/>
+  <int value="177" label="MEDIA_PREV_TRACK"/>
+  <int value="178" label="MEDIA_STOP"/>
+  <int value="179" label="MEDIA_PLAY_PAUSE"/>
+  <int value="180" label="MEDIA_LAUNCH_MAIL"/>
+  <int value="181" label="MEDIA_LAUNCH_MEDIA_SELECT"/>
+  <int value="182" label="MEDIA_LAUNCH_APP1"/>
+  <int value="183" label="MEDIA_LAUNCH_APP2"/>
+  <int value="184" label="KEYCODE_184"/>
+  <int value="185" label="KEYCODE_185"/>
+  <int value="186" label="OEM_1"/>
+  <int value="187" label="OEM_PLUS"/>
+  <int value="188" label="OEM_COMMA"/>
+  <int value="189" label="OEM_MINUS"/>
+  <int value="190" label="OEM_PERIOD"/>
+  <int value="191" label="OEM_2"/>
+  <int value="192" label="OEM_3"/>
+  <int value="193" label="KEYCODE_193"/>
+  <int value="194" label="KEYCODE_194"/>
+  <int value="195" label="KEYCODE_195"/>
+  <int value="196" label="KEYCODE_196"/>
+  <int value="197" label="KEYCODE_197"/>
+  <int value="198" label="KEYCODE_198"/>
+  <int value="199" label="KEYCODE_199"/>
+  <int value="200" label="KEYCODE_200"/>
+  <int value="201" label="KEYCODE_201"/>
+  <int value="202" label="KEYCODE_202"/>
+  <int value="203" label="KEYCODE_203"/>
+  <int value="204" label="KEYCODE_204"/>
+  <int value="205" label="KEYCODE_205"/>
+  <int value="206" label="KEYCODE_206"/>
+  <int value="207" label="KEYCODE_207"/>
+  <int value="208" label="KEYCODE_208"/>
+  <int value="209" label="KEYCODE_209"/>
+  <int value="210" label="KEYCODE_210"/>
+  <int value="211" label="KEYCODE_211"/>
+  <int value="212" label="KEYCODE_212"/>
+  <int value="213" label="KEYCODE_213"/>
+  <int value="214" label="KEYCODE_214"/>
+  <int value="215" label="KEYCODE_215"/>
+  <int value="216" label="BRIGHTNESS_DOWN"/>
+  <int value="217" label="BRIGHTNESS_UP"/>
+  <int value="218" label="KBD_BRIGHTNESS_DOWN"/>
+  <int value="219" label="OEM_4"/>
+  <int value="220" label="OEM_5"/>
+  <int value="221" label="OEM_6"/>
+  <int value="222" label="OEM_7"/>
+  <int value="223" label="OEM_8"/>
+  <int value="224" label="KEYCODE_224"/>
+  <int value="225" label="ALTGR"/>
+  <int value="226" label="OEM_102"/>
+  <int value="227" label="KEYCODE_227"/>
+  <int value="228" label="KEYCODE_228"/>
+  <int value="229" label="PROCESSKEY"/>
+  <int value="230" label="COMPOSE"/>
+  <int value="231" label="PACKET"/>
+  <int value="232" label="KBD_BRIGHTNESS_UP"/>
+  <int value="233" label="KEYCODE_233"/>
+  <int value="234" label="KEYCODE_234"/>
+  <int value="235" label="KEYCODE_235"/>
+  <int value="236" label="KEYCODE_236"/>
+  <int value="237" label="KEYCODE_237"/>
+  <int value="238" label="KEYCODE_238"/>
+  <int value="239" label="KEYCODE_239"/>
+  <int value="240" label="KEYCODE_240"/>
+  <int value="241" label="KEYCODE_241"/>
+  <int value="242" label="KEYCODE_242"/>
+  <int value="243" label="DBE_SBCSCHAR"/>
+  <int value="244" label="DBE_DBCSCHAR"/>
+  <int value="245" label="KEYCODE_245"/>
+  <int value="246" label="ATTN"/>
+  <int value="247" label="CRSEL"/>
+  <int value="248" label="EXSEL"/>
+  <int value="249" label="EREOF"/>
+  <int value="250" label="PLAY"/>
+  <int value="251" label="ZOOM"/>
+  <int value="252" label="NONAME"/>
+  <int value="253" label="PA1"/>
+  <int value="254" label="OEM_CLEAR"/>
+  <int value="255" label="KEYCODE_255"/>
+  <int value="256" label="NONE"/>
+</enum>
+
 <enum name="KeyPermissionsManagerArcUsageUpdateStatus">
   <int value="0" label="kStarted"/>
   <int value="1" label="kSucceeded"/>
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml
index d5d91f6..f6d0bb3 100644
--- a/tools/metrics/histograms/metadata/accessibility/histograms.xml
+++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -28,6 +28,117 @@
                uniques"/>
 </variants>
 
+<variants name="SwitchAccessMenuAction">
+  <variant name="ActionRecorder"
+      summary="Switch Access Menu Action: Action Recorder"/>
+  <variant name="Copy" summary="Switch Access Menu Action: Copy"/>
+  <variant name="Cut" summary="Switch Access Menu Action: Cut"/>
+  <variant name="Decrement" summary="Switch Access Menu Action: Decrement"/>
+  <variant name="Dictation" summary="Switch Access Menu Action: Dictation"/>
+  <variant name="DisplayBrightnessDown"
+      summary="Switch Access Menu Action: Display Brightness Down"/>
+  <variant name="DisplayBrightnessUp"
+      summary="Switch Access Menu Action: Display Brightness Up"/>
+  <variant name="DisplayMenu"
+      summary="Switch Access Menu Action: Display Menu"/>
+  <variant name="DisplayMirror"
+      summary="Switch Access Menu Action: Display Mirror"/>
+  <variant name="DisplayRotate"
+      summary="Switch Access Menu Action: Display Rotate"/>
+  <variant name="DisplayZoomIn"
+      summary="Switch Access Menu Action: Display Zoom In"/>
+  <variant name="DisplayZoomOut"
+      summary="Switch Access Menu Action: Display Zoom Out"/>
+  <variant name="EndTextSelection"
+      summary="Switch Access Menu Action: End Text Selection"/>
+  <variant name="ExecuteMacro"
+      summary="Switch Access Menu Action: Execute Macro"/>
+  <variant name="Increment" summary="Switch Access Menu Action: Increment"/>
+  <variant name="ItemScan" summary="Switch Access Menu Action: Item Scan"/>
+  <variant name="JumpToBeginningOfText"
+      summary="Switch Access Menu Action: Jump To Beginning Of Text"/>
+  <variant name="JumpToEndOfText"
+      summary="Switch Access Menu Action: Jump To End Of Text"/>
+  <variant name="Keyboard" summary="Switch Access Menu Action: Keyboard"/>
+  <variant name="LeaveGroup" summary="Switch Access Menu Action: Leave Group"/>
+  <variant name="LeftClick" summary="Switch Access Menu Action: Left Click"/>
+  <variant name="MediaFastforward"
+      summary="Switch Access Menu Action: Media Fastforward"/>
+  <variant name="MediaMenu" summary="Switch Access Menu Action: Media Menu"/>
+  <variant name="MediaMute" summary="Switch Access Menu Action: Media Mute"/>
+  <variant name="MediaPlayPause"
+      summary="Switch Access Menu Action: Media Play Pause"/>
+  <variant name="MediaRewind"
+      summary="Switch Access Menu Action: Media Rewind"/>
+  <variant name="MediaVolumeDown"
+      summary="Switch Access Menu Action: Media Volume Down"/>
+  <variant name="MediaVolumeUp"
+      summary="Switch Access Menu Action: Media Volume Up"/>
+  <variant name="MoveBackwardOneCharOfText"
+      summary="Switch Access Menu Action: Move Backward One Char Of Text"/>
+  <variant name="MoveBackwardOneWordOfText"
+      summary="Switch Access Menu Action: Move Backward One Word Of Text"/>
+  <variant name="MoveCursor" summary="Switch Access Menu Action: Move Cursor"/>
+  <variant name="MoveDownOneLineOfText"
+      summary="Switch Access Menu Action: Move Down One Line Of Text"/>
+  <variant name="MoveForwardOneCharOfText"
+      summary="Switch Access Menu Action: Move Forward One Char Of Text"/>
+  <variant name="MoveForwardOneWordOfText"
+      summary="Switch Access Menu Action: Move Forward One Word Of Text"/>
+  <variant name="MoveUpOneLineOfText"
+      summary="Switch Access Menu Action: Move Up One Line Of Text"/>
+  <variant name="Paste" summary="Switch Access Menu Action: Paste"/>
+  <variant name="PointScan" summary="Switch Access Menu Action: Point Scan"/>
+  <variant name="RightClick" summary="Switch Access Menu Action: Right Click"/>
+  <variant name="ScrollDown" summary="Switch Access Menu Action: Scroll Down"/>
+  <variant name="ScrollLeft" summary="Switch Access Menu Action: Scroll Left"/>
+  <variant name="ScrollRight"
+      summary="Switch Access Menu Action: Scroll Right"/>
+  <variant name="ScrollUp" summary="Switch Access Menu Action: Scroll Up"/>
+  <variant name="Select" summary="Switch Access Menu Action: Select"/>
+  <variant name="Settings" summary="Switch Access Menu Action: Settings"/>
+  <variant name="Shortcuts" summary="Switch Access Menu Action: Shortcuts"/>
+  <variant name="StartRecording"
+      summary="Switch Access Menu Action: Start Recording"/>
+  <variant name="StartTextSelection"
+      summary="Switch Access Menu Action: Start Text Selection"/>
+  <variant name="StopRecording"
+      summary="Switch Access Menu Action: Stop Recording"/>
+  <variant name="SystemDiagnostics"
+      summary="Switch Access Menu Action: System Diagnostics"/>
+  <variant name="SystemHelp" summary="Switch Access Menu Action: System Help"/>
+  <variant name="SystemLauncher"
+      summary="Switch Access Menu Action: System Launcher"/>
+  <variant name="SystemMenu" summary="Switch Access Menu Action: System Menu"/>
+  <variant name="SystemScreenshot"
+      summary="Switch Access Menu Action: System Screenshot"/>
+  <variant name="SystemStatusBar"
+      summary="Switch Access Menu Action: System Status Bar"/>
+  <variant name="SystemTaskManager"
+      summary="Switch Access Menu Action: System Task Manager"/>
+  <variant name="UserLock" summary="Switch Access Menu Action: User Lock"/>
+  <variant name="UserMenu" summary="Switch Access Menu Action: User Menu"/>
+  <variant name="UserNextUser"
+      summary="Switch Access Menu Action: User Next User"/>
+  <variant name="UserPreviousUser"
+      summary="Switch Access Menu Action: User Previous User"/>
+  <variant name="UserSignOut"
+      summary="Switch Access Menu Action: User Sign Out"/>
+  <variant name="WebBookmark"
+      summary="Switch Access Menu Action: Web Bookmark"/>
+  <variant name="WebBottomOfPage"
+      summary="Switch Access Menu Action: Web Bottom Of Page"/>
+  <variant name="WebClearHistory"
+      summary="Switch Access Menu Action: Web Clear History"/>
+  <variant name="WebDownloads"
+      summary="Switch Access Menu Action: Web Downloads"/>
+  <variant name="WebFindInPage"
+      summary="Switch Access Menu Action: Web Find In Page"/>
+  <variant name="WebMenu" summary="Switch Access Menu Action: Web Menu"/>
+  <variant name="WebTopOfPage"
+      summary="Switch Access Menu Action: Web Top Of Page"/>
+</variants>
+
 <histogram name="Accessibility.ActiveTime" units="ms"
     expires_after="2022-04-17">
   <owner>aleventhal@chromium.org</owner>
@@ -630,6 +741,99 @@
   </summary>
 </histogram>
 
+<histogram name="Accessibility.CrosSwitchAccess.AutoScan" enum="BooleanEnabled"
+    expires_after="2022-04-03">
+  <owner>dtseng@chromium.org</owner>
+  <owner>anastasi@google.com</owner>
+  <owner>chrome-a11y-core@google.com</owner>
+  <summary>
+    The value of the Switch Access &quot;Auto Scan&quot; setting, logged
+    immediately after toggling. This will show us how often users are turning
+    the feature on, and how often they are turning it back off again.
+  </summary>
+</histogram>
+
+<histogram name="Accessibility.CrosSwitchAccess.AutoScan.KeyboardSpeedMs"
+    units="ms" expires_after="2022-04-03">
+  <owner>dtseng@chromium.org</owner>
+  <owner>anastasi@google.com</owner>
+  <owner>chrome-a11y-core@google.com</owner>
+  <summary>
+    Measures the user-set speed for scanning between keyboard keys in Switch
+    Access, logged immediately after changing the setting. Values range from 1ms
+    to 10000ms, in 100ms buckets.
+  </summary>
+</histogram>
+
+<histogram name="Accessibility.CrosSwitchAccess.AutoScan.SpeedMs" units="ms"
+    expires_after="2022-04-03">
+  <owner>dtseng@chromium.org</owner>
+  <owner>anastasi@google.com</owner>
+  <owner>chrome-a11y-core@google.com</owner>
+  <summary>
+    Measures the user-set speed for auto scanning between items in Switch
+    Access, logged immediately after changing the setting. Values range from 1ms
+    to 10000ms, in 100ms buckets.
+  </summary>
+</histogram>
+
+<histogram name="Accessibility.CrosSwitchAccess.Error"
+    enum="CrosSwitchAccessError" expires_after="2022-04-17">
+  <owner>dtseng@chromium.org</owner>
+  <owner>anastasi@google.com</owner>
+  <owner>chrome-a11y-core@google.com</owner>
+  <summary>
+    Switch Access error occurred. See CrosSwitchAccessError enum for error
+    types.
+  </summary>
+</histogram>
+
+<histogram
+    name="Accessibility.CrosSwitchAccess.MenuAction.{SwitchAccessMenuAction}"
+    enum="BooleanUsage" expires_after="2022-04-17">
+  <owner>dtseng@chromium.org</owner>
+  <owner>anastasi@google.com</owner>
+  <owner>chrome-a11y-core@google.com</owner>
+  <summary>
+    Switch Access menu action chosen by user, logged immediately upon selection.
+    See SwitchAccessMenuAction variants for details.
+  </summary>
+  <token key="SwitchAccessMenuAction" variants="SwitchAccessMenuAction"/>
+</histogram>
+
+<histogram name="Accessibility.CrosSwitchAccess.NextKeyCode" enum="KeyCode"
+    expires_after="2022-04-17">
+  <owner>dtseng@chromium.org</owner>
+  <owner>anastasi@google.com</owner>
+  <owner>chrome-a11y-core@google.com</owner>
+  <summary>
+    Which key code user has assigned to the Next Action (e.g. 32 for the Space
+    key)
+  </summary>
+</histogram>
+
+<histogram name="Accessibility.CrosSwitchAccess.PreviousKeyCode" enum="KeyCode"
+    expires_after="2022-04-17">
+  <owner>dtseng@chromium.org</owner>
+  <owner>anastasi@google.com</owner>
+  <owner>chrome-a11y-core@google.com</owner>
+  <summary>
+    Which key code user has assigned to the Previous Action (e.g. 32 for the
+    Space key)
+  </summary>
+</histogram>
+
+<histogram name="Accessibility.CrosSwitchAccess.SelectKeyCode" enum="KeyCode"
+    expires_after="2022-04-17">
+  <owner>dtseng@chromium.org</owner>
+  <owner>anastasi@google.com</owner>
+  <owner>chrome-a11y-core@google.com</owner>
+  <summary>
+    Which key code user has assigned to the Select Action (e.g. 32 for the Space
+    key)
+  </summary>
+</histogram>
+
 <histogram name="Accessibility.CrosVirtualKeyboard" enum="BooleanEnabled"
     expires_after="2022-04-17">
   <owner>dtseng@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/bluetooth/histograms.xml b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
index e41acf4..869661c7 100644
--- a/tools/metrics/histograms/metadata/bluetooth/histograms.xml
+++ b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
@@ -248,6 +248,20 @@
   </summary>
 </histogram>
 
+<histogram name="Bluetooth.ChromeOS.FastPair.TotalGattConnectionTime"
+    units="ms" expires_after="2022-09-20">
+  <owner>shanefitz@google.com</owner>
+  <owner>julietlevesque@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the total time it takes to create a successful GATT connection to
+    the device. Time is calculated as the difference between when the GATT
+    connection is beginning to connect, and when it reaches the connection
+    state. If the connection attempt fails or takes longer than 5 seconds, no
+    value is logged to this metric,
+  </summary>
+</histogram>
+
 <histogram
     name="Bluetooth.ChromeOS.FastPair.TotalUxPairTime.{FastPairPairingProtocol}"
     units="ms" expires_after="2022-09-20">
diff --git a/tools/metrics/histograms/metadata/compositing/histograms.xml b/tools/metrics/histograms/metadata/compositing/histograms.xml
index e547cf43..a333246 100644
--- a/tools/metrics/histograms/metadata/compositing/histograms.xml
+++ b/tools/metrics/histograms/metadata/compositing/histograms.xml
@@ -777,6 +777,356 @@
   </summary>
 </histogram>
 
+<histogram name="Graphics.PresentationTimestamp.InvalidBeforeSwap" units="ms"
+    expires_after="2022-05-01">
+  <owner>sadrul@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Presentation timestamp comes from the driver when showing a display frame on
+    screen. These timestamps could be invalid, and be earlier than the swap
+    time. This metric records how much time before the swap-time such invalid
+    timestamps are. If the presentation timestamp is after the swap-time, then
+    this metric is not recorded.
+  </summary>
+</histogram>
+
+<histogram name="Graphics.PresentationTimestamp.InvalidFromFuture" units="ms"
+    expires_after="2022-01-02">
+  <owner>sadrul@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Presentation timestamp comes from the driver when showing a display frame on
+    screen. These timestamps can sometimes be in the future. This metric records
+    how far in the future these timestamps can be. If the timestamp is not in
+    the future, then this metric is not recorded.
+  </summary>
+</histogram>
+
+<histogram name="Graphics.PresentationTimestamp.LargePresentationDelta"
+    units="ms" expires_after="2022-01-02">
+  <owner>sadrul@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Measures very large (more than 3 minutes) delay in presenting display
+    frames. Presentation timestamp comes from the driver when showing a display
+    frame on screen. There are times when the presentation can be delayed a long
+    time. This metric reports how often this can happen. This is reported only
+    if the presentation takes 3 minutes or more.
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.95pctPercentDroppedFrames_1sWindow"
+    units="%" expires_after="2022-04-17">
+  <owner>behdadb@chromium.org</owner>
+  <owner>sadrul@chromium.org</owner>
+  <summary>
+    The percent dropped frame is calculated for each sliding window of 1 second
+    length and added to a histogram, and then the 95 percentile of that
+    histogram is reported as 95pctPercentDroppedFrames_1sWindow.
+
+    The sliding windows cover the duration of page-load (From start of page-load
+    until user navigates away, or closes the tab/chrome, or when app goes to
+    background). Note that this means that this metric will bias toward tabs
+    that are closed more quickly, and might underreport tabs that remain open
+    for a long duration.
+
+    PercentDroppedFrames is measured by tracking the number of frames which were
+    not displayed on screen out of the total number of frames expected to be
+    produced and displayed. In other words, the lower this number is, the
+    smoother experience.
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.Checkerboarding" units="%"
+    expires_after="2022-05-22">
+  <owner>sadrul@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Tracks the percent of frames that were shown with checkerboards during a
+    particular sequence of frames (e.g. during scroll, animation, etc.). Note
+    that this is only recorded for sequences of length &gt; 4.
+
+    Checkerboarding is measured by tracking the number of times checkerboard
+    frames are displayed (as opposed to the number of checkerboard frames
+    produced), since the same checkerboard frame can be displayed more than
+    once.
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.Diagnostic.DiscardedDependentCount"
+    units="dependent reporters" expires_after="2022-04-17">
+  <owner>sadrul@chromium.org</owner>
+  <owner>behdadb@chromium.org</owner>
+  <summary>
+    Diagnostic metric to measure how many dependent reporters have been
+    discarded.
+
+    The reporters might be dependant on another reporter (when having partial
+    updates) but if the number of dependents go over a limit we would discard
+    them earlier. This metric count how many of such reporters been discarded
+    earlier than expected as a result of outstanding number of dependent
+    reporters. The metric will be reported at the end of each frame if there has
+    been any discarded dependent reporters.
+  </summary>
+</histogram>
+
+<histogram
+    name="Graphics.Smoothness.Diagnostic.DroppedFrameAfterScrollStart.Frames"
+    units="vsyncs" expires_after="2021-09-01">
+  <obsolete>
+    Removed 2021-11-29.
+  </obsolete>
+  <owner>sadrul@chromium.org</owner>
+  <owner>behdadb@chromium.org</owner>
+  <summary>Metric is replaced by DroppedFrameAfterScrollStart2.Time.</summary>
+</histogram>
+
+<histogram
+    name="Graphics.Smoothness.Diagnostic.DroppedFrameAfterScrollStart.Time"
+    units="ms" expires_after="2021-09-01">
+  <obsolete>
+    Removed 2021-11-29.
+  </obsolete>
+  <owner>sadrul@chromium.org</owner>
+  <owner>behdadb@chromium.org</owner>
+  <summary>Metric is replaced by DroppedFrameAfterScrollStart2.Time.</summary>
+</histogram>
+
+<histogram
+    name="Graphics.Smoothness.Diagnostic.DroppedFrameAfterScrollStart2.Frames"
+    units="vsyncs" expires_after="2022-11-29">
+  <owner>sadrul@chromium.org</owner>
+  <owner>behdadb@chromium.org</owner>
+  <summary>
+    Diagnostic metric to measure how long after scroll-start a frame is dropped.
+
+    For every frame dropped during a scroll, this metric reports how long after
+    scroll started the frame dropped (in number of vsyncs).
+  </summary>
+</histogram>
+
+<histogram
+    name="Graphics.Smoothness.Diagnostic.DroppedFrameAfterScrollStart2.Time"
+    units="ms" expires_after="2022-11-29">
+  <owner>sadrul@chromium.org</owner>
+  <owner>behdadb@chromium.org</owner>
+  <summary>
+    Diagnostic metric to measure how long after scroll-start a frame is dropped.
+
+    For every frame dropped during a scroll, this metric reports how long after
+    scroll started the frame dropped (in milliseconds).
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.Diagnostic.ReadSharedMemoryDuration"
+    units="microseconds" expires_after="2021-09-01">
+  <owner>sadrul@chromium.org</owner>
+  <owner>behdadb@chromium.org</owner>
+  <summary>
+    Diagnostic metric to track how long it takes to read the smoothness data
+    from shared memory.
+
+    Warning: This metric may include reports from clients with low-resolution
+    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
+    will cause this metric to have an abnormal distribution.
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.Diagnostic.ReadSharedMemoryUKMSuccess"
+    units="boolean" expires_after="2021-09-01">
+  <owner>sadrul@chromium.org</owner>
+  <owner>fangzhoug@chromium.org</owner>
+  <summary>
+    Diagnostic metric to track how often reading the smoothness data for UKM
+    from the shared memory (using atomic memcpy) fails (or succeeds).
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.FrameSequenceLength" units="count"
+    expires_after="2021-08-15">
+  <obsolete>
+    Removed 2021-07-09.
+  </obsolete>
+  <owner>sadrul@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Counts the number of frames expected during a particular sequence of frames
+    (e.g. during scroll, animation, etc.).
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.Jank" units="%" expires_after="2022-06-01">
+  <owner>sadrul@chromium.org</owner>
+  <owner>mjzhang@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Tracks the percent of janks for a particular sequence of frames (e.g. during
+    scroll, animation, etc.). This is reported in various sub-metrics with
+    suffixes describing the type of the sequence (e.g. TouchScroll etc.).
+
+    Jank is measured by tracking the number of abrupt increases in frame
+    presentation interval, divided by the total number of frames expected to be
+    produced and displayed. The lower this number is, the less the smoothness
+    varies over time.
+
+    Note that this metric is reported only when there are sufficient number of
+    frames (&gt;= 100). If there are sequences with fewer frames, then these are
+    aggregated until there are enough frames to produce the metric.
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.Jank.All{Type}" units="%"
+    expires_after="2022-06-01">
+  <owner>sadrul@chromium.org</owner>
+  <owner>mjzhang@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Tracks the percent of janks for a particular frame sequence. {Type}
+
+    Jank is measured by tracking the number of abrupt increases in frame
+    presentation interval, divided by the total number of frames expected to be
+    produced and displayed. The lower this number is, the less the smoothness
+    varies over time.
+
+    Note that this metric is reported only when there are sufficient number of
+    frames (&gt;= 100). If there are sequences with fewer frames, then these are
+    aggregated until there are enough frames to produce the metric.
+  </summary>
+  <token key="Type">
+    <variant name="Animations"
+        summary="This metric aggregates data reported from all types of
+                 animations (e.g. comositor-driven animations, main-thread
+                 driven animations, and raf-driven animations, etc.)."/>
+    <variant name="Interactions"
+        summary="This metric aggregates data reported for all supported
+                 combinations of interaction types (e.g. scrolling, pinching,
+                 etc.) and input device types (e.g. touchscreen, touchpad,
+                 mousewheel, etc.)."/>
+    <variant name="Sequences"
+        summary="This metric aggregates data from all animations and all
+                 interactions."/>
+  </token>
+</histogram>
+
+<histogram name="Graphics.Smoothness.MaxPercentDroppedFrames_1sWindow"
+    units="%" expires_after="2022-06-12">
+  <owner>behdadb@chromium.org</owner>
+  <owner>sadrul@chromium.org</owner>
+  <summary>
+    Tracks the percent of dropped frames for in a 1 second sliding window.
+
+    PercentDroppedFrames is measured by tracking the number of frames which were
+    not displayed on screen out of the total number of frames expected to be
+    produced and displayed. In other words, the lower this number is, the
+    smoother experience.
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.MaxStale" units="ms"
+    expires_after="2022-06-12">
+  <owner>sadrul@chromium.org</owner>
+  <owner>mjzhang@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Tracks the maximum staleness value for all presentations for a particular
+    sequence of frames (e.g. during scroll, animation, etc.). This is reported
+    in various sub-metrics with suffixes describing the type of the sequence
+    (e.g. TouchScroll etc.).
+
+    Typically, a frame presentation is expected to last at least one vsync
+    cycle, plus any number of additional vsync cycles if no updates are expected
+    duration that time. If the presentation interval is prolonged due to reasons
+    other than listed above, then that prolonged portion will be considered
+    staleness for that frame.
+
+    Note that this metric is reported only when there are sufficient number of
+    frames (&gt;= 100). If there are sequences with fewer frames, then these are
+    aggregated until there are enough frames to produce the metric.
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.PercentDroppedFrames" units="%"
+    expires_after="2022-09-30">
+  <owner>sadrul@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Tracks the percent of dropped frames for a particular sequence of frames
+    (e.g. during scroll, animation, etc.). This is reported in various
+    sub-metrics with suffixes describing the type of the sequence (e.g.
+    TouchScroll etc.).
+
+    PercentDroppedFrames is measured by tracking the number of frames which were
+    not displayed on screen out of the total number of frames expected to be
+    produced and displayed. In other words, the lower this number is, the
+    smoother experience.
+
+    Note that this metric is reported only when there are sufficient number of
+    frames (&gt;= 100). If there are sequences with fewer frames, then these are
+    aggregated until there are enough frames to produce the metric.
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.PercentDroppedFrames.AllAnimations"
+    units="%" expires_after="2022-06-12">
+  <owner>sadrul@chromium.org</owner>
+  <owner>ericrk@chromium.org</owner>
+  <summary>
+    Tracks the percent of dropped frames for a particular sequence of frames for
+    all animations. This metric is reported for all animations (e.g.
+    comositor-driven animations, main-thread driven animations, and raf-driven
+    animations).
+
+    PercentDroppedFrames is measured by tracking the number of frames which were
+    not displayed on screen out of the total number of frames expected to be
+    produced and displayed. In other words, the lower this number is, the
+    smoother experience.
+
+    Note that this metric is reported only when there are sufficient number of
+    frames (&gt;= 100). If there are sequences with fewer frames, then these are
+    aggregated until there are enough frames to produce the metric.
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.PercentDroppedFrames.AllInteractions"
+    units="%" expires_after="2022-06-12">
+  <owner>sadrul@chromium.org</owner>
+  <owner>ericrk@chromium.org</owner>
+  <summary>
+    Tracks the percent of dropped frames for a particular sequence of frames
+    where a user-input (e.g. scroll, pinch) is active. This metric is reported
+    for all sources of user-input (i.e. both touchscreen and
+    touchpad/mouse-wheel).
+
+    PercentDroppedFrames is measured by tracking the number of frames which were
+    not displayed on screen out of the total number of frames expected to be
+    produced and displayed. In other words, the lower this number is, the
+    smoother experience.
+
+    Note that this metric is reported only when there are sufficient number of
+    frames (&gt;= 100). If there are sequences with fewer frames, then these are
+    aggregated until there are enough frames to produce the metric.
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.PercentDroppedFrames.AllSequences"
+    units="%" expires_after="2022-06-12">
+  <owner>sadrul@chromium.org</owner>
+  <owner>ericrk@chromium.org</owner>
+  <summary>
+    Tracks the percent of dropped frames for a particular sequence of frames.
+    This metric is reported for all animations and all interactions.
+
+    PercentDroppedFrames is measured by tracking the number of frames which were
+    not displayed on screen out of the total number of frames expected to be
+    produced and displayed. In other words, the lower this number is, the
+    smoother experience.
+
+    Note that this metric is reported only when there are sufficient number of
+    frames (&gt;= 100). If there are sequences with fewer frames, then these are
+    aggregated until there are enough frames to produce the metric.
+  </summary>
+</histogram>
+
 <histogram name="Graphics.Smoothness.PercentDroppedFrames2.AllAnimations"
     units="%" expires_after="2022-09-10">
   <owner>sadrul@chromium.org</owner>
@@ -847,6 +1197,202 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="Graphics.Smoothness.PercentMissedDeadlineFrames"
+    units="%" expires_after="2022-09-30">
+  <owner>sadrul@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Tracks the percent of frames that missed the deadline for a particular
+    sequence of frames (e.g. during scroll, animation, etc.). This is reported
+    in various sub-metrics with suffixes describing the type of the sequence
+    (e.g. TouchScroll etc.).
+
+    PercentMissedDeadlineFrames is measured by tracking the number of frames
+    which were displayed on screen but missed the vsync interval.
+
+    Note that this metric is reported only when there are sufficient number of
+    frames (&gt;= 100). If there are sequences with fewer frames, then these are
+    aggregated until there are enough frames to produce the metric.
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.PercentMissedDeadlineFrames.AllAnimations"
+    units="%" expires_after="2022-09-30">
+  <owner>sadrul@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Tracks the percent of frames that missed the deadline for a particular
+    sequence of frames for all animations. This metric is reported for all
+    animations (e.g. comositor-driven animations, main-thread driven animations,
+    and raf-driven animations).
+
+    PercentMissedDeadlineFrames is measured by tracking the number of frames
+    which were displayed on screen but missed the vsync interval.
+
+    Note that this metric is reported only when there are sufficient number of
+    frames (&gt;= 100). If there are sequences with fewer frames, then these are
+    aggregated until there are enough frames to produce the metric.
+  </summary>
+</histogram>
+
+<histogram
+    name="Graphics.Smoothness.PercentMissedDeadlineFrames.AllInteractions"
+    units="%" expires_after="2022-09-30">
+  <owner>sadrul@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Tracks the percent of frames that missed the deadline for a particular
+    sequence of frames where a user-input (e.g. scroll, pinch) is active. This
+    metric is reported for all sources of user-input (i.e. both touchscreen and
+    touchpad/mouse-wheel).
+
+    PercentMissedDeadlineFrames is measured by tracking the number of frames
+    which were displayed on screen but missed the vsync interval.
+
+    Note that this metric is reported only when there are sufficient number of
+    frames (&gt;= 100). If there are sequences with fewer frames, then these are
+    aggregated until there are enough frames to produce the metric.
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.PercentMissedDeadlineFrames.AllSequences"
+    units="%" expires_after="2022-09-30">
+  <owner>sadrul@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Tracks the percent of frames that missed the deadline for a particular
+    sequence of frames. This metric is reported for all animations and all
+    interactions.
+
+    PercentMissedDeadlineFrames is measured by tracking the number of frames
+    which were displayed on screen but missed the vsync interval.
+
+    Note that this metric is reported only when there are sufficient number of
+    frames (&gt;= 100). If there are sequences with fewer frames, then these are
+    aggregated until there are enough frames to produce the metric.
+  </summary>
+</histogram>
+
+<histogram
+    name="Graphics.Smoothness.PerSession.95pctPercentDroppedFrames_1sWindow"
+    units="%" expires_after="2022-04-17">
+  <owner>sadrul@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Tracks the 95th percentile of dropped frames percent of a sliding window of
+    1 second. The metric is reported once per page-load when the page closes. If
+    there are fewer than 20 sliding windows for calculating 95th percentile, the
+    max value will be used instead of 95th percentile.
+
+    PercentDroppedFrames is measured by tracking the number of frames which were
+    not displayed on screen out of the total number of frames expected to be
+    produced and displayed. In other words, the lower this number is, the
+    smoother experience.
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.PerSession.AveragePercentDroppedFrames"
+    units="%" expires_after="2022-06-05">
+  <owner>sadrul@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Tracks the percent of dropped frames. The metric is reported once per
+    page-load when the page closes.
+
+    AveragePercentDroppedFrames is measured by tracking the number of frames
+    which were not displayed on screen out of the total number of frames
+    expected to be produced and displayed. In other words, the lower this number
+    is, the smoother experience.
+  </summary>
+</histogram>
+
+<histogram
+    name="Graphics.Smoothness.PerSession.MaxPercentDroppedFrames_1sWindow"
+    units="%" expires_after="2022-05-29">
+  <owner>sadrul@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Tracks the Max of dropped frames percent of a sliding window of 1 second.
+    The metric is reported once per page-load when the page closes.
+
+    PercentDroppedFrames is measured by tracking the number of frames which were
+    not displayed on screen out of the total number of frames expected to be
+    produced and displayed. In other words, the lower this number is, the
+    smoother experience.
+  </summary>
+</histogram>
+
+<histogram
+    name="Graphics.Smoothness.PerSession.TimeMaxPercentDroppedFrames_1sWindow"
+    units="ms" expires_after="2021-11-18">
+  <owner>sadrul@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Tracks the time since First Contentful Paint when the sliding window with
+    the max percent of dropped frames occurs, up to 25 seconds. The metric is
+    reported once per page-load when the page closes.
+
+    PercentDroppedFrames is measured by tracking the number of frames which were
+    not displayed on screen out of the total number of frames expected to be
+    produced and displayed. In other words, the lower this number is, the
+    smoother experience.
+  </summary>
+</histogram>
+
+<histogram name="Graphics.Smoothness.Stale" units="ms"
+    expires_after="2022-06-12">
+  <owner>sadrul@chromium.org</owner>
+  <owner>mjzhang@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Tracks the staleness value for each frame presentation for a particular
+    sequence of frames (e.g. during scroll, animation, etc.). This is reported
+    in various sub-metrics with suffixes describing the type of the sequence
+    (e.g. TouchScroll etc.).
+
+    Typically, a frame presentation is expected to last at least one vsync
+    cycle, plus any number of additional vsync cycles if no updates are expected
+    duration that time. If the presentation interval is prolonged due to reasons
+    other than listed above, then that prolonged portion will be considered
+    staleness.
+
+    Note that the reporting of this metric occurs as soon as a frame is
+    presented, and is not affected by the length of the frame sequence.
+  </summary>
+</histogram>
+
+<histogram name="GraphicsPipeline.ReceivedBeginFrame" units="microseconds"
+    expires_after="M85">
+  <obsolete>
+    Expired in M85.
+  </obsolete>
+  <owner>yiyix@chromium.org</owner>
+  <owner>chrome-gpu-metrics@google.com</owner>
+  <summary>
+    The amount of time it takes for the BeginFrame to travel to the Client from
+    the DisplayCompositor.
+
+    Note that this metrics is only recorded on clients on which a
+    high-resolution clock is available
+  </summary>
+</histogram>
+
+<histogram name="GraphicsPipeline.SubmitCompositorFrameAfterBeginFrame"
+    units="microseconds" expires_after="M85">
+  <obsolete>
+    Expired in M85.
+  </obsolete>
+  <owner>yiyix@chromium.org</owner>
+  <owner>chrome-gpu-metrics@google.com</owner>
+  <summary>
+    How long the client takes to prepare a compositor frame after receiving a
+    BeginFrameArgs.
+
+    Note that this is only recorded on clients on which a high-resolution clock
+    is available.
+  </summary>
+</histogram>
+
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/extensions/histograms.xml b/tools/metrics/histograms/metadata/extensions/histograms.xml
index e04a76e..d6854b1 100644
--- a/tools/metrics/histograms/metadata/extensions/histograms.xml
+++ b/tools/metrics/histograms/metadata/extensions/histograms.xml
@@ -950,6 +950,17 @@
   </summary>
 </histogram>
 
+<histogram name="Extensions.DevTools.UserIsInDeveloperMode"
+    enum="InDeveloperMode" expires_after="2022-03-04">
+  <owner>ghazale@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
+  <summary>
+    Reports whether DevTools extensions are being loaded while the user has
+    extensions developer mode enabled. This excludes extensions installed by the
+    enterprise policy and component extensions.
+  </summary>
+</histogram>
+
 <histogram name="Extensions.DidCreateScriptContext_Blessed" units="ms"
     expires_after="2022-11-18">
   <owner>rdevlin.cronin@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/gpu/histograms.xml b/tools/metrics/histograms/metadata/gpu/histograms.xml
index 1a6643fc..0b57d41 100644
--- a/tools/metrics/histograms/metadata/gpu/histograms.xml
+++ b/tools/metrics/histograms/metadata/gpu/histograms.xml
@@ -2094,6 +2094,150 @@
   </summary>
 </histogram>
 
+<histogram name="Viz.DelegatedCompositing.Status" enum="DelegatedStatus"
+    expires_after="2022-07-15">
+  <owner>petermcneeley@chromium.org</owner>
+  <owner>rjkroege@chromium.org</owner>
+  <summary>
+    An enum status result for attempted delegated compositing (success or
+    failure reason) recorded every drawn frame. Currently only recorded for
+    LaCros delegated compositing.
+  </summary>
+</histogram>
+
+<histogram name="Viz.DisplayCompositor.OverlayNumProposedCandidates"
+    units="units" expires_after="2022-04-17">
+  <owner>petermcneeley@chromium.org</owner>
+  <owner>dcastagna@chromium.org</owner>
+  <summary>
+    A count of the number of proposed overlay candidates available for overlay
+    selection. Recorded every time a frame is rendered by the display
+    compositor.
+  </summary>
+</histogram>
+
+<histogram name="Viz.DisplayCompositor.OverlayQuadMaterial"
+    enum="OverlayQuadMaterial" expires_after="2022-04-17">
+  <owner>petermcneeley@chromium.org</owner>
+  <owner>dcastagna@chromium.org</owner>
+  <summary>
+    Quad material for current promoted overlay, per frame. Recorded every time a
+    frame is rendered by the display compositor.
+  </summary>
+</histogram>
+
+<histogram name="Viz.DisplayCompositor.OverlayStrategy"
+    enum="OverlayStrategies" expires_after="2022-05-15">
+  <owner>dcastagna@chromium.org</owner>
+  <owner>hoegsberg@chromium.org</owner>
+  <summary>
+    Overlay strategies used to promote Hardware Overlays, per frame. Recorded
+    every time a frame is rendered by the display compositor.
+  </summary>
+</histogram>
+
+<histogram name="Viz.DisplayCompositor.OverlaySwitchInterval" units="ms"
+    expires_after="2022-05-01">
+  <owner>petermcneeley@chromium.org</owner>
+  <owner>dcastagna@chromium.org</owner>
+  <summary>
+    The time, in milliseconds, since the change in overlay selection. Recorded
+    every time a frame is rendered by the display compositor.
+  </summary>
+</histogram>
+
+<histogram name="Viz.DisplayCompositor.RootDamageRect.Overlay"
+    enum="BooleanOverlayDamageRect" expires_after="2022-04-17">
+  <owner>magchen@chromium.org</owner>
+  <owner>zmo@chromium.org</owner>
+  <summary>
+    Any root damage excluding overlay damage in the current frame?
+  </summary>
+</histogram>
+
+<histogram name="Viz.DisplayCompositor.RootDamageRect.Underlay"
+    enum="UnderlayDamageRect" expires_after="2022-04-17">
+  <owner>magchen@chromium.org</owner>
+  <owner>zmo@chromium.org</owner>
+  <summary>
+    The root damage type excluding underlay damage in the current frame.
+  </summary>
+</histogram>
+
+<histogram name="Viz.FileDescriptorTracking.TimeToCompute" units="microseconds"
+    expires_after="2022-07-09">
+  <owner>petermcneeley@chromium.org</owner>
+  <owner>rjkroege@chromium.org</owner>
+  <summary>
+    Time spent computing the number of active File Descriptors. This is logged
+    once every 5 minutes as the cost of this computation is estimated to be at
+    least 1ms. Currently only recorded for LaCros delegated compositing.
+
+    Warning: This metric does not include reports from clients with
+    low-resolution clocks.
+  </summary>
+</histogram>
+
+<histogram name="Viz.FileDescriptorTracking.{FdStat}" units="units"
+    expires_after="2022-07-09">
+  <owner>petermcneeley@chromium.org</owner>
+  <owner>rjkroege@chromium.org</owner>
+  <summary>
+    {FdStat} File Descriptors for the GPU process. This is logged once every 5
+    minutes as the cost of this computation is estimated to be at least 1ms.
+    Currently only recorded for LaCros delegated compositing.
+  </summary>
+  <token key="FdStat">
+    <variant name="NumActive" summary="Current number of active"/>
+    <variant name="NumSoftMax" summary="Maximum number of"/>
+    <variant name="PercentageUsed" summary="Percentage of in use"/>
+  </token>
+</histogram>
+
+<histogram name="Viz.FrameSinkVideoCapturer.I420.CaptureDuration" units="ms"
+    expires_after="2021-03-07">
+  <owner>samans@chromium.org</owner>
+  <owner>sadrul@chromium.org</owner>
+  <owner>viz-team-wat@google.com</owner>
+  <summary>
+    The time it took from when FrameSinkVideoCapturerImpl sent a request for an
+    I420 readback until the result comes back and ReadI420Planes successfully
+    finishes.
+  </summary>
+</histogram>
+
+<histogram name="Viz.FrameSinkVideoCapturer.I420.CaptureSucceeded"
+    enum="BooleanSuccess" expires_after="2020-12-31">
+  <owner>samans@chromium.org</owner>
+  <owner>sadrul@chromium.org</owner>
+  <owner>viz-team-wat@google.com</owner>
+  <summary>
+    Whether an I420 readback initiated by FrameSinkVideoCapturerImpl succeeded.
+  </summary>
+</histogram>
+
+<histogram name="Viz.FrameSinkVideoCapturer.RGBA.CaptureDuration" units="ms"
+    expires_after="2022-05-01">
+  <owner>samans@chromium.org</owner>
+  <owner>sadrul@chromium.org</owner>
+  <owner>viz-team-wat@google.com</owner>
+  <summary>
+    The time it took from when FrameSinkVideoCapturerImpl sent a request for an
+    RGBA readback until the result comes back and ReadRGBAPlane successfully
+    finishes.
+  </summary>
+</histogram>
+
+<histogram name="Viz.FrameSinkVideoCapturer.RGBA.CaptureSucceeded"
+    enum="BooleanSuccess" expires_after="2020-12-31">
+  <owner>samans@chromium.org</owner>
+  <owner>sadrul@chromium.org</owner>
+  <owner>viz-team-wat@google.com</owner>
+  <summary>
+    Whether an RGBA readback initiated by FrameSinkVideoCapturerImpl succeeded.
+  </summary>
+</histogram>
+
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/navigation/histograms.xml b/tools/metrics/histograms/metadata/navigation/histograms.xml
index 12965af6..eeafeee 100644
--- a/tools/metrics/histograms/metadata/navigation/histograms.xml
+++ b/tools/metrics/histograms/metadata/navigation/histograms.xml
@@ -961,7 +961,7 @@
 </histogram>
 
 <histogram name="Navigation.MainFrame.ThirdPartyCookieBlockingEnabled"
-    enum="ThirdPartyCookieBlockState" expires_after="2022-01-23">
+    enum="ThirdPartyCookieBlockState" expires_after="2022-07-23">
   <owner>dullweber@chromium.org</owner>
   <owner>feuunk@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index e059b05..6a6b588 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -7091,552 +7091,6 @@
   </summary>
 </histogram>
 
-<histogram name="Graphics.PresentationTimestamp.InvalidBeforeSwap" units="ms"
-    expires_after="2022-05-01">
-  <owner>sadrul@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Presentation timestamp comes from the driver when showing a display frame on
-    screen. These timestamps could be invalid, and be earlier than the swap
-    time. This metric records how much time before the swap-time such invalid
-    timestamps are. If the presentation timestamp is after the swap-time, then
-    this metric is not recorded.
-  </summary>
-</histogram>
-
-<histogram name="Graphics.PresentationTimestamp.InvalidFromFuture" units="ms"
-    expires_after="2022-01-02">
-  <owner>sadrul@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Presentation timestamp comes from the driver when showing a display frame on
-    screen. These timestamps can sometimes be in the future. This metric records
-    how far in the future these timestamps can be. If the timestamp is not in
-    the future, then this metric is not recorded.
-  </summary>
-</histogram>
-
-<histogram name="Graphics.PresentationTimestamp.LargePresentationDelta"
-    units="ms" expires_after="2022-01-02">
-  <owner>sadrul@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Measures very large (more than 3 minutes) delay in presenting display
-    frames. Presentation timestamp comes from the driver when showing a display
-    frame on screen. There are times when the presentation can be delayed a long
-    time. This metric reports how often this can happen. This is reported only
-    if the presentation takes 3 minutes or more.
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.95pctPercentDroppedFrames_1sWindow"
-    units="%" expires_after="2022-04-17">
-  <owner>behdadb@chromium.org</owner>
-  <owner>sadrul@chromium.org</owner>
-  <summary>
-    The percent dropped frame is calculated for each sliding window of 1 second
-    length and added to a histogram, and then the 95 percentile of that
-    histogram is reported as 95pctPercentDroppedFrames_1sWindow.
-
-    The sliding windows cover the duration of page-load (From start of page-load
-    until user navigates away, or closes the tab/chrome, or when app goes to
-    background). Note that this means that this metric will bias toward tabs
-    that are closed more quickly, and might underreport tabs that remain open
-    for a long duration.
-
-    PercentDroppedFrames is measured by tracking the number of frames which were
-    not displayed on screen out of the total number of frames expected to be
-    produced and displayed. In other words, the lower this number is, the
-    smoother experience.
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.Checkerboarding" units="%"
-    expires_after="2022-05-22">
-  <owner>sadrul@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Tracks the percent of frames that were shown with checkerboards during a
-    particular sequence of frames (e.g. during scroll, animation, etc.). Note
-    that this is only recorded for sequences of length &gt; 4.
-
-    Checkerboarding is measured by tracking the number of times checkerboard
-    frames are displayed (as opposed to the number of checkerboard frames
-    produced), since the same checkerboard frame can be displayed more than
-    once.
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.Diagnostic.DiscardedDependentCount"
-    units="dependent reporters" expires_after="2022-04-17">
-  <owner>sadrul@chromium.org</owner>
-  <owner>behdadb@chromium.org</owner>
-  <summary>
-    Diagnostic metric to measure how many dependent reporters have been
-    discarded.
-
-    The reporters might be dependant on another reporter (when having partial
-    updates) but if the number of dependents go over a limit we would discard
-    them earlier. This metric count how many of such reporters been discarded
-    earlier than expected as a result of outstanding number of dependent
-    reporters. The metric will be reported at the end of each frame if there has
-    been any discarded dependent reporters.
-  </summary>
-</histogram>
-
-<histogram
-    name="Graphics.Smoothness.Diagnostic.DroppedFrameAfterScrollStart.Frames"
-    units="vsyncs" expires_after="2021-09-01">
-  <obsolete>
-    Removed 2021-11-29.
-  </obsolete>
-  <owner>sadrul@chromium.org</owner>
-  <owner>behdadb@chromium.org</owner>
-  <summary>Metric is replaced by DroppedFrameAfterScrollStart2.Time.</summary>
-</histogram>
-
-<histogram
-    name="Graphics.Smoothness.Diagnostic.DroppedFrameAfterScrollStart.Time"
-    units="ms" expires_after="2021-09-01">
-  <obsolete>
-    Removed 2021-11-29.
-  </obsolete>
-  <owner>sadrul@chromium.org</owner>
-  <owner>behdadb@chromium.org</owner>
-  <summary>Metric is replaced by DroppedFrameAfterScrollStart2.Time.</summary>
-</histogram>
-
-<histogram
-    name="Graphics.Smoothness.Diagnostic.DroppedFrameAfterScrollStart2.Frames"
-    units="vsyncs" expires_after="2022-11-29">
-  <owner>sadrul@chromium.org</owner>
-  <owner>behdadb@chromium.org</owner>
-  <summary>
-    Diagnostic metric to measure how long after scroll-start a frame is dropped.
-
-    For every frame dropped during a scroll, this metric reports how long after
-    scroll started the frame dropped (in number of vsyncs).
-  </summary>
-</histogram>
-
-<histogram
-    name="Graphics.Smoothness.Diagnostic.DroppedFrameAfterScrollStart2.Time"
-    units="ms" expires_after="2022-11-29">
-  <owner>sadrul@chromium.org</owner>
-  <owner>behdadb@chromium.org</owner>
-  <summary>
-    Diagnostic metric to measure how long after scroll-start a frame is dropped.
-
-    For every frame dropped during a scroll, this metric reports how long after
-    scroll started the frame dropped (in milliseconds).
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.Diagnostic.ReadSharedMemoryDuration"
-    units="microseconds" expires_after="2021-09-01">
-  <owner>sadrul@chromium.org</owner>
-  <owner>behdadb@chromium.org</owner>
-  <summary>
-    Diagnostic metric to track how long it takes to read the smoothness data
-    from shared memory.
-
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution.
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.Diagnostic.ReadSharedMemoryUKMSuccess"
-    units="boolean" expires_after="2021-09-01">
-  <owner>sadrul@chromium.org</owner>
-  <owner>fangzhoug@chromium.org</owner>
-  <summary>
-    Diagnostic metric to track how often reading the smoothness data for UKM
-    from the shared memory (using atomic memcpy) fails (or succeeds).
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.FrameSequenceLength" units="count"
-    expires_after="2021-08-15">
-  <obsolete>
-    Removed 2021-07-09.
-  </obsolete>
-  <owner>sadrul@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Counts the number of frames expected during a particular sequence of frames
-    (e.g. during scroll, animation, etc.).
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.Jank" units="%" expires_after="2022-06-01">
-  <owner>sadrul@chromium.org</owner>
-  <owner>mjzhang@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Tracks the percent of janks for a particular sequence of frames (e.g. during
-    scroll, animation, etc.). This is reported in various sub-metrics with
-    suffixes describing the type of the sequence (e.g. TouchScroll etc.).
-
-    Jank is measured by tracking the number of abrupt increases in frame
-    presentation interval, divided by the total number of frames expected to be
-    produced and displayed. The lower this number is, the less the smoothness
-    varies over time.
-
-    Note that this metric is reported only when there are sufficient number of
-    frames (&gt;= 100). If there are sequences with fewer frames, then these are
-    aggregated until there are enough frames to produce the metric.
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.Jank.All{Type}" units="%"
-    expires_after="2022-06-01">
-  <owner>sadrul@chromium.org</owner>
-  <owner>mjzhang@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Tracks the percent of janks for a particular frame sequence. {Type}
-
-    Jank is measured by tracking the number of abrupt increases in frame
-    presentation interval, divided by the total number of frames expected to be
-    produced and displayed. The lower this number is, the less the smoothness
-    varies over time.
-
-    Note that this metric is reported only when there are sufficient number of
-    frames (&gt;= 100). If there are sequences with fewer frames, then these are
-    aggregated until there are enough frames to produce the metric.
-  </summary>
-  <token key="Type">
-    <variant name="Animations"
-        summary="This metric aggregates data reported from all types of
-                 animations (e.g. comositor-driven animations, main-thread
-                 driven animations, and raf-driven animations, etc.)."/>
-    <variant name="Interactions"
-        summary="This metric aggregates data reported for all supported
-                 combinations of interaction types (e.g. scrolling, pinching,
-                 etc.) and input device types (e.g. touchscreen, touchpad,
-                 mousewheel, etc.)."/>
-    <variant name="Sequences"
-        summary="This metric aggregates data from all animations and all
-                 interactions."/>
-  </token>
-</histogram>
-
-<histogram name="Graphics.Smoothness.MaxPercentDroppedFrames_1sWindow"
-    units="%" expires_after="2022-06-12">
-  <owner>behdadb@chromium.org</owner>
-  <owner>sadrul@chromium.org</owner>
-  <summary>
-    Tracks the percent of dropped frames for in a 1 second sliding window.
-
-    PercentDroppedFrames is measured by tracking the number of frames which were
-    not displayed on screen out of the total number of frames expected to be
-    produced and displayed. In other words, the lower this number is, the
-    smoother experience.
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.MaxStale" units="ms"
-    expires_after="2022-06-12">
-  <owner>sadrul@chromium.org</owner>
-  <owner>mjzhang@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Tracks the maximum staleness value for all presentations for a particular
-    sequence of frames (e.g. during scroll, animation, etc.). This is reported
-    in various sub-metrics with suffixes describing the type of the sequence
-    (e.g. TouchScroll etc.).
-
-    Typically, a frame presentation is expected to last at least one vsync
-    cycle, plus any number of additional vsync cycles if no updates are expected
-    duration that time. If the presentation interval is prolonged due to reasons
-    other than listed above, then that prolonged portion will be considered
-    staleness for that frame.
-
-    Note that this metric is reported only when there are sufficient number of
-    frames (&gt;= 100). If there are sequences with fewer frames, then these are
-    aggregated until there are enough frames to produce the metric.
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.PercentDroppedFrames" units="%"
-    expires_after="2022-09-30">
-  <owner>sadrul@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Tracks the percent of dropped frames for a particular sequence of frames
-    (e.g. during scroll, animation, etc.). This is reported in various
-    sub-metrics with suffixes describing the type of the sequence (e.g.
-    TouchScroll etc.).
-
-    PercentDroppedFrames is measured by tracking the number of frames which were
-    not displayed on screen out of the total number of frames expected to be
-    produced and displayed. In other words, the lower this number is, the
-    smoother experience.
-
-    Note that this metric is reported only when there are sufficient number of
-    frames (&gt;= 100). If there are sequences with fewer frames, then these are
-    aggregated until there are enough frames to produce the metric.
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.PercentDroppedFrames.AllAnimations"
-    units="%" expires_after="2022-06-12">
-  <owner>sadrul@chromium.org</owner>
-  <owner>ericrk@chromium.org</owner>
-  <summary>
-    Tracks the percent of dropped frames for a particular sequence of frames for
-    all animations. This metric is reported for all animations (e.g.
-    comositor-driven animations, main-thread driven animations, and raf-driven
-    animations).
-
-    PercentDroppedFrames is measured by tracking the number of frames which were
-    not displayed on screen out of the total number of frames expected to be
-    produced and displayed. In other words, the lower this number is, the
-    smoother experience.
-
-    Note that this metric is reported only when there are sufficient number of
-    frames (&gt;= 100). If there are sequences with fewer frames, then these are
-    aggregated until there are enough frames to produce the metric.
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.PercentDroppedFrames.AllInteractions"
-    units="%" expires_after="2022-06-12">
-  <owner>sadrul@chromium.org</owner>
-  <owner>ericrk@chromium.org</owner>
-  <summary>
-    Tracks the percent of dropped frames for a particular sequence of frames
-    where a user-input (e.g. scroll, pinch) is active. This metric is reported
-    for all sources of user-input (i.e. both touchscreen and
-    touchpad/mouse-wheel).
-
-    PercentDroppedFrames is measured by tracking the number of frames which were
-    not displayed on screen out of the total number of frames expected to be
-    produced and displayed. In other words, the lower this number is, the
-    smoother experience.
-
-    Note that this metric is reported only when there are sufficient number of
-    frames (&gt;= 100). If there are sequences with fewer frames, then these are
-    aggregated until there are enough frames to produce the metric.
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.PercentDroppedFrames.AllSequences"
-    units="%" expires_after="2022-06-12">
-  <owner>sadrul@chromium.org</owner>
-  <owner>ericrk@chromium.org</owner>
-  <summary>
-    Tracks the percent of dropped frames for a particular sequence of frames.
-    This metric is reported for all animations and all interactions.
-
-    PercentDroppedFrames is measured by tracking the number of frames which were
-    not displayed on screen out of the total number of frames expected to be
-    produced and displayed. In other words, the lower this number is, the
-    smoother experience.
-
-    Note that this metric is reported only when there are sufficient number of
-    frames (&gt;= 100). If there are sequences with fewer frames, then these are
-    aggregated until there are enough frames to produce the metric.
-  </summary>
-</histogram>
-
-<histogram base="true" name="Graphics.Smoothness.PercentMissedDeadlineFrames"
-    units="%" expires_after="2022-09-30">
-  <owner>sadrul@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Tracks the percent of frames that missed the deadline for a particular
-    sequence of frames (e.g. during scroll, animation, etc.). This is reported
-    in various sub-metrics with suffixes describing the type of the sequence
-    (e.g. TouchScroll etc.).
-
-    PercentMissedDeadlineFrames is measured by tracking the number of frames
-    which were displayed on screen but missed the vsync interval.
-
-    Note that this metric is reported only when there are sufficient number of
-    frames (&gt;= 100). If there are sequences with fewer frames, then these are
-    aggregated until there are enough frames to produce the metric.
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.PercentMissedDeadlineFrames.AllAnimations"
-    units="%" expires_after="2022-09-30">
-  <owner>sadrul@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Tracks the percent of frames that missed the deadline for a particular
-    sequence of frames for all animations. This metric is reported for all
-    animations (e.g. comositor-driven animations, main-thread driven animations,
-    and raf-driven animations).
-
-    PercentMissedDeadlineFrames is measured by tracking the number of frames
-    which were displayed on screen but missed the vsync interval.
-
-    Note that this metric is reported only when there are sufficient number of
-    frames (&gt;= 100). If there are sequences with fewer frames, then these are
-    aggregated until there are enough frames to produce the metric.
-  </summary>
-</histogram>
-
-<histogram
-    name="Graphics.Smoothness.PercentMissedDeadlineFrames.AllInteractions"
-    units="%" expires_after="2022-09-30">
-  <owner>sadrul@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Tracks the percent of frames that missed the deadline for a particular
-    sequence of frames where a user-input (e.g. scroll, pinch) is active. This
-    metric is reported for all sources of user-input (i.e. both touchscreen and
-    touchpad/mouse-wheel).
-
-    PercentMissedDeadlineFrames is measured by tracking the number of frames
-    which were displayed on screen but missed the vsync interval.
-
-    Note that this metric is reported only when there are sufficient number of
-    frames (&gt;= 100). If there are sequences with fewer frames, then these are
-    aggregated until there are enough frames to produce the metric.
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.PercentMissedDeadlineFrames.AllSequences"
-    units="%" expires_after="2022-09-30">
-  <owner>sadrul@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Tracks the percent of frames that missed the deadline for a particular
-    sequence of frames. This metric is reported for all animations and all
-    interactions.
-
-    PercentMissedDeadlineFrames is measured by tracking the number of frames
-    which were displayed on screen but missed the vsync interval.
-
-    Note that this metric is reported only when there are sufficient number of
-    frames (&gt;= 100). If there are sequences with fewer frames, then these are
-    aggregated until there are enough frames to produce the metric.
-  </summary>
-</histogram>
-
-<histogram
-    name="Graphics.Smoothness.PerSession.95pctPercentDroppedFrames_1sWindow"
-    units="%" expires_after="2022-04-17">
-  <owner>sadrul@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Tracks the 95th percentile of dropped frames percent of a sliding window of
-    1 second. The metric is reported once per page-load when the page closes. If
-    there are fewer than 20 sliding windows for calculating 95th percentile, the
-    max value will be used instead of 95th percentile.
-
-    PercentDroppedFrames is measured by tracking the number of frames which were
-    not displayed on screen out of the total number of frames expected to be
-    produced and displayed. In other words, the lower this number is, the
-    smoother experience.
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.PerSession.AveragePercentDroppedFrames"
-    units="%" expires_after="2022-06-05">
-  <owner>sadrul@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Tracks the percent of dropped frames. The metric is reported once per
-    page-load when the page closes.
-
-    AveragePercentDroppedFrames is measured by tracking the number of frames
-    which were not displayed on screen out of the total number of frames
-    expected to be produced and displayed. In other words, the lower this number
-    is, the smoother experience.
-  </summary>
-</histogram>
-
-<histogram
-    name="Graphics.Smoothness.PerSession.MaxPercentDroppedFrames_1sWindow"
-    units="%" expires_after="2022-05-29">
-  <owner>sadrul@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Tracks the Max of dropped frames percent of a sliding window of 1 second.
-    The metric is reported once per page-load when the page closes.
-
-    PercentDroppedFrames is measured by tracking the number of frames which were
-    not displayed on screen out of the total number of frames expected to be
-    produced and displayed. In other words, the lower this number is, the
-    smoother experience.
-  </summary>
-</histogram>
-
-<histogram
-    name="Graphics.Smoothness.PerSession.TimeMaxPercentDroppedFrames_1sWindow"
-    units="ms" expires_after="2021-11-18">
-  <owner>sadrul@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Tracks the time since First Contentful Paint when the sliding window with
-    the max percent of dropped frames occurs, up to 25 seconds. The metric is
-    reported once per page-load when the page closes.
-
-    PercentDroppedFrames is measured by tracking the number of frames which were
-    not displayed on screen out of the total number of frames expected to be
-    produced and displayed. In other words, the lower this number is, the
-    smoother experience.
-  </summary>
-</histogram>
-
-<histogram name="Graphics.Smoothness.Stale" units="ms"
-    expires_after="2022-06-12">
-  <owner>sadrul@chromium.org</owner>
-  <owner>mjzhang@chromium.org</owner>
-  <owner>graphics-dev@chromium.org</owner>
-  <summary>
-    Tracks the staleness value for each frame presentation for a particular
-    sequence of frames (e.g. during scroll, animation, etc.). This is reported
-    in various sub-metrics with suffixes describing the type of the sequence
-    (e.g. TouchScroll etc.).
-
-    Typically, a frame presentation is expected to last at least one vsync
-    cycle, plus any number of additional vsync cycles if no updates are expected
-    duration that time. If the presentation interval is prolonged due to reasons
-    other than listed above, then that prolonged portion will be considered
-    staleness.
-
-    Note that the reporting of this metric occurs as soon as a frame is
-    presented, and is not affected by the length of the frame sequence.
-  </summary>
-</histogram>
-
-<histogram name="GraphicsPipeline.ReceivedBeginFrame" units="microseconds"
-    expires_after="M85">
-  <obsolete>
-    Expired in M85.
-  </obsolete>
-  <owner>yiyix@chromium.org</owner>
-  <owner>chrome-gpu-metrics@google.com</owner>
-  <summary>
-    The amount of time it takes for the BeginFrame to travel to the Client from
-    the DisplayCompositor.
-
-    Note that this metrics is only recorded on clients on which a
-    high-resolution clock is available
-  </summary>
-</histogram>
-
-<histogram name="GraphicsPipeline.SubmitCompositorFrameAfterBeginFrame"
-    units="microseconds" expires_after="M85">
-  <obsolete>
-    Expired in M85.
-  </obsolete>
-  <owner>yiyix@chromium.org</owner>
-  <owner>chrome-gpu-metrics@google.com</owner>
-  <summary>
-    How long the client takes to prepare a compositor frame after receiving a
-    BeginFrameArgs.
-
-    Note that this is only recorded on clients on which a high-resolution clock
-    is available.
-  </summary>
-</histogram>
-
 <histogram base="true" name="GridTabSwitcher.DirtySpan" units="ms"
     expires_after="2020-11-15">
   <owner>yusufo@chromium.org</owner>
@@ -16958,150 +16412,6 @@
   </summary>
 </histogram>
 
-<histogram name="Viz.DelegatedCompositing.Status" enum="DelegatedStatus"
-    expires_after="2022-07-15">
-  <owner>petermcneeley@chromium.org</owner>
-  <owner>rjkroege@chromium.org</owner>
-  <summary>
-    An enum status result for attempted delegated compositing (success or
-    failure reason) recorded every drawn frame. Currently only recorded for
-    LaCros delegated compositing.
-  </summary>
-</histogram>
-
-<histogram name="Viz.DisplayCompositor.OverlayNumProposedCandidates"
-    units="units" expires_after="2022-04-17">
-  <owner>petermcneeley@chromium.org</owner>
-  <owner>dcastagna@chromium.org</owner>
-  <summary>
-    A count of the number of proposed overlay candidates available for overlay
-    selection. Recorded every time a frame is rendered by the display
-    compositor.
-  </summary>
-</histogram>
-
-<histogram name="Viz.DisplayCompositor.OverlayQuadMaterial"
-    enum="OverlayQuadMaterial" expires_after="2022-04-17">
-  <owner>petermcneeley@chromium.org</owner>
-  <owner>dcastagna@chromium.org</owner>
-  <summary>
-    Quad material for current promoted overlay, per frame. Recorded every time a
-    frame is rendered by the display compositor.
-  </summary>
-</histogram>
-
-<histogram name="Viz.DisplayCompositor.OverlayStrategy"
-    enum="OverlayStrategies" expires_after="2022-05-15">
-  <owner>dcastagna@chromium.org</owner>
-  <owner>hoegsberg@chromium.org</owner>
-  <summary>
-    Overlay strategies used to promote Hardware Overlays, per frame. Recorded
-    every time a frame is rendered by the display compositor.
-  </summary>
-</histogram>
-
-<histogram name="Viz.DisplayCompositor.OverlaySwitchInterval" units="ms"
-    expires_after="2022-05-01">
-  <owner>petermcneeley@chromium.org</owner>
-  <owner>dcastagna@chromium.org</owner>
-  <summary>
-    The time, in milliseconds, since the change in overlay selection. Recorded
-    every time a frame is rendered by the display compositor.
-  </summary>
-</histogram>
-
-<histogram name="Viz.DisplayCompositor.RootDamageRect.Overlay"
-    enum="BooleanOverlayDamageRect" expires_after="2022-04-17">
-  <owner>magchen@chromium.org</owner>
-  <owner>zmo@chromium.org</owner>
-  <summary>
-    Any root damage excluding overlay damage in the current frame?
-  </summary>
-</histogram>
-
-<histogram name="Viz.DisplayCompositor.RootDamageRect.Underlay"
-    enum="UnderlayDamageRect" expires_after="2022-04-17">
-  <owner>magchen@chromium.org</owner>
-  <owner>zmo@chromium.org</owner>
-  <summary>
-    The root damage type excluding underlay damage in the current frame.
-  </summary>
-</histogram>
-
-<histogram name="Viz.FileDescriptorTracking.TimeToCompute" units="microseconds"
-    expires_after="2022-07-09">
-  <owner>petermcneeley@chromium.org</owner>
-  <owner>rjkroege@chromium.org</owner>
-  <summary>
-    Time spent computing the number of active File Descriptors. This is logged
-    once every 5 minutes as the cost of this computation is estimated to be at
-    least 1ms. Currently only recorded for LaCros delegated compositing.
-
-    Warning: This metric does not include reports from clients with
-    low-resolution clocks.
-  </summary>
-</histogram>
-
-<histogram name="Viz.FileDescriptorTracking.{FdStat}" units="units"
-    expires_after="2022-07-09">
-  <owner>petermcneeley@chromium.org</owner>
-  <owner>rjkroege@chromium.org</owner>
-  <summary>
-    {FdStat} File Descriptors for the GPU process. This is logged once every 5
-    minutes as the cost of this computation is estimated to be at least 1ms.
-    Currently only recorded for LaCros delegated compositing.
-  </summary>
-  <token key="FdStat">
-    <variant name="NumActive" summary="Current number of active"/>
-    <variant name="NumSoftMax" summary="Maximum number of"/>
-    <variant name="PercentageUsed" summary="Percentage of in use"/>
-  </token>
-</histogram>
-
-<histogram name="Viz.FrameSinkVideoCapturer.I420.CaptureDuration" units="ms"
-    expires_after="2021-03-07">
-  <owner>samans@chromium.org</owner>
-  <owner>sadrul@chromium.org</owner>
-  <owner>viz-team-wat@google.com</owner>
-  <summary>
-    The time it took from when FrameSinkVideoCapturerImpl sent a request for an
-    I420 readback until the result comes back and ReadI420Planes successfully
-    finishes.
-  </summary>
-</histogram>
-
-<histogram name="Viz.FrameSinkVideoCapturer.I420.CaptureSucceeded"
-    enum="BooleanSuccess" expires_after="2020-12-31">
-  <owner>samans@chromium.org</owner>
-  <owner>sadrul@chromium.org</owner>
-  <owner>viz-team-wat@google.com</owner>
-  <summary>
-    Whether an I420 readback initiated by FrameSinkVideoCapturerImpl succeeded.
-  </summary>
-</histogram>
-
-<histogram name="Viz.FrameSinkVideoCapturer.RGBA.CaptureDuration" units="ms"
-    expires_after="2022-05-01">
-  <owner>samans@chromium.org</owner>
-  <owner>sadrul@chromium.org</owner>
-  <owner>viz-team-wat@google.com</owner>
-  <summary>
-    The time it took from when FrameSinkVideoCapturerImpl sent a request for an
-    RGBA readback until the result comes back and ReadRGBAPlane successfully
-    finishes.
-  </summary>
-</histogram>
-
-<histogram name="Viz.FrameSinkVideoCapturer.RGBA.CaptureSucceeded"
-    enum="BooleanSuccess" expires_after="2020-12-31">
-  <owner>samans@chromium.org</owner>
-  <owner>sadrul@chromium.org</owner>
-  <owner>viz-team-wat@google.com</owner>
-  <summary>
-    Whether an RGBA readback initiated by FrameSinkVideoCapturerImpl succeeded.
-  </summary>
-</histogram>
-
 <histogram name="VoiceInteraction.AssistantActionPerformed{Source}"
     enum="AssistantActionPerformed" expires_after="2022-06-12">
   <owner>jds@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/profile/histograms.xml b/tools/metrics/histograms/metadata/profile/histograms.xml
index 518d54d..93321828 100644
--- a/tools/metrics/histograms/metadata/profile/histograms.xml
+++ b/tools/metrics/histograms/metadata/profile/histograms.xml
@@ -949,7 +949,8 @@
 
 <histogram name="ProfilePicker.StartupTime.BeforeCreation" units="ms"
     expires_after="2022-05-01">
-  <owner>msalama@chromium.org</owner>
+  <owner>alexilin@chromium.org</owner>
+  <owner>dgn@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
     Records the time between Chrome startup and the call to
@@ -960,7 +961,8 @@
 
 <histogram name="ProfilePicker.StartupTime.FirstPaint" units="ms"
     expires_after="2022-05-01">
-  <owner>msalama@chromium.org</owner>
+  <owner>alexilin@chromium.org</owner>
+  <owner>dgn@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
     Records the time between web view creation and the first paint. Only
@@ -971,6 +973,7 @@
 <histogram name="ProfilePicker.StartupTime.FirstPaint.FromApplicationStart"
     units="ms" expires_after="2022-06-05">
   <owner>alexilin@chromium.org</owner>
+  <owner>dgn@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
     Records the time from the application start to the first non-empty paint of
@@ -983,8 +986,9 @@
 </histogram>
 
 <histogram name="ProfilePicker.StartupTime.MainViewInitialized" units="ms"
-    expires_after="2021-12-26">
-  <owner>msalama@chromium.org</owner>
+    expires_after="2022-05-01">
+  <owner>alexilin@chromium.org</owner>
+  <owner>dgn@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
     Records the time between web view creation and main view being initialized
@@ -993,8 +997,9 @@
 </histogram>
 
 <histogram name="ProfilePicker.StartupTime.WebViewCreated" units="ms"
-    expires_after="2021-12-26">
-  <owner>msalama@chromium.org</owner>
+    expires_after="2022-05-01">
+  <owner>alexilin@chromium.org</owner>
+  <owner>dgn@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
     Records the time between ProfilePickerView::Display() is called and the web
diff --git a/tools/metrics/histograms/metadata/security/histograms.xml b/tools/metrics/histograms/metadata/security/histograms.xml
index 022b7e1..534f6eb 100644
--- a/tools/metrics/histograms/metadata/security/histograms.xml
+++ b/tools/metrics/histograms/metadata/security/histograms.xml
@@ -702,7 +702,7 @@
 </histogram>
 
 <histogram name="Security.SCTAuditing.OptIn.DedupeCacheHWM" units="reports"
-    expires_after="2022-02-01">
+    expires_after="2022-09-01">
   <owner>cthomp@chromium.org</owner>
   <owner>trusty-transport@chromium.org</owner>
   <summary>
@@ -712,7 +712,7 @@
 </histogram>
 
 <histogram name="Security.SCTAuditing.OptIn.ReportCompletionStatus"
-    enum="SCTAuditingReportCompletionStatus" expires_after="2022-02-01">
+    enum="SCTAuditingReportCompletionStatus" expires_after="2022-09-01">
   <owner>cthomp@chromium.org</owner>
   <owner>trusty-transport@chromium.org</owner>
   <summary>
@@ -722,9 +722,9 @@
 </histogram>
 
 <histogram name="Security.SCTAuditing.OptIn.ReportDeduplicated" enum="Boolean"
-    expires_after="2022-04-17">
+    expires_after="2022-09-01">
   <owner>cthomp@chromium.org</owner>
-  <owner>security-enamel@chromium.org</owner>
+  <owner>trusty-transport@chromium.org</owner>
   <summary>
     Records whether a report was deduplicated (i.e., the SCTs were already in
     the cache) or not. Recorded on each new report seen by the SCT auditing
@@ -733,7 +733,7 @@
 </histogram>
 
 <histogram name="Security.SCTAuditing.OptIn.ReportersHWM" units="reporters"
-    expires_after="2022-02-01">
+    expires_after="2022-09-01">
   <owner>cthomp@chromium.org</owner>
   <owner>trusty-transport@chromium.org</owner>
   <summary>
@@ -743,9 +743,9 @@
 </histogram>
 
 <histogram name="Security.SCTAuditing.OptIn.ReportSampled" enum="Boolean"
-    expires_after="2021-12-12">
+    expires_after="2022-09-01">
   <owner>cthomp@chromium.org</owner>
-  <owner>security-enamel@chromium.org</owner>
+  <owner>trusty-transport@chromium.org</owner>
   <summary>
     Records whether a report was sampled to be sent to Safe Browsing. Recorded
     for each new report seen by the SCT auditing cache that was not
@@ -754,9 +754,9 @@
 </histogram>
 
 <histogram name="Security.SCTAuditing.OptIn.ReportSize" units="bytes"
-    expires_after="2021-12-12">
+    expires_after="2022-09-01">
   <owner>cthomp@chromium.org</owner>
-  <owner>security-enamel@chromium.org</owner>
+  <owner>trusty-transport@chromium.org</owner>
   <summary>
     Records the size of an SCT auditing report that will be sent to Safe
     Browsing (i.e., was not deduplicated and was sampled).
@@ -764,9 +764,9 @@
 </histogram>
 
 <histogram name="Security.SCTAuditing.OptIn.ReportSucceeded" enum="Boolean"
-    expires_after="2021-12-12">
+    expires_after="2022-09-01">
   <owner>cthomp@chromium.org</owner>
-  <owner>security-enamel@chromium.org</owner>
+  <owner>trusty-transport@chromium.org</owner>
   <summary>
     Records whether sending an SCT auditing report succeeded or not. This is
     recorded after we receive the headers from the server (or we stop due to a
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 955d192..222792e 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -15396,12 +15396,11 @@
 </event>
 
 <event name="PrefetchProxy" singular="True">
-  <owner>robertogden@chromium.org</owner>
+  <owner>curranmax@chromium.org</owner>
   <owner>ryansturm@chromium.org</owner>
-  <owner>tbansal@chromium.org</owner>
+  <owner>spelchat@chromium.org</owner>
   <summary>
-    Metrics related to the privacy-preserving prefetch proxy. Recorded only for
-    Lite mode (AKA Data Saver) users.
+    Metrics related to the privacy-preserving prefetch proxy.
   </summary>
   <metric name="count_css_js_loaded_cache_before_fcp">
     <summary>
@@ -15474,9 +15473,9 @@
 </event>
 
 <event name="PrefetchProxy.AfterSRPClick" singular="True">
-  <owner>robertogden@chromium.org</owner>
+  <owner>curranmax@chromium.org</owner>
   <owner>ryansturm@chromium.org</owner>
-  <owner>tbansal@chromium.org</owner>
+  <owner>spelchat@chromium.org</owner>
   <summary>
     Metrics related to the privacy-preserving prefetch proxy on the page load
     after the SRP. Recorded only for eligible users on the first mainframe
@@ -15513,9 +15512,9 @@
 </event>
 
 <event name="PrefetchProxy.PrefetchedResource">
-  <owner>robertogden@chromium.org</owner>
+  <owner>curranmax@chromium.org</owner>
   <owner>ryansturm@chromium.org</owner>
-  <owner>tbansal@chromium.org</owner>
+  <owner>spelchat@chromium.org</owner>
   <summary>
     Each event corresponds to a single resource, either mainframe or
     subresource, that was prefetched during an Isolated Prerender. Logged one or
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 95cdffe..c04afacb 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,20 +5,20 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "e86a33a1d4a5e95146e8d502232aefa4d085cd28",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/f9abf9948a180a56a3595ec54ff9f2f5c2c9947c/trace_processor_shell.exe"
+            "hash": "c4efce6c0cefe5e1c137bbbfe5ed1189b38b8da9",
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/22467673a50f5599e7d8b1f49982faf7c1bc30d9/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "851992a7ccb80791df659bcca23f47292cfce319",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/f9abf9948a180a56a3595ec54ff9f2f5c2c9947c/trace_processor_shell"
+            "hash": "11daea10c30ca26ac3695540f3b07b83665ed7e1",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/22467673a50f5599e7d8b1f49982faf7c1bc30d9/trace_processor_shell"
         },
         "linux_arm64": {
             "hash": "5074025a2898ec41a872e70a5719e417acb0a380",
             "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "linux": {
-            "hash": "bfcfedfd42706342bbca38ea59198da6673d7a06",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/f9abf9948a180a56a3595ec54ff9f2f5c2c9947c/trace_processor_shell"
+            "hash": "bedc0c16f454388d206b5b054fc36122bbeb4ba2",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/22467673a50f5599e7d8b1f49982faf7c1bc30d9/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 8a9a6e4c..b25972e 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -651,9 +651,6 @@
 
 # Benchmark: wasmpspdfkit
 crbug.com/1211795 [ mac ] wasmpspdfkit/https://pspdfkit.com/webassembly-benchmark/ [ Skip ]
-crbug.com/1275969 [ win ] wasmpspdfkit/https://pspdfkit.com/webassembly-benchmark/ [ Skip ]
-crbug.com/1275969 [ android ] wasmpspdfkit/https://pspdfkit.com/webassembly-benchmark/ [ Skip ]
-crbug.com/1275969 [ linux ] wasmpspdfkit/https://pspdfkit.com/webassembly-benchmark/ [ Skip ]
 
 # Benchmark: webrtc
 crbug.com/1051644 [ win ] webrtc/pause_play_peerconnections [ Skip ]
diff --git a/tools/perf/page_sets/data/WasmPsPdfKit.json b/tools/perf/page_sets/data/WasmPsPdfKit.json
index f796a1c..0bef0f63 100644
--- a/tools/perf/page_sets/data/WasmPsPdfKit.json
+++ b/tools/perf/page_sets/data/WasmPsPdfKit.json
@@ -1,7 +1,7 @@
 {
     "archives": {
         "https://pspdfkit.com/webassembly-benchmark/": {
-            "DEFAULT": "WasmPsPdfKit_49bbf1766f.wprgo"
+            "DEFAULT": "WasmPsPdfKit_9d6928a535.wprgo"
         }
     },
     "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
diff --git a/tools/perf/page_sets/data/WasmPsPdfKit_49bbf1766f.wprgo.sha1 b/tools/perf/page_sets/data/WasmPsPdfKit_49bbf1766f.wprgo.sha1
deleted file mode 100644
index 5291144..0000000
--- a/tools/perf/page_sets/data/WasmPsPdfKit_49bbf1766f.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-49bbf1766fd6dda019ec7697959cef5c6005dd7d
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/WasmPsPdfKit_9d6928a535.wprgo.sha1 b/tools/perf/page_sets/data/WasmPsPdfKit_9d6928a535.wprgo.sha1
new file mode 100644
index 0000000..1d1c75d
--- /dev/null
+++ b/tools/perf/page_sets/data/WasmPsPdfKit_9d6928a535.wprgo.sha1
@@ -0,0 +1 @@
+9d6928a535a5c26543536bcd57338c0dd80068e7
\ No newline at end of file
diff --git a/ui/android/java/res/color/blue_when_enabled.xml b/ui/android/java/res/color/blue_when_enabled.xml
index 6e098c1..3ac7966 100644
--- a/ui/android/java/res/color/blue_when_enabled.xml
+++ b/ui/android/java/res/color/blue_when_enabled.xml
@@ -4,6 +4,6 @@
      found in the LICENSE file.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="@color/default_text_color_link_disabled" android:state_enabled="false" />
-    <item android:color="@color/default_text_color_link"/>
+    <item android:color="@color/default_text_color_link_disabled_baseline" android:state_enabled="false" />
+    <item android:color="@color/default_text_color_link_baseline"/>
 </selector>
diff --git a/ui/android/java/res/values-night/colors.xml b/ui/android/java/res/values-night/colors.xml
index 518bb081..8139f79 100644
--- a/ui/android/java/res/values-night/colors.xml
+++ b/ui/android/java/res/values-night/colors.xml
@@ -9,8 +9,8 @@
     <color name="default_text_color_inverse">@color/default_text_color_dark</color>
     <color name="default_text_color_secondary">@color/default_text_color_secondary_light</color>
     <color name="default_text_color_blue_baseline">@color/default_text_color_blue_light</color>
-    <color name="default_text_color_link">@color/default_text_color_link_light</color>
-    <color name="default_text_color_link_disabled">@color/default_text_color_link_disabled_light</color>
+    <color name="default_text_color_link_baseline">@color/default_text_color_link_light</color>
+    <color name="default_text_color_link_disabled_baseline">@color/default_text_color_link_disabled_light</color>
     <color name="default_text_color_error">@color/default_text_color_error_light</color>
     <color name="default_text_color_on_accent1_baseline">@color/default_text_color_on_accent1_dark</color>
     <color name="default_text_color_on_accent1_disabled_baseline">@color/default_text_color_disabled_light</color>
diff --git a/ui/android/java/res/values/semantic_colors_adaptive.xml b/ui/android/java/res/values/semantic_colors_adaptive.xml
index dfaf9fb..49d3d591 100644
--- a/ui/android/java/res/values/semantic_colors_adaptive.xml
+++ b/ui/android/java/res/values/semantic_colors_adaptive.xml
@@ -14,8 +14,8 @@
     <!-- Text color for non-clickable blue text. -->
     <color name="default_text_color_blue_baseline" tools:ignore="UnusedResources">@color/default_text_color_blue_dark</color>
     <!-- Text color for clickable text. -->
-    <color name="default_text_color_link">@color/default_text_color_link_dark</color>
-    <color name="default_text_color_link_disabled">@color/default_text_color_link_disabled_dark</color>
+    <color name="default_text_color_link_baseline">@color/default_text_color_link_dark</color>
+    <color name="default_text_color_link_disabled_baseline">@color/default_text_color_link_disabled_dark</color>
     <color name="default_text_color_error">@color/default_text_color_error_dark</color>
     <color name="default_text_color_on_accent1_baseline">@color/default_text_color_on_accent1_light</color>
     <color name="default_text_color_on_accent1_disabled_baseline">@color/default_text_color_disabled_dark</color>
diff --git a/ui/android/java/src/org/chromium/ui/text/NoUnderlineClickableSpan.java b/ui/android/java/src/org/chromium/ui/text/NoUnderlineClickableSpan.java
index c8d9ee8..53896d07 100644
--- a/ui/android/java/src/org/chromium/ui/text/NoUnderlineClickableSpan.java
+++ b/ui/android/java/src/org/chromium/ui/text/NoUnderlineClickableSpan.java
@@ -27,7 +27,7 @@
      * @param onClickCallback The callback notified when the span is clicked.
      */
     public NoUnderlineClickableSpan(Resources resources, Callback<View> onClickCallback) {
-        this(resources, R.color.default_text_color_link, onClickCallback);
+        this(resources, R.color.default_text_color_link_baseline, onClickCallback);
     }
 
     /**
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index 033acf24..1b994db 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -212,7 +212,7 @@
 bool g_egl_angle_external_context_and_surface_supported = false;
 bool g_egl_ext_query_device_supported = false;
 bool g_egl_angle_context_virtualization_supported = false;
-bool g_EGL_ANGLE_vulkan_image_supported = false;
+bool g_egl_angle_vulkan_image_supported = false;
 EGLGpuSwitchingObserver* g_egl_gpu_switching_observer = nullptr;
 
 constexpr const char kSwapEventTraceCategories[] = "gpu";
@@ -1129,7 +1129,7 @@
   g_egl_angle_context_virtualization_supported =
       HasEGLExtension("EGL_ANGLE_context_virtualization");
 
-  g_EGL_ANGLE_vulkan_image_supported =
+  g_egl_angle_vulkan_image_supported =
       HasEGLExtension("EGL_ANGLE_vulkan_image");
 
   if (g_egl_angle_power_preference_supported) {
@@ -1303,8 +1303,8 @@
 }
 
 // static
-bool GLSurfaceEGL::IsANGLEVulkanImageClientBufferSupported() {
-  return g_EGL_ANGLE_vulkan_image_supported;
+bool GLSurfaceEGL::IsANGLEVulkanImageSupported() {
+  return g_egl_angle_vulkan_image_supported;
 }
 
 bool GLSurfaceEGL::IsEGLQueryDeviceSupported() {
diff --git a/ui/gl/gl_surface_egl.h b/ui/gl/gl_surface_egl.h
index 5493db7..e95d3b6b 100644
--- a/ui/gl/gl_surface_egl.h
+++ b/ui/gl/gl_surface_egl.h
@@ -132,7 +132,7 @@
   static bool IsANGLEDisplayPowerPreferenceSupported();
   static bool IsANGLEExternalContextAndSurfaceSupported();
   static bool IsANGLEContextVirtualizationSupported();
-  static bool IsANGLEVulkanImageClientBufferSupported();
+  static bool IsANGLEVulkanImageSupported();
 
   static bool IsEGLQueryDeviceSupported();
 
diff --git a/ui/ozone/platform/wayland/host/wayland_input_method_context.cc b/ui/ozone/platform/wayland/host/wayland_input_method_context.cc
index 1ac224b..3187674 100644
--- a/ui/ozone/platform/wayland/host/wayland_input_method_context.cc
+++ b/ui/ozone/platform/wayland/host/wayland_input_method_context.cc
@@ -398,8 +398,38 @@
 
 VirtualKeyboardController*
 WaylandInputMethodContext::GetVirtualKeyboardController() {
-  // TODO(crbug.com/1141531): return VirtualKeyboardController for wayland.
-  return nullptr;
+  if (!text_input_)
+    return nullptr;
+  return this;
+}
+
+bool WaylandInputMethodContext::DisplayVirtualKeyboard() {
+  if (!text_input_)
+    return false;
+
+  text_input_->ShowInputPanel();
+  return true;
+}
+
+void WaylandInputMethodContext::DismissVirtualKeyboard() {
+  if (!text_input_)
+    return;
+
+  text_input_->HideInputPanel();
+}
+
+void WaylandInputMethodContext::AddObserver(
+    VirtualKeyboardControllerObserver* observer) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
+void WaylandInputMethodContext::RemoveObserver(
+    VirtualKeyboardControllerObserver* observer) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
+bool WaylandInputMethodContext::IsKeyboardVisible() {
+  return virtual_keyboard_visible_;
 }
 
 void WaylandInputMethodContext::OnPreeditString(
@@ -575,6 +605,14 @@
                                     ime_text_spans);
 }
 
+void WaylandInputMethodContext::OnInputPanelState(uint32_t state) {
+  virtual_keyboard_visible_ = (state & 1) != 0;
+  // Note: Currently there's no support of VirtualKeyboardControllerObserver.
+  // In the future, we may need to support it. Specifically,
+  // RenderWidgetHostViewAura would like to know the VirtualKeyboard's
+  // region somehow.
+}
+
 void WaylandInputMethodContext::OnKeyboardFocusedWindowChanged() {
   MaybeUpdateActivated();
 }
diff --git a/ui/ozone/platform/wayland/host/wayland_input_method_context.h b/ui/ozone/platform/wayland/host/wayland_input_method_context.h
index ef7077f..d2b7082 100644
--- a/ui/ozone/platform/wayland/host/wayland_input_method_context.h
+++ b/ui/ozone/platform/wayland/host/wayland_input_method_context.h
@@ -8,9 +8,11 @@
 #include <memory>
 #include <vector>
 
+#include "base/observer_list.h"
 #include "base/strings/string_piece.h"
 #include "ui/base/ime/character_composer.h"
 #include "ui/base/ime/linux/linux_input_method_context.h"
+#include "ui/base/ime/virtual_keyboard_controller.h"
 #include "ui/gfx/range/range.h"
 #include "ui/ozone/platform/wayland/host/wayland_keyboard.h"
 #include "ui/ozone/platform/wayland/host/wayland_window_observer.h"
@@ -22,6 +24,7 @@
 class ZWPTextInputWrapper;
 
 class WaylandInputMethodContext : public LinuxInputMethodContext,
+                                  public VirtualKeyboardController,
                                   public WaylandWindowObserver,
                                   public ZWPTextInputWrapperClient {
  public:
@@ -52,6 +55,13 @@
   void Blur() override;
   VirtualKeyboardController* GetVirtualKeyboardController() override;
 
+  // VirtualKeyboardController overrides:
+  bool DisplayVirtualKeyboard() override;
+  void DismissVirtualKeyboard() override;
+  void AddObserver(VirtualKeyboardControllerObserver* observer) override;
+  void RemoveObserver(VirtualKeyboardControllerObserver* observer) override;
+  bool IsKeyboardVisible() override;
+
   // WaylandWindowObserver overrides:
   void OnKeyboardFocusedWindowChanged() override;
 
@@ -65,6 +75,7 @@
   void OnSetPreeditRegion(int32_t index,
                           uint32_t length,
                           const std::vector<SpanStyle>& spans) override;
+  void OnInputPanelState(uint32_t state) override;
 
  private:
   void UpdatePreeditText(const std::u16string& preedit_text);
@@ -99,6 +110,9 @@
   std::string surrounding_text_;
   // The selection range in UTF-8 offsets in the |surrounding_text_|.
   gfx::Range selection_range_utf8_ = gfx::Range::InvalidRange();
+
+  // Caches VirtualKeyboard visibility.
+  bool virtual_keyboard_visible_ = false;
 };
 
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc b/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc
index a45f102..70b566e 100644
--- a/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc
@@ -494,6 +494,36 @@
       input_method_context_delegate_->was_on_set_preedit_region_called());
 }
 
+TEST_P(WaylandInputMethodContextTest, DisplayVirtualKeyboard) {
+  EXPECT_CALL(*zwp_text_input_, ShowInputPanel());
+  EXPECT_TRUE(input_method_context_->DisplayVirtualKeyboard());
+  connection_->ScheduleFlush();
+  Sync();
+}
+
+TEST_P(WaylandInputMethodContextTest, DismissVirtualKeyboard) {
+  EXPECT_CALL(*zwp_text_input_, HideInputPanel());
+  input_method_context_->DismissVirtualKeyboard();
+  connection_->ScheduleFlush();
+  Sync();
+}
+
+TEST_P(WaylandInputMethodContextTest, UpdateVirtualKeyboardState) {
+  EXPECT_FALSE(input_method_context_->IsKeyboardVisible());
+
+  zwp_text_input_v1_send_input_panel_state(zwp_text_input_->resource(), 1);
+  connection_->ScheduleFlush();
+  Sync();
+
+  EXPECT_TRUE(input_method_context_->IsKeyboardVisible());
+
+  zwp_text_input_v1_send_input_panel_state(zwp_text_input_->resource(), 0);
+  connection_->ScheduleFlush();
+  Sync();
+
+  EXPECT_FALSE(input_method_context_->IsKeyboardVisible());
+}
+
 class WaylandInputMethodContextNoKeyboardTest
     : public WaylandInputMethodContextTest {
  public:
diff --git a/ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h b/ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h
index 2b7c438..4bd18339 100644
--- a/ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h
+++ b/ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h
@@ -65,6 +65,14 @@
   virtual void OnSetPreeditRegion(int32_t index,
                                   uint32_t length,
                                   const std::vector<SpanStyle>& spans) = 0;
+
+  // Called when the visibility state of the input panel changed.
+  // There's no detailed spec of |state|, and no actual implementor except
+  // components/exo is found in the world at this moment.
+  // Thus, in ozone/wayland use the lowest bit as boolean
+  // (visible=1/invisible=0), and ignore other bits for future compatibility.
+  // This behavior must be consistent with components/exo.
+  virtual void OnInputPanelState(uint32_t state) = 0;
 };
 
 // A wrapper around different versions of wayland text input protocols.
diff --git a/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc b/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc
index e45a77bd..06e07cff 100644
--- a/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc
+++ b/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc
@@ -129,7 +129,8 @@
     void* data,
     struct zwp_text_input_v1* text_input,
     uint32_t state) {
-  NOTIMPLEMENTED_LOG_ONCE();
+  auto* self = static_cast<ZWPTextInputWrapperV1*>(data);
+  self->client_->OnInputPanelState(state);
 }
 
 // static
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index e643f4b..0201835 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -2540,6 +2540,12 @@
       MenuRunner::HAS_MNEMONICS | MenuRunner::CONTEXT_MENU);
 }
 
+void Textfield::InvalidateContextMenu() {
+  // Ensure that the Runner doesn't outlive the Model.
+  context_menu_runner_.reset();
+  context_menu_contents_.reset();
+}
+
 bool Textfield::ImeEditingAllowed() const {
   // Disallow input method editing of password fields.
   ui::TextInputType t = GetTextInputType();
diff --git a/ui/views/controls/textfield/textfield.h b/ui/views/controls/textfield/textfield.h
index 21033d7..95be0939 100644
--- a/ui/views/controls/textfield/textfield.h
+++ b/ui/views/controls/textfield/textfield.h
@@ -525,6 +525,11 @@
   // Update the cursor position in the text field.
   void UpdateCursorViewPosition();
 
+  // If there's an existing context menu, invalidate it, maybe closing it if
+  // it's showing. This is required if part of the context menu's model is about
+  // to be destroyed.
+  void InvalidateContextMenu();
+
  private:
   friend class TextfieldTestApi;
 
diff --git a/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.m.d.ts b/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.m.d.ts
index cc31c310..674e241 100644
--- a/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.m.d.ts
+++ b/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.m.d.ts
@@ -5,7 +5,7 @@
 import {LegacyElementMixin} from 'chrome://resources/polymer/v3_0/polymer/lib/legacy/legacy-element-mixin.js';
 
 interface CrIconButtonElement extends LegacyElementMixin, HTMLElement {
-  disabled: boolean|null|undefined;
+  disabled: boolean;
   customTabIndex: number|null|undefined;
   ironIcon: string|null|undefined;
   hostAttributes: object|null;
diff --git a/weblayer/browser/client_hints_factory.cc b/weblayer/browser/client_hints_factory.cc
index 9664c22f..d65a8d2 100644
--- a/weblayer/browser/client_hints_factory.cc
+++ b/weblayer/browser/client_hints_factory.cc
@@ -42,7 +42,7 @@
       context, BrowserProcess::GetInstance()->GetNetworkQualityTracker(),
       HostContentSettingsMapFactory::GetForBrowserContext(context),
       CookieSettingsFactory::GetForBrowserContext(context),
-      embedder_support::GetUserAgentMetadata());
+      BrowserProcess::GetInstance()->GetLocalState());
 }
 
 content::BrowserContext* ClientHintsFactory::GetBrowserContextToUse(