diff --git a/AUTHORS b/AUTHORS
index f373ec9e..ebbd358 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1277,6 +1277,7 @@
 Venture 3 Systems LLC <*@venture3systems.com>
 Vewd Software AS <*@vewd.com>
 Vivaldi Technologies AS <*@vivaldi.com>
+Wacom <*@wacom.com>
 Yandex LLC <*@yandex-team.ru>
 # Please DO NOT APPEND here. See comments at the top of the file.
 # END organizations section.
diff --git a/DEPS b/DEPS
index 3900e77..bdc7e8a6 100644
--- a/DEPS
+++ b/DEPS
@@ -209,7 +209,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': 'ff8b52df55fffbfeacaa8da65fe3d397dfee427b',
+  'skia_revision': '8f39137a45339830691d0911894eabf3fb25a89e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -221,7 +221,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': '4ab224f7b05ace9bcfcd00f0dd6d603b194091c8',
+  'angle_revision': '03a9bf1ea420da81c411cbbcb04f15e350f95b5f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -256,7 +256,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': '7c714ed900b83db7db82077bf478233cd42a9a1c',
+  'nacl_revision': 'dad64a4e4e0c40ff2471a39748494e65f2d9f7b9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -280,7 +280,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': 'a934af88835906e8c2843972dfac6295bd5dafc8',
+  'catapult_revision': '785c56fc0ffa12b96475d37c5bd44e432e6f9c59',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -288,7 +288,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': '9a145b3598b7f2139b8149efe2c82889a279f853',
+  'devtools_frontend_revision': 'c0969ff0ad4faf423ed1cc3a20feec97990b2580',
   # 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.
@@ -643,7 +643,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/linux-amd64',
-          'version': 'MZwyLjNaFyHnOcU0G5IW27ouk3UoZYF5WrMIwn35yuwC',
+          'version': 'oeoQzSzs7TVxdItXFVzVHiYUocanjhc799DduUfj-S0C',
         },
       ],
       'dep_type': 'cipd',
@@ -654,7 +654,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': 'NpEliTduVpeYtsPsmz3EbL91y9lLV8U0r68T6HYNu_wC',
+          'version': 'TJjRB2dYdQGXH0TtNl_y_JT_1stRwadWKtneHg79nMMC',
         },
       ],
       'dep_type': 'cipd',
@@ -665,7 +665,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/windows-amd64',
-          'version': 'wNkFrxma6lCFycXDdHX1Gunjow5_8_NCacfKImq908UC',
+          'version': 'hnyWLOusdf5vsW0c0RL6MQwaUl2IbLn5RmefmU_g0CQC',
         },
       ],
       'dep_type': 'cipd',
@@ -946,7 +946,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' + '@' + '0828e9e93022c7a57616fc42e546a077a241806d',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'c5b619faabf18739581c4a15e3b6f93e03f4d301',
       'condition': 'checkout_chromeos',
   },
 
@@ -966,7 +966,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'f663e543389de90c5573825a654721b367c31633',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '33c8e2b578e58f626c1417f96ab3ddc8418b6a30',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1613,7 +1613,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@fc79d057f820f8cdbdb4cb458b2c87bfd1b60195',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@7ed3ce766cd9f5944e8c6a092110e928286f4408',
     'condition': 'checkout_src_internal',
   },
 
@@ -1632,7 +1632,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'EWaZJ_V9Mwpy_Z6Id5K38yan2Jrs6hiN7ioLAHd0WoUC',
+        'version': 'JGsOzVAb46Ymzku7DjvyxxCeTGDJSvYRS6NTSg6gfn8C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1643,7 +1643,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'xq4pEEOIawHakyveSSX6nidBuCtMLJdLlNXPvAKJ2k0C',
+        'version': '2B5B05ygDeofr7aRL1FFD24nF7DFcgThqzOGAq1qSwsC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index afad320..12442ea54 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -2704,8 +2704,6 @@
     "shelf/shelf_test_util.h",
     "shelf/shelf_view_test_api.cc",
     "shelf/shelf_view_test_api.h",
-    "shelf/test/widget_animation_waiter.cc",
-    "shelf/test/widget_animation_waiter.h",
     "shell_test_api.cc",
     "system/holding_space/holding_space_test_api.cc",
     "system/message_center/test_notifier_settings_controller.cc",
diff --git a/ash/assistant/assistant_controller_impl.cc b/ash/assistant/assistant_controller_impl.cc
index ac79fe6..e296e91 100644
--- a/ash/assistant/assistant_controller_impl.cc
+++ b/ash/assistant/assistant_controller_impl.cc
@@ -219,7 +219,6 @@
     case DeepLinkType::kLists:
     case DeepLinkType::kNotes:
     case DeepLinkType::kOnboarding:
-    case DeepLinkType::kProactiveSuggestions:
     case DeepLinkType::kQuery:
     case DeepLinkType::kReminders:
     case DeepLinkType::kSettings:
diff --git a/ash/assistant/assistant_interaction_controller_impl.cc b/ash/assistant/assistant_interaction_controller_impl.cc
index 9ef07f085..ac8bbac9 100644
--- a/ash/assistant/assistant_interaction_controller_impl.cc
+++ b/ash/assistant/assistant_interaction_controller_impl.cc
@@ -365,6 +365,8 @@
 // pending query that occur outside of this method.
 void AssistantInteractionControllerImpl::OnInteractionStarted(
     const AssistantInteractionMetadata& metadata) {
+  VLOG(1) << __func__;
+
   // Abort any request in progress.
   screen_context_request_factory_.InvalidateWeakPtrs();
 
@@ -416,6 +418,8 @@
 
 void AssistantInteractionControllerImpl::OnInteractionFinished(
     AssistantInteractionResolution resolution) {
+  VLOG(1) << __func__;
+
   base::UmaHistogramEnumeration("Assistant.Interaction.Resolution", resolution);
   model_.SetMicState(MicState::kClosed);
 
diff --git a/ash/assistant/ui/main_stage/assistant_card_element_view.cc b/ash/assistant/ui/main_stage/assistant_card_element_view.cc
index 529651e..aa42defe 100644
--- a/ash/assistant/ui/main_stage/assistant_card_element_view.cc
+++ b/ash/assistant/ui/main_stage/assistant_card_element_view.cc
@@ -28,7 +28,6 @@
 
 using assistant::util::DeepLinkParam;
 using assistant::util::DeepLinkType;
-using assistant::util::ProactiveSuggestionsAction;
 
 // Helpers ---------------------------------------------------------------------
 
@@ -169,24 +168,6 @@
     const GURL& url,
     WindowOpenDisposition disposition,
     bool from_user_gesture) {
-  // Proactive suggestion deep links may be invoked without a user gesture to
-  // log view impressions. Those are (currently) the only deep links we allow to
-  // be processed without originating from a user event.
-  if (!from_user_gesture) {
-    DeepLinkType deep_link_type = assistant::util::GetDeepLinkType(url);
-    if (deep_link_type != DeepLinkType::kProactiveSuggestions) {
-      NOTREACHED();
-      return;
-    }
-
-    const base::Optional<ProactiveSuggestionsAction> action =
-        assistant::util::GetDeepLinkParamAsProactiveSuggestionsAction(
-            assistant::util::GetDeepLinkParams(url), DeepLinkParam::kAction);
-    if (action != ProactiveSuggestionsAction::kViewImpression) {
-      NOTREACHED();
-      return;
-    }
-  }
   // We delegate navigation to the AssistantController so that it can apply
   // special handling to deep links.
   AssistantController::Get()->OpenUrl(url);
diff --git a/ash/assistant/util/assistant_util.cc b/ash/assistant/util/assistant_util.cc
index 7879fa1c..eb2a4aa 100644
--- a/ash/assistant/util/assistant_util.cc
+++ b/ash/assistant/util/assistant_util.cc
@@ -44,7 +44,6 @@
     case AssistantEntryPoint::kDeepLink:
     case AssistantEntryPoint::kLauncherChip:
     case AssistantEntryPoint::kLauncherSearchResult:
-    case AssistantEntryPoint::kProactiveSuggestions:
     case AssistantEntryPoint::kSetup:
     case AssistantEntryPoint::kStylus:
       return false;
diff --git a/ash/assistant/util/deep_link_util.cc b/ash/assistant/util/deep_link_util.cc
index 000928b..964a2ca 100644
--- a/ash/assistant/util/deep_link_util.cc
+++ b/ash/assistant/util/deep_link_util.cc
@@ -50,12 +50,6 @@
 constexpr char kRemoveAlarmOrTimer[] = "removeAlarmOrTimer";
 constexpr char kResumeTimer[] = "resumeTimer";
 
-// Supported proactive suggestions action deep link param values.
-constexpr char kCardClick[] = "cardClick";
-constexpr char kEntryPointClick[] = "entryPointClick";
-constexpr char kEntryPointClose[] = "entryPointClose";
-constexpr char kViewImpression[] = "viewImpression";
-
 // Supported reminder action deep link param values.
 constexpr char kCreateReminder[] = "create";
 constexpr char kEditReminder[] = "edit";
@@ -68,8 +62,6 @@
 constexpr char kAssistantListsPrefix[] = "googleassistant://lists";
 constexpr char kAssistantNotesPrefix[] = "googleassistant://notes";
 constexpr char kAssistantOnboardingPrefix[] = "googleassistant://onboarding";
-constexpr char kAssistantProactiveSuggestionsPrefix[] =
-    "googleassistant://proactive-suggestions";
 constexpr char kAssistantQueryPrefix[] = "googleassistant://send-query";
 constexpr char kAssistantRemindersPrefix[] = "googleassistant://reminders";
 constexpr char kAssistantScreenshotPrefix[] =
@@ -322,22 +314,6 @@
   return base::nullopt;
 }
 
-base::Optional<ProactiveSuggestionsAction>
-GetDeepLinkParamAsProactiveSuggestionsAction(
-    const std::map<std::string, std::string>& params,
-    DeepLinkParam param) {
-  const base::Optional<std::string>& value = GetDeepLinkParam(params, param);
-  if (value == kCardClick)
-    return ProactiveSuggestionsAction::kCardClick;
-  if (value == kEntryPointClick)
-    return ProactiveSuggestionsAction::kEntryPointClick;
-  if (value == kEntryPointClose)
-    return ProactiveSuggestionsAction::kEntryPointClose;
-  if (value == kViewImpression)
-    return ProactiveSuggestionsAction::kViewImpression;
-  return base::nullopt;
-}
-
 base::Optional<AssistantQuerySource> GetDeepLinkParamAsQuerySource(
     const std::map<std::string, std::string>& params,
     DeepLinkParam param) {
@@ -385,8 +361,6 @@
       {DeepLinkType::kLists, kAssistantListsPrefix},
       {DeepLinkType::kNotes, kAssistantNotesPrefix},
       {DeepLinkType::kOnboarding, kAssistantOnboardingPrefix},
-      {DeepLinkType::kProactiveSuggestions,
-       kAssistantProactiveSuggestionsPrefix},
       {DeepLinkType::kQuery, kAssistantQueryPrefix},
       {DeepLinkType::kReminders, kAssistantRemindersPrefix},
       {DeepLinkType::kScreenshot, kAssistantScreenshotPrefix},
@@ -503,7 +477,6 @@
     case DeepLinkType::kChromeSettings:
     case DeepLinkType::kFeedback:
     case DeepLinkType::kOnboarding:
-    case DeepLinkType::kProactiveSuggestions:
     case DeepLinkType::kQuery:
     case DeepLinkType::kScreenshot:
     case DeepLinkType::kTaskManager:
diff --git a/ash/assistant/util/deep_link_util.h b/ash/assistant/util/deep_link_util.h
index a69fb9b0..fdac42c 100644
--- a/ash/assistant/util/deep_link_util.h
+++ b/ash/assistant/util/deep_link_util.h
@@ -34,7 +34,6 @@
   kLists,
   kNotes,
   kOnboarding,
-  kProactiveSuggestions,
   kQuery,
   kReminders,
   kScreenshot,
@@ -72,14 +71,6 @@
   kResumeTimer,
 };
 
-// Enumeration of proactive suggestions deep link actions.
-enum class ProactiveSuggestionsAction {
-  kCardClick,
-  kEntryPointClick,
-  kEntryPointClose,
-  kViewImpression,
-};
-
 // Enumeration of reminder deep link actions.
 enum class ReminderAction {
   kCreate,
@@ -173,15 +164,6 @@
     const std::map<std::string, std::string>& params,
     DeepLinkParam param);
 
-// Returns a specific ProactiveSuggestionsAction |param| from the given
-// parameters. If the desired parameter is not found, an empty value is
-// returned.
-COMPONENT_EXPORT(ASSISTANT_UTIL)
-base::Optional<ProactiveSuggestionsAction>
-GetDeepLinkParamAsProactiveSuggestionsAction(
-    const std::map<std::string, std::string>& params,
-    DeepLinkParam param);
-
 // Returns a specific query source |param| from the given parameters. If the
 // desired parameter is not found or is not mappable to an Assistant query
 // source, an empty value is returned.
diff --git a/ash/assistant/util/deep_link_util_unittest.cc b/ash/assistant/util/deep_link_util_unittest.cc
index f8e65df..b21a20574 100644
--- a/ash/assistant/util/deep_link_util_unittest.cc
+++ b/ash/assistant/util/deep_link_util_unittest.cc
@@ -451,45 +451,6 @@
   AssertDeepLinkParamEq(base::nullopt, DeepLinkParam::kAction);
 }
 
-TEST_F(DeepLinkUtilTest, GetDeepLinkParamAsProactiveSuggestionsAction) {
-  std::map<std::string, std::string> params;
-
-  auto AssertDeepLinkParamEq =
-      [&params](const base::Optional<ProactiveSuggestionsAction>& expected,
-                DeepLinkParam param) {
-        ASSERT_EQ(expected,
-                  GetDeepLinkParamAsProactiveSuggestionsAction(params, param));
-      };
-
-  // Case: Deep link parameter present, well formed "cardClick".
-  params["action"] = "cardClick";
-  AssertDeepLinkParamEq(ProactiveSuggestionsAction::kCardClick,
-                        DeepLinkParam::kAction);
-
-  // Case: Deep link parameter present, well formed "entryPointClick".
-  params["action"] = "entryPointClick";
-  AssertDeepLinkParamEq(ProactiveSuggestionsAction::kEntryPointClick,
-                        DeepLinkParam::kAction);
-
-  // Case: Deep link parameter present, well formed "entryPointClose".
-  params["action"] = "entryPointClose";
-  AssertDeepLinkParamEq(ProactiveSuggestionsAction::kEntryPointClose,
-                        DeepLinkParam::kAction);
-
-  // Case: Deep link parameter present, well formed "viewImpression".
-  params["action"] = "viewImpression";
-  AssertDeepLinkParamEq(ProactiveSuggestionsAction::kViewImpression,
-                        DeepLinkParam::kAction);
-
-  // Case: Deep link parameter present, incorrect parameter.
-  params["action"] = "invalid";
-  AssertDeepLinkParamEq(base::nullopt, DeepLinkParam::kAction);
-
-  // Case: Deep link parameter absent.
-  params.clear();
-  AssertDeepLinkParamEq(base::nullopt, DeepLinkParam::kAction);
-}
-
 TEST_F(DeepLinkUtilTest, GetDeepLinkParamAsRemindersAction) {
   std::map<std::string, std::string> params;
 
@@ -524,8 +485,6 @@
       {"googleassistant://lists", DeepLinkType::kLists},
       {"googleassistant://notes", DeepLinkType::kNotes},
       {"googleassistant://onboarding", DeepLinkType::kOnboarding},
-      {"googleassistant://proactive-suggestions",
-       DeepLinkType::kProactiveSuggestions},
       {"googleassistant://reminders", DeepLinkType::kReminders},
       {"googleassistant://send-feedback", DeepLinkType::kFeedback},
       {"googleassistant://send-query", DeepLinkType::kQuery},
@@ -541,8 +500,6 @@
       {"googleassistant://lists?param=true", DeepLinkType::kLists},
       {"googleassistant://notes?param=true", DeepLinkType::kNotes},
       {"googleassistant://onboarding?param=true", DeepLinkType::kOnboarding},
-      {"googleassistant://proactive-suggestions?param=true",
-       DeepLinkType::kProactiveSuggestions},
       {"googleassistant://reminders?param=true", DeepLinkType::kReminders},
       {"googleassistant://send-feedback?param=true", DeepLinkType::kFeedback},
       {"googleassistant://send-query?param=true", DeepLinkType::kQuery},
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index f035ab9..be5b4fa 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -480,10 +480,6 @@
 const base::Feature kLanguageSettingsUpdate2{"LanguageSettingsUpdate2",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables or disables device management disclosure on login / lock screen.
-const base::Feature kLoginDeviceManagementDisclosure{
-    "LoginDeviceManagementDisclosure", base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Controls whether to enable the requirement of a minimum chrome version on the
 // device through the policy DeviceMinimumVersion. If the requirement is
 // not met and the warning time in the policy has expired, the user is
@@ -836,10 +832,6 @@
   return base::FeatureList::IsEnabled(kKerberosSettingsSection);
 }
 
-bool IsLoginDeviceManagementDisclosureEnabled() {
-  return base::FeatureList::IsEnabled(kLoginDeviceManagementDisclosure);
-}
-
 bool IsMinimumChromeVersionEnabled() {
   return base::FeatureList::IsEnabled(kMinimumChromeVersion);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index cf0248a..33c6b48 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -211,8 +211,6 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kLanguageSettingsUpdate2;
 COMPONENT_EXPORT(ASH_CONSTANTS)
-extern const base::Feature kLoginDeviceManagementDisclosure;
-COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kMediaAppAnnotation;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kMediaAppDisplayExif;
@@ -368,7 +366,6 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsInstantTetheringBackgroundAdvertisingSupported();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsKerberosSettingsSectionEnabled();
-COMPONENT_EXPORT(ASH_CONSTANTS) bool IsLoginDeviceManagementDisclosureEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsMinimumChromeVersionEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsNetworkingInDiagnosticsAppEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsNewOobeLayoutEnabled();
diff --git a/ash/content/scanning/resources/scanner_select.js b/ash/content/scanning/resources/scanner_select.js
index a23f2a2..accaaf5 100644
--- a/ash/content/scanning/resources/scanner_select.js
+++ b/ash/content/scanning/resources/scanner_select.js
@@ -10,9 +10,9 @@
 import './strings.m.js';
 
 import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
-import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {afterNextRender, html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {ScannerArr} from './scanning_app_types.js';
+import {ScannerArr, ScannerInfo} from './scanning_app_types.js';
 import {alphabeticalCompare, getScannerDisplayName, tokenToString} from './scanning_app_util.js';
 
 /**
@@ -41,6 +41,12 @@
       type: String,
       notify: true,
     },
+
+    /** @type {!Map<string, !ScannerInfo>} */
+    scannerInfoMap: Object,
+
+    /** @type {string} */
+    lastUsedScannerId: String,
   },
 
   observers: ['onScannersChange_(scanners.*)'],
@@ -78,9 +84,33 @@
       });
     }
 
-    // If it exists, select the first option in the sorted array as the default.
+    // Either select the last used scanner or default to the first scanner in
+    // the dropdown.
     if (this.scanners.length > 0) {
-      this.selectedScannerId = tokenToString(this.scanners[0].id);
+      if (!this.lastUsedScannerId) {
+        this.selectedScannerId = tokenToString(this.scanners[0].id);
+        return;
+      }
+
+      this.selectedScannerId = this.lastUsedScannerId;
+
+      // After the dropdown renders with the scanner options, set the selected
+      // scanner.
+      afterNextRender(this, () => {
+        this.$$('#scannerSelect').selectedIndex =
+            this.getSelectedScannerIndex_();
+      });
     }
   },
+
+  /**
+   * @return {number}
+   * @private
+   */
+  getSelectedScannerIndex_() {
+    const selectedScannerToken =
+        this.scannerInfoMap.get(this.selectedScannerId).token;
+    return this.scanners.findIndex(
+        scanner => scanner.id === selectedScannerToken);
+  },
 });
diff --git a/ash/content/scanning/resources/scanning_app.html b/ash/content/scanning/resources/scanning_app.html
index d9703b2..70a853e3 100644
--- a/ash/content/scanning/resources/scanning_app.html
+++ b/ash/content/scanning/resources/scanning_app.html
@@ -191,7 +191,9 @@
           <template is="dom-if" if="[[!showDoneSection_]]">
             <scanner-select id="scannerSelect" scanners="[[scanners_]]"
                 disabled="[[settingsDisabled_]]"
-                selected-scanner-id="{{selectedScannerId}}"></scanner-select>
+                selected-scanner-id="{{selectedScannerId}}"
+                last-used-scanner-id="[[lastUsedScannerId_]]"
+                scanner-info-map="[[scannerInfoMap_]]"></scanner-select>
             <source-select id="sourceSelect" options="[[capabilities_.sources]]"
                 disabled="[[settingsDisabled_]]"
                 selected-option="{{selectedSource}}"></source-select>
diff --git a/ash/content/scanning/resources/scanning_app.js b/ash/content/scanning/resources/scanning_app.js
index 0241fc54..ddd05da3 100644
--- a/ash/content/scanning/resources/scanning_app.js
+++ b/ash/content/scanning/resources/scanning_app.js
@@ -284,6 +284,9 @@
 
     /** @private {?ScanSettings} */
     savedScanSettings_: Object,
+
+    /** @private {string} */
+    lastUsedScannerId_: String,
   },
 
   observers:
@@ -438,6 +441,9 @@
 
     for (const scanner of response.scanners) {
       this.setScannerInfo_(scanner);
+      if (this.isLastUsedScanner_(scanner)) {
+        this.lastUsedScannerId_ = tokenToString(scanner.id);
+      }
     }
 
     this.setAppState_(AppState.GOT_SCANNERS);
@@ -794,6 +800,17 @@
   },
 
   /**
+   * @param {!ash.scanning.mojom.Scanner} scanner
+   * @return {boolean}
+   * @private
+   */
+  isLastUsedScanner_(scanner) {
+    return this.savedScanSettings_ !== undefined &&
+        this.savedScanSettings_.lastUsedScannerName ===
+        getScannerDisplayName(scanner);
+  },
+
+  /**
    * @return {boolean}
    * @private
    */
diff --git a/ash/detachable_base/detachable_base_notification_controller_unittest.cc b/ash/detachable_base/detachable_base_notification_controller_unittest.cc
index afd8fa87..1a03a22 100644
--- a/ash/detachable_base/detachable_base_notification_controller_unittest.cc
+++ b/ash/detachable_base/detachable_base_notification_controller_unittest.cc
@@ -246,11 +246,11 @@
 TEST_F(DetachableBaseNotificationControllerTest,
        NotificationOnUpdateRequiredBeforeLogin) {
   // Update requirement detected before login - expect the update required
-  // notification to be shown.
+  // notification to be hidden.
   detachable_base_handler()->BaseFirmwareUpdateNeeded();
-  EXPECT_TRUE(IsBaseRequiresUpdateNotificationVisible());
+  EXPECT_FALSE(IsBaseRequiresUpdateNotificationVisible());
 
-  // Login, expect the notification to still be there.
+  // Login, expect the notification to be shown.
   CreateUserSessions(1);
   EXPECT_TRUE(IsBaseRequiresUpdateNotificationVisible());
 
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index d0a7ff2..b9f7b0b0 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -774,8 +774,6 @@
 
 void LockContentsView::ShowEnterpriseDomainManager(
     const std::string& entreprise_domain_manager) {
-  if (!chromeos::features::IsLoginDeviceManagementDisclosureEnabled())
-    return;
   bottom_status_indicator_->SetText(l10n_util::GetStringFUTF16(
       IDS_ASH_LOGIN_MANAGED_DEVICE_INDICATOR, ui::GetChromeOSDeviceName(),
       base::UTF8ToUTF16(entreprise_domain_manager)));
diff --git a/ash/login/ui/lock_contents_view_unittest.cc b/ash/login/ui/lock_contents_view_unittest.cc
index 0a70f03..d1a4d14d 100644
--- a/ash/login/ui/lock_contents_view_unittest.cc
+++ b/ash/login/ui/lock_contents_view_unittest.cc
@@ -820,22 +820,8 @@
   EXPECT_TRUE(test_api.bottom_status_indicator()->GetVisible());
 }
 
-class LockContentsViewUnitTestWithDeviceDisclosureEnabled
-    : public LockContentsViewUnitTest {
- public:
-  LockContentsViewUnitTestWithDeviceDisclosureEnabled()
-      : LockContentsViewUnitTest() {
-    feature_list_.InitWithFeatures(
-        {chromeos::features::kLoginDeviceManagementDisclosure}, {});
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
 // Show bottom status indicator if device is enrolled
-TEST_F(LockContentsViewUnitTestWithDeviceDisclosureEnabled,
-       ShowStatusIndicatorIfEnrolledDevice) {
+TEST_F(LockContentsViewUnitTest, ShowStatusIndicatorIfEnrolledDevice) {
   // If the device is enrolled, bottom_status_indicator should be visible.
   Shell::Get()->system_tray_model()->SetEnterpriseDomainInfo("BestCompanyEver",
                                                              false);
@@ -860,8 +846,7 @@
 }
 
 // Show bottom status indicator if device is enrolled
-TEST_F(LockContentsViewUnitTestWithDeviceDisclosureEnabled,
-       ShowManagementBubbleOnClickIfEnrolledDevice) {
+TEST_F(LockContentsViewUnitTest, ShowManagementBubbleOnClickIfEnrolledDevice) {
   // If the device is enrolled, bottom_status_indicator should be visible.
   Shell::Get()->system_tray_model()->SetEnterpriseDomainInfo("BestCompanyEver",
                                                              false);
@@ -896,8 +881,7 @@
 
 // Do not show the management bubble on click if ADB sideloading is enabled and
 // device is enrolled.
-TEST_F(LockContentsViewUnitTestWithDeviceDisclosureEnabled,
-       DoNotShowManagementBubbleOnClickIfAdb) {
+TEST_F(LockContentsViewUnitTest, DoNotShowManagementBubbleOnClickIfAdb) {
   // If the device is enrolled, bottom_status_indicator should be visible.
   Shell::Get()->system_tray_model()->SetEnterpriseDomainInfo("BestCompanyEver",
                                                              false);
diff --git a/ash/public/cpp/holding_space/holding_space_item.cc b/ash/public/cpp/holding_space/holding_space_item.cc
index ff0a74347..bcd500e 100644
--- a/ash/public/cpp/holding_space/holding_space_item.cc
+++ b/ash/public/cpp/holding_space/holding_space_item.cc
@@ -134,12 +134,12 @@
   return deletion_callback_list_.Add(std::move(callback));
 }
 
-bool HoldingSpaceItem::IsFinalized() const {
+bool HoldingSpaceItem::IsInitialized() const {
   return !file_system_url_.is_empty();
 }
 
-void HoldingSpaceItem::Finalize(const GURL& file_system_url) {
-  DCHECK(!IsFinalized());
+void HoldingSpaceItem::Initialize(const GURL& file_system_url) {
+  DCHECK(!IsInitialized());
   DCHECK(!file_system_url.is_empty());
   file_system_url_ = file_system_url;
 }
diff --git a/ash/public/cpp/holding_space/holding_space_item.h b/ash/public/cpp/holding_space/holding_space_item.h
index 4d93affe..6b55dbf3 100644
--- a/ash/public/cpp/holding_space/holding_space_item.h
+++ b/ash/public/cpp/holding_space/holding_space_item.h
@@ -63,7 +63,7 @@
 
   // Deserializes from `base::DictionaryValue` to `HoldingSpaceItem`.
   // This creates a partially initialized item with an empty file system URL.
-  // The item should be finalized using `Finalize()`.
+  // The item should be fully initialized using `Initialize()`.
   static std::unique_ptr<HoldingSpaceItem> Deserialize(
       const base::DictionaryValue& dict,
       ImageResolver image_resolver);
@@ -81,14 +81,14 @@
   base::CallbackListSubscription AddDeletionCallback(
       base::RepeatingClosureList::CallbackType callback) const;
 
-  // Indicates whether the item has been finalized. This will be false for items
-  // created using `Deserialize()` for which `Finalize()` has not yet been
-  // called.
-  // Non-finalized items should not be shown in the holding space UI.
-  bool IsFinalized() const;
+  // Indicates whether the item has been initialized. This will be false for
+  // items created using `Deserialize()` for which `Initialize()` has not yet
+  // been called. Non-initialized items should not be shown in holding space UI.
+  bool IsInitialized() const;
 
-  // Used to finalize partially initialized items created by `Deserialize()`.
-  void Finalize(const GURL& file_system_url);
+  // Used to fully initialize partially initialized items created by
+  // `Deserialize()`.
+  void Initialize(const GURL& file_system_url);
 
   // Updates the file backing the item to `file_path` and `file_system_url`.
   void UpdateBackingFile(const base::FilePath& file_path,
diff --git a/ash/public/cpp/holding_space/holding_space_item_unittest.cc b/ash/public/cpp/holding_space/holding_space_item_unittest.cc
index e2fd4a9..c6be05e 100644
--- a/ash/public/cpp/holding_space/holding_space_item_unittest.cc
+++ b/ash/public/cpp/holding_space/holding_space_item_unittest.cc
@@ -52,11 +52,11 @@
       serialized_holding_space_item,
       /*image_resolver=*/base::BindOnce(&CreateFakeHoldingSpaceImage));
 
-  EXPECT_FALSE(deserialized_holding_space_item->IsFinalized());
+  EXPECT_FALSE(deserialized_holding_space_item->IsInitialized());
   EXPECT_TRUE(deserialized_holding_space_item->file_system_url().is_empty());
 
-  deserialized_holding_space_item->Finalize(file_system_url);
-  EXPECT_TRUE(deserialized_holding_space_item->IsFinalized());
+  deserialized_holding_space_item->Initialize(file_system_url);
+  EXPECT_TRUE(deserialized_holding_space_item->IsInitialized());
   EXPECT_EQ(*deserialized_holding_space_item, *holding_space_item);
 }
 
diff --git a/ash/public/cpp/holding_space/holding_space_model.cc b/ash/public/cpp/holding_space/holding_space_model.cc
index 3587561..7bb22d5 100644
--- a/ash/public/cpp/holding_space/holding_space_model.cc
+++ b/ash/public/cpp/holding_space/holding_space_model.cc
@@ -28,8 +28,8 @@
   for (std::unique_ptr<HoldingSpaceItem>& item : items) {
     DCHECK(!GetItem(item->id()));
 
-    if (item->IsFinalized())
-      ++finalized_item_counts_by_type_[item->type()];
+    if (item->IsInitialized())
+      ++initialized_item_counts_by_type_[item->type()];
 
     item_ptrs.push_back(item.get());
     items_.push_back(std::move(item));
@@ -50,8 +50,8 @@
       std::cref(item_ids)));
 }
 
-void HoldingSpaceModel::FinalizeOrRemoveItem(const std::string& id,
-                                             const GURL& file_system_url) {
+void HoldingSpaceModel::InitializeOrRemoveItem(const std::string& id,
+                                               const GURL& file_system_url) {
   if (file_system_url.is_empty()) {
     RemoveItem(id);
     return;
@@ -65,13 +65,13 @@
   DCHECK(item_it != items_.end());
 
   HoldingSpaceItem* item = item_it->get();
-  DCHECK(!item->IsFinalized());
+  DCHECK(!item->IsInitialized());
 
-  item->Finalize(file_system_url);
-  ++finalized_item_counts_by_type_[item->type()];
+  item->Initialize(file_system_url);
+  ++initialized_item_counts_by_type_[item->type()];
 
   for (auto& observer : observers_)
-    observer.OnHoldingSpaceItemFinalized(item);
+    observer.OnHoldingSpaceItemInitialized(item);
 }
 
 void HoldingSpaceModel::UpdateBackingFileForItem(
@@ -86,7 +86,7 @@
   DCHECK(item_it != items_.end());
 
   HoldingSpaceItem* item = item_it->get();
-  DCHECK(item->IsFinalized());
+  DCHECK(item->IsInitialized());
 
   item->UpdateBackingFile(file_path, file_system_url);
 
@@ -106,8 +106,8 @@
       items.push_back(std::move(item));
       items_.erase(items_.begin() + i);
 
-      if (item_ptrs.back()->IsFinalized())
-        --finalized_item_counts_by_type_[item_ptrs.back()->type()];
+      if (item_ptrs.back()->IsInitialized())
+        --initialized_item_counts_by_type_[item_ptrs.back()->type()];
     }
   }
 
@@ -132,7 +132,7 @@
   ItemList items;
   items.swap(items_);
 
-  finalized_item_counts_by_type_.clear();
+  initialized_item_counts_by_type_.clear();
 
   std::vector<const HoldingSpaceItem*> item_ptrs;
   for (auto& item : items)
@@ -174,10 +174,10 @@
   return GetItem(type, file_path) != nullptr;
 }
 
-bool HoldingSpaceModel::ContainsFinalizedItemOfType(
+bool HoldingSpaceModel::ContainsInitializedItemOfType(
     HoldingSpaceItem::Type type) const {
-  auto it = finalized_item_counts_by_type_.find(type);
-  return it != finalized_item_counts_by_type_.end() && it->second > 0u;
+  auto it = initialized_item_counts_by_type_.find(type);
+  return it != initialized_item_counts_by_type_.end() && it->second > 0u;
 }
 
 void HoldingSpaceModel::AddObserver(HoldingSpaceModelObserver* observer) {
diff --git a/ash/public/cpp/holding_space/holding_space_model.h b/ash/public/cpp/holding_space/holding_space_model.h
index 10ec923fa..41066469 100644
--- a/ash/public/cpp/holding_space/holding_space_model.h
+++ b/ash/public/cpp/holding_space/holding_space_model.h
@@ -53,9 +53,11 @@
   // Removes multiple holding space items from the model.
   void RemoveItems(const std::set<std::string>& ids);
 
-  // Finalizes a partially initialized holding space item using the provided
-  // file system URL. The item will be removed if the file system url is empty.
-  void FinalizeOrRemoveItem(const std::string& id, const GURL& file_system_url);
+  // Fully initializes a partially initialized holding space item using the
+  // provided `file_system_url`. The item will be removed if `file_system_url`
+  // is empty.
+  void InitializeOrRemoveItem(const std::string& id,
+                              const GURL& file_system_url);
 
   // Updates the backing file for a single holding space item to the specified
   // `file_path` and `file_system_url`.
@@ -90,9 +92,9 @@
   bool ContainsItem(HoldingSpaceItem::Type type,
                     const base::FilePath& file_path) const;
 
-  // Returns true if the model contains any finalized items of the specified
-  // `type`, false otherwise.
-  bool ContainsFinalizedItemOfType(HoldingSpaceItem::Type type) const;
+  // Returns `true` if the model contains any initialized items of the specified
+  // `type`, `false` otherwise.
+  bool ContainsInitializedItemOfType(HoldingSpaceItem::Type type) const;
 
   const ItemList& items() const { return items_; }
 
@@ -104,10 +106,10 @@
   // the model.
   ItemList items_;
 
-  // Caches the count of finalized items in the model for each holding space
-  // item type. Used to quickly look up whether the model contains any finalized
-  // items of a given type.
-  std::map<HoldingSpaceItem::Type, size_t> finalized_item_counts_by_type_;
+  // Caches the count of initialized items in the model for each holding space
+  // item type. Used to quickly look up whether the model contains any
+  // initialized items of a given type.
+  std::map<HoldingSpaceItem::Type, size_t> initialized_item_counts_by_type_;
 
   base::ObserverList<HoldingSpaceModelObserver> observers_;
 };
diff --git a/ash/public/cpp/holding_space/holding_space_model_observer.h b/ash/public/cpp/holding_space/holding_space_model_observer.h
index a4fc99e..24015be 100644
--- a/ash/public/cpp/holding_space/holding_space_model_observer.h
+++ b/ash/public/cpp/holding_space/holding_space_model_observer.h
@@ -28,8 +28,9 @@
   // Called when an `item` gets updated within the holding space model.
   virtual void OnHoldingSpaceItemUpdated(const HoldingSpaceItem* item) {}
 
-  // Called when a partially initialized holding space `item` gets finalized.
-  virtual void OnHoldingSpaceItemFinalized(const HoldingSpaceItem* item) {}
+  // Called when a partially initialized holding space `item` gets fully
+  // initialized.
+  virtual void OnHoldingSpaceItemInitialized(const HoldingSpaceItem* item) {}
 };
 
 }  // namespace ash
diff --git a/ash/shelf/hotseat_widget_unittest.cc b/ash/shelf/hotseat_widget_unittest.cc
index 4e5e9f6..011df6d 100644
--- a/ash/shelf/hotseat_widget_unittest.cc
+++ b/ash/shelf/hotseat_widget_unittest.cc
@@ -31,7 +31,6 @@
 #include "ash/shelf/test/hotseat_state_watcher.h"
 #include "ash/shelf/test/shelf_layout_manager_test_base.h"
 #include "ash/shelf/test/widget_animation_smoothness_inspector.h"
-#include "ash/shelf/test/widget_animation_waiter.h"
 #include "ash/shell.h"
 #include "ash/system/ime_menu/ime_menu_tray.h"
 #include "ash/system/overview/overview_button_tray.h"
@@ -52,6 +51,7 @@
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/events/gesture_detection/gesture_configuration.h"
 #include "ui/events/test/event_generator.h"
+#include "ui/views/test/widget_animation_waiter.h"
 #include "ui/wm/core/window_util.h"
 
 namespace ash {
@@ -2525,7 +2525,7 @@
 TEST_P(HotseatWidgetTest, DISABLED_OverviewToHomeAnimationAndBackIsSmooth) {
   // Go into tablet mode and make sure animations are over.
   HotseatWidget* hotseat = GetPrimaryShelf()->hotseat_widget();
-  WidgetAnimationWaiter waiter(hotseat);
+  views::WidgetAnimationWaiter waiter(hotseat);
   TabletModeControllerTestApi().EnterTabletMode();
   waiter.WaitForAnimation();
 
@@ -2535,14 +2535,14 @@
   // Go into overview and back to know what to expect in terms of bounds.
   const gfx::Rect shown_hotseat_bounds = hotseat->GetWindowBoundsInScreen();
   {
-    WidgetAnimationWaiter waiter(hotseat);
+    views::WidgetAnimationWaiter waiter(hotseat);
     StartOverview();
     waiter.WaitForAnimation();
   }
 
   const gfx::Rect extended_hotseat_bounds = hotseat->GetWindowBoundsInScreen();
   {
-    WidgetAnimationWaiter waiter(hotseat);
+    views::WidgetAnimationWaiter waiter(hotseat);
     EndOverview();
     waiter.WaitForAnimation();
   }
@@ -2556,7 +2556,7 @@
 
   {
     WidgetAnimationSmoothnessInspector inspector(hotseat);
-    WidgetAnimationWaiter waiter(hotseat);
+    views::WidgetAnimationWaiter waiter(hotseat);
     StartOverview();
     waiter.WaitForAnimation();
     EXPECT_TRUE(inspector.CheckAnimation(4));
@@ -2567,7 +2567,7 @@
 
   {
     WidgetAnimationSmoothnessInspector inspector(hotseat);
-    WidgetAnimationWaiter waiter(hotseat);
+    views::WidgetAnimationWaiter waiter(hotseat);
     EndOverview();
     waiter.WaitForAnimation();
     EXPECT_TRUE(inspector.CheckAnimation(4));
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 0cc5b725..ebdf832 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -53,7 +53,6 @@
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shelf/test/hotseat_state_watcher.h"
 #include "ash/shelf/test/shelf_layout_manager_test_base.h"
-#include "ash/shelf/test/widget_animation_waiter.h"
 #include "ash/shell.h"
 #include "ash/system/ime_menu/ime_menu_tray.h"
 #include "ash/system/status_area_widget.h"
@@ -97,6 +96,7 @@
 #include "ui/events/test/event_generator.h"
 #include "ui/events/types/event_type.h"
 #include "ui/views/animation/bounds_animator.h"
+#include "ui/views/test/widget_animation_waiter.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/coordinate_conversion.h"
@@ -1820,7 +1820,7 @@
         GetShelfWidget()->GetWindowBoundsInScreen();
     gfx::Point start(shelf_bounds_in_screen.CenterPoint());
     gfx::Point end(start.x(), shelf_bounds_in_screen.bottom());
-    WidgetAnimationWaiter waiter(GetShelfWidget(), visible_bounds);
+    views::WidgetAnimationWaiter waiter(GetShelfWidget(), visible_bounds);
     generator->GestureScrollSequence(start, end,
                                      base::TimeDelta::FromMilliseconds(10), 5);
     EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
@@ -1859,7 +1859,7 @@
     gfx::Point end(start.x(), start.y() - 100);
     ui::test::EventGenerator* generator = GetEventGenerator();
 
-    WidgetAnimationWaiter waiter(GetShelfWidget(), visible_bounds);
+    views::WidgetAnimationWaiter waiter(GetShelfWidget(), visible_bounds);
     generator->GestureScrollSequence(start, end,
                                      base::TimeDelta::FromMilliseconds(10), 1);
     EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
@@ -1891,7 +1891,7 @@
     ui::test::EventGenerator* generator = GetEventGenerator();
 
     // Show the shelf first.
-    WidgetAnimationWaiter waiter1(GetShelfWidget(), visible_bounds);
+    views::WidgetAnimationWaiter waiter1(GetShelfWidget(), visible_bounds);
     SwipeUpOnShelf();
     waiter1.WaitForAnimation();
     EXPECT_TRUE(waiter1.WasValidAnimation());
@@ -1901,7 +1901,7 @@
     gfx::Point start =
         GetShelfWidget()->GetWindowBoundsInScreen().CenterPoint();
     gfx::Point end = gfx::Point(start.x(), start.y() + 100);
-    WidgetAnimationWaiter waiter2(GetShelfWidget(), auto_hidden_bounds);
+    views::WidgetAnimationWaiter waiter2(GetShelfWidget(), auto_hidden_bounds);
     generator->GestureScrollSequence(start, end,
                                      base::TimeDelta::FromMilliseconds(10), 1);
     EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
@@ -3898,7 +3898,7 @@
   }
 
   // Activate a window and wait for the navigation widget animation to finish.
-  WidgetAnimationWaiter waiter(shelf->navigation_widget());
+  views::WidgetAnimationWaiter waiter(shelf->navigation_widget());
   std::unique_ptr<aura::Window> window =
       AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400));
   wm::ActivateWindow(window.get());
diff --git a/ash/system/holding_space/holding_space_item_views_section.cc b/ash/system/holding_space/holding_space_item_views_section.cc
index 9015dbf..ffccd38 100644
--- a/ash/system/holding_space/holding_space_item_views_section.cc
+++ b/ash/system/holding_space/holding_space_item_views_section.cc
@@ -279,7 +279,7 @@
     const std::vector<const HoldingSpaceItem*>& items) {
   const bool needs_update = std::any_of(
       items.begin(), items.end(), [this](const HoldingSpaceItem* item) {
-        return item->IsFinalized() &&
+        return item->IsInitialized() &&
                base::Contains(supported_types_, item->type());
       });
   if (needs_update)
@@ -296,7 +296,7 @@
     MaybeAnimateOut();
 }
 
-void HoldingSpaceItemViewsSection::OnHoldingSpaceItemFinalized(
+void HoldingSpaceItemViewsSection::OnHoldingSpaceItemInitialized(
     const HoldingSpaceItem* item) {
   if (base::Contains(supported_types_, item->type()))
     MaybeAnimateOut();
@@ -407,7 +407,7 @@
       disable_animations ? base::TimeDelta() : kAnimationDuration;
 
   // If this section does not have a `placeholder_` and the model does not
-  // contain any associated and finalized items, then this section is becoming
+  // contain any associated and initialized items, then this section is becoming
   // invisible to the user and the `header_` needs to be animated out alongside
   // any content.
   bool animate_out_header = !placeholder_;
@@ -417,7 +417,7 @@
       animate_out_header = std::none_of(
           supported_types_.begin(), supported_types_.end(),
           [&model](HoldingSpaceItem::Type supported_type) {
-            return model->ContainsFinalizedItemOfType(supported_type);
+            return model->ContainsInitializedItemOfType(supported_type);
           });
     }
   }
@@ -489,7 +489,8 @@
     return;
 
   for (const auto& item : model->items()) {
-    if (item->IsFinalized() && base::Contains(supported_types_, item->type())) {
+    if (item->IsInitialized() &&
+        base::Contains(supported_types_, item->type())) {
       DCHECK(!base::Contains(views_by_item_id_, item->id()));
 
       // Remove the last holding space item view if already at max capacity.
diff --git a/ash/system/holding_space/holding_space_item_views_section.h b/ash/system/holding_space/holding_space_item_views_section.h
index a28965a..ffdde41 100644
--- a/ash/system/holding_space/holding_space_item_views_section.h
+++ b/ash/system/holding_space/holding_space_item_views_section.h
@@ -63,7 +63,7 @@
   // view if, for example, its parent is animating out.
   void OnHoldingSpaceItemsAdded(const std::vector<const HoldingSpaceItem*>&);
   void OnHoldingSpaceItemsRemoved(const std::vector<const HoldingSpaceItem*>&);
-  void OnHoldingSpaceItemFinalized(const HoldingSpaceItem* item);
+  void OnHoldingSpaceItemInitialized(const HoldingSpaceItem* item);
 
   // Removes all holding space item views from this section. This method is
   // expected to only be called:
@@ -73,7 +73,7 @@
   void RemoveAllHoldingSpaceItemViews();
 
   // Returns whether this section has a placeholder to show in lieu of item
-  // views when the model contains no finalized items of supported types.
+  // views when the model contains no initialized items of supported types.
   bool has_placeholder() const { return !!placeholder_; }
 
   // Returns the types of holding space items supported by this section.
diff --git a/ash/system/holding_space/holding_space_tray.cc b/ash/system/holding_space/holding_space_tray.cc
index 26d17e9..ac81edd 100644
--- a/ash/system/holding_space/holding_space_tray.cc
+++ b/ash/system/holding_space/holding_space_tray.cc
@@ -151,10 +151,10 @@
   return prefs && holding_space_prefs::IsPreviewsEnabled(prefs);
 }
 
-// Returns whether the holding space model contains any finalized items.
-bool ModelContainsFinalizedItems(HoldingSpaceModel* model) {
+// Returns whether the holding space model contains any initialized items.
+bool ModelContainsInitializedItems(HoldingSpaceModel* model) {
   for (const auto& item : model->items()) {
-    if (item->IsFinalized())
+    if (item->IsInitialized())
       return true;
   }
   return false;
@@ -508,9 +508,9 @@
   // the holding space tray will continue to be visible until the user has
   // pinned their first file. After the user has pinned their first file, the
   // holding space tray will only be visible in the shelf if their holding space
-  // contains finalized items.
+  // contains initialized items.
   SetVisiblePreferred((has_ever_added_item && !has_ever_pinned_item) ||
-                      ModelContainsFinalizedItems(model));
+                      ModelContainsInitializedItems(model));
 }
 
 std::u16string HoldingSpaceTray::GetAccessibleNameForBubble() {
@@ -546,14 +546,14 @@
 
 void HoldingSpaceTray::OnHoldingSpaceItemsAdded(
     const std::vector<const HoldingSpaceItem*>& items) {
-  // If a finalized holding space item is added to the model mid-session, the
+  // If an initialized holding space item is added to the model mid-session, the
   // holding space tray should bounce in (if it isn't already visible) and
   // previews should be animated.
   if (!Shell::Get()->session_controller()->IsUserSessionBlocked()) {
-    const bool has_finalized_item = std::any_of(
+    const bool has_initialized_item = std::any_of(
         items.begin(), items.end(),
-        [](const HoldingSpaceItem* item) { return item->IsFinalized(); });
-    if (has_finalized_item)
+        [](const HoldingSpaceItem* item) { return item->IsInitialized(); });
+    if (has_initialized_item)
       SetShouldAnimate(true);
   }
 
@@ -563,13 +563,13 @@
 
 void HoldingSpaceTray::OnHoldingSpaceItemsRemoved(
     const std::vector<const HoldingSpaceItem*>& items) {
-  // If a finalized holding space item is removed from the model mid-session,
+  // If an initialized holding space item is removed from the model mid-session,
   // the holding space tray should animate updates.
   if (!Shell::Get()->session_controller()->IsUserSessionBlocked()) {
-    const bool has_finalized_item = std::any_of(
+    const bool has_initialized_item = std::any_of(
         items.begin(), items.end(),
-        [](const HoldingSpaceItem* item) { return item->IsFinalized(); });
-    if (has_finalized_item)
+        [](const HoldingSpaceItem* item) { return item->IsInitialized(); });
+    if (has_initialized_item)
       SetShouldAnimate(true);
   }
 
@@ -577,7 +577,7 @@
   UpdatePreviewsState();
 }
 
-void HoldingSpaceTray::OnHoldingSpaceItemFinalized(
+void HoldingSpaceTray::OnHoldingSpaceItemInitialized(
     const HoldingSpaceItem* item) {
   UpdateVisibility();
   UpdatePreviewsState();
@@ -711,7 +711,7 @@
 void HoldingSpaceTray::UpdatePreviewsVisibility() {
   const bool show_previews =
       IsPreviewsEnabled() && HoldingSpaceController::Get()->model() &&
-      ModelContainsFinalizedItems(HoldingSpaceController::Get()->model());
+      ModelContainsInitializedItems(HoldingSpaceController::Get()->model());
 
   if (PreviewsShown() == show_previews)
     return;
@@ -749,7 +749,7 @@
   std::set<base::FilePath> paths_with_previews;
   for (const auto& item :
        base::Reversed(HoldingSpaceController::Get()->model()->items())) {
-    if (!item->IsFinalized())
+    if (!item->IsInitialized())
       continue;
     if (base::Contains(paths_with_previews, item->file_path()))
       continue;
diff --git a/ash/system/holding_space/holding_space_tray.h b/ash/system/holding_space/holding_space_tray.h
index 57ac1a3..6f73d8dc 100644
--- a/ash/system/holding_space/holding_space_tray.h
+++ b/ash/system/holding_space/holding_space_tray.h
@@ -112,7 +112,7 @@
       const std::vector<const HoldingSpaceItem*>& items) override;
   void OnHoldingSpaceItemsRemoved(
       const std::vector<const HoldingSpaceItem*>& items) override;
-  void OnHoldingSpaceItemFinalized(const HoldingSpaceItem* item) override;
+  void OnHoldingSpaceItemInitialized(const HoldingSpaceItem* item) override;
 
   // SessionObserver:
   void OnActiveUserPrefServiceChanged(PrefService* prefs) override;
diff --git a/ash/system/holding_space/holding_space_tray_child_bubble.cc b/ash/system/holding_space/holding_space_tray_child_bubble.cc
index 7e5e110..0dbed19 100644
--- a/ash/system/holding_space/holding_space_tray_child_bubble.cc
+++ b/ash/system/holding_space/holding_space_tray_child_bubble.cc
@@ -45,16 +45,17 @@
       base::Passed(std::move(callback)));
 }
 
-// Returns whether the given holding space `model` contains any finalized items
-// which are supported by the specified holding space item views `section`.
-bool ModelContainsFinalizedItemsForSection(
+// Returns whether the given holding space `model` contains any initialized
+// items which are supported by the specified holding space item views
+// `section`.
+bool ModelContainsInitializedItemsForSection(
     const HoldingSpaceModel* model,
     const HoldingSpaceItemViewsSection* section) {
   const auto& supported_types = section->supported_types();
   return std::any_of(
       supported_types.begin(), supported_types.end(),
       [&model](HoldingSpaceItem::Type supported_type) {
-        return model->ContainsFinalizedItemOfType(supported_type);
+        return model->ContainsInitializedItemOfType(supported_type);
       });
 }
 
@@ -201,14 +202,14 @@
   DCHECK(model);
 
   // This child bubble should animate out if the attached model does not
-  // contain finalized items supported by any of its sections. The exception is
-  // if a section has a placeholder to show in lieu of holding space items. If
-  // a placeholder exists, the child bubble should persist.
+  // contain initialized items supported by any of its sections. The exception
+  // is if a section has a placeholder to show in lieu of holding space items.
+  // If a placeholder exists, the child bubble should persist.
   const bool animate_out = std::none_of(
       sections_.begin(), sections_.end(),
       [&model](const HoldingSpaceItemViewsSection* section) {
         return section->has_placeholder() ||
-               ModelContainsFinalizedItemsForSection(model, section);
+               ModelContainsInitializedItemsForSection(model, section);
       });
 
   if (animate_out) {
@@ -220,14 +221,14 @@
     section->OnHoldingSpaceItemsRemoved(items);
 }
 
-void HoldingSpaceTrayChildBubble::OnHoldingSpaceItemFinalized(
+void HoldingSpaceTrayChildBubble::OnHoldingSpaceItemInitialized(
     const HoldingSpaceItem* item) {
-  // Ignore item finalization while the bubble is animating out. The bubble
+  // Ignore item initialized while the bubble is animating out. The bubble
   // content will be updated to match the model after the out animation
   // completes.
   if (!is_animating_out_) {
     for (HoldingSpaceItemViewsSection* section : sections_)
-      section->OnHoldingSpaceItemFinalized(item);
+      section->OnHoldingSpaceItemInitialized(item);
   }
 }
 
@@ -395,7 +396,7 @@
     item_ptrs.push_back(item.get());
 
   // Populating a `section` may cause it's visibility to change if the `model`
-  // contains finalized items of types which it supports. This, in turn, will
+  // contains initialized items of types which it supports. This, in turn, will
   // cause visibility of this child bubble to update and animate in if needed.
   for (HoldingSpaceItemViewsSection* section : sections_)
     section->OnHoldingSpaceItemsAdded(item_ptrs);
diff --git a/ash/system/holding_space/holding_space_tray_child_bubble.h b/ash/system/holding_space/holding_space_tray_child_bubble.h
index e7dff4271..d7bd2529 100644
--- a/ash/system/holding_space/holding_space_tray_child_bubble.h
+++ b/ash/system/holding_space/holding_space_tray_child_bubble.h
@@ -58,7 +58,7 @@
       const std::vector<const HoldingSpaceItem*>& items) override;
   void OnHoldingSpaceItemsRemoved(
       const std::vector<const HoldingSpaceItem*>& items) override;
-  void OnHoldingSpaceItemFinalized(const HoldingSpaceItem* item) override;
+  void OnHoldingSpaceItemInitialized(const HoldingSpaceItem* item) override;
 
  protected:
   // Invoked to create the `sections_` for this child bubble.
diff --git a/ash/system/holding_space/holding_space_tray_icon.cc b/ash/system/holding_space/holding_space_tray_icon.cc
index ab9fc21..c18332a 100644
--- a/ash/system/holding_space/holding_space_tray_icon.cc
+++ b/ash/system/holding_space/holding_space_tray_icon.cc
@@ -264,7 +264,7 @@
   std::set<std::string> item_ids;
   for (size_t index = 0; index < items.size(); ++index) {
     const HoldingSpaceItem* item = items[index];
-    DCHECK(item->IsFinalized());
+    DCHECK(item->IsInitialized());
 
     item_ids.insert(item->id());
     item_ids_.push_back(item->id());
diff --git a/ash/system/holding_space/holding_space_tray_unittest.cc b/ash/system/holding_space/holding_space_tray_unittest.cc
index 4c10fbb..3d00ca96 100644
--- a/ash/system/holding_space/holding_space_tray_unittest.cc
+++ b/ash/system/holding_space/holding_space_tray_unittest.cc
@@ -636,15 +636,15 @@
   GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
   EXPECT_FALSE(test_api()->IsShowingInShelf());
 
-  // Finalize one item, and verify the tray button gets shown.
-  model()->FinalizeOrRemoveItem(item_2->id(), GURL("filesystem:fake_2"));
+  // Initialize one item, and verify the tray button gets shown.
+  model()->InitializeOrRemoveItem(item_2->id(), GURL("filesystem:fake_2"));
 
   GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
   EXPECT_TRUE(test_api()->IsShowingInShelf());
   EXPECT_FALSE(IsViewVisible(test_api()->GetDefaultTrayIcon()));
   EXPECT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
 
-  // Remove the finalized item - the shelf button should get hidden.
+  // Remove the initialized item - the shelf button should get hidden.
   model()->RemoveItem(item_2->id());
   GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
   EXPECT_FALSE(test_api()->IsShowingInShelf());
@@ -883,7 +883,7 @@
                       HoldingSpaceItem::Type::kNearbyShare));
 
 // Tests how download chips are updated during item addition, removal and
-// finalization.
+// initialization.
 TEST_P(HoldingSpaceTrayDownloadsSectionTest, DownloadsSection) {
   StartSession();
 
@@ -927,9 +927,9 @@
   EXPECT_EQ(item_1->id(),
             HoldingSpaceItemView::Cast(download_chips[1])->item()->id());
 
-  // Finalize partially initialized item, and verify it gets added to the
-  // section, in the order of addition, replacing the oldest item.
-  model()->FinalizeOrRemoveItem(item_2->id(), GURL("filesystem:fake_2"));
+  // Fully initialize partially initialized item, and verify it gets added to
+  // the section, in the order of addition, replacing the oldest item.
+  model()->InitializeOrRemoveItem(item_2->id(), GURL("filesystem:fake_2"));
 
   EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
   EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
@@ -971,13 +971,13 @@
 }
 
 // Verifies the downloads section is shown and orders items as expected when the
-// model contains a number of finalized items prior to showing UI.
+// model contains a number of initialized items prior to showing UI.
 TEST_P(HoldingSpaceTrayDownloadsSectionTest,
-       DownloadsSectionWithFinalizedItemsOnly) {
+       DownloadsSectionWithInitializedItemsOnly) {
   MarkTimeOfFirstPin();
   StartSession();
 
-  // Add a number of finalized download items.
+  // Add a number of initialized download items.
   std::deque<HoldingSpaceItem*> items;
   for (size_t i = 0; i < kMaxDownloads; ++i) {
     items.push_back(AddItem(
@@ -1003,7 +1003,7 @@
 }
 
 TEST_P(HoldingSpaceTrayDownloadsSectionTest,
-       FinalizingDownloadItemThatShouldBeInvisible) {
+       InitializingDownloadItemThatShouldBeInvisible) {
   StartSession();
   test_api()->Show();
 
@@ -1026,9 +1026,9 @@
   EXPECT_EQ(item_2->id(),
             HoldingSpaceItemView::Cast(download_chips[1])->item()->id());
 
-  // Finalize partially initialized item, and verify it's not added to the
-  // section.
-  model()->FinalizeOrRemoveItem(item_1->id(), GURL("filesystem:fake_1"));
+  // Fully initialize partially initialized item, and verify it's not added to
+  // the section.
+  model()->InitializeOrRemoveItem(item_1->id(), GURL("filesystem:fake_1"));
 
   EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
   EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
@@ -1090,7 +1090,7 @@
 }
 
 // Tests how screen captures section is updated during item addition, removal
-// and finalization.
+// and initialization.
 TEST_F(HoldingSpaceTrayTest, ScreenCapturesSection) {
   StartSession();
   test_api()->Show();
@@ -1138,9 +1138,9 @@
   EXPECT_EQ(item_1->id(),
             HoldingSpaceItemView::Cast(screen_captures[2])->item()->id());
 
-  // Finalize partially initialized item, and verify it gets added to the
-  // section, in the order of addition, replacing the oldest item.
-  model()->FinalizeOrRemoveItem(item_2->id(), GURL("filesystem:fake_2"));
+  // Fully initialize partially initialized item, and verify it gets added to
+  // the section, in the order of addition, replacing the oldest item.
+  model()->InitializeOrRemoveItem(item_2->id(), GURL("filesystem:fake_2"));
 
   EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
   EXPECT_TRUE(test_api()->GetDownloadChips().empty());
@@ -1190,12 +1190,12 @@
 }
 
 // Verifies the screen captures section is shown and orders items as expected
-// when the model contains a number of finalized items prior to showing UI.
-TEST_F(HoldingSpaceTrayTest, ScreenCapturesSectionWithFinalizedItemsOnly) {
+// when the model contains a number of initialized items prior to showing UI.
+TEST_F(HoldingSpaceTrayTest, ScreenCapturesSectionWithInitializedItemsOnly) {
   MarkTimeOfFirstPin();
   StartSession();
 
-  // Add a number of finalized screen capture items.
+  // Add a number of initialized screen capture items.
   std::deque<HoldingSpaceItem*> items;
   for (size_t i = 0; i < kMaxScreenCaptures; ++i) {
     items.push_back(
@@ -1221,7 +1221,8 @@
   test_api()->Close();
 }
 
-TEST_F(HoldingSpaceTrayTest, FinalizingScreenCaptureItemThatShouldBeInvisible) {
+TEST_F(HoldingSpaceTrayTest,
+       InitializingScreenCaptureItemThatShouldBeInvisible) {
   StartSession();
   test_api()->Show();
 
@@ -1258,9 +1259,9 @@
   EXPECT_EQ(item_2->id(),
             HoldingSpaceItemView::Cast(screen_captures[2])->item()->id());
 
-  // Finalize partially initialized item, and verify it's not added to the
-  // section.
-  model()->FinalizeOrRemoveItem(item_1->id(), GURL("filesystem:fake_1"));
+  // Fully initialize partially initialized item, and verify it's not added to
+  // the section.
+  model()->InitializeOrRemoveItem(item_1->id(), GURL("filesystem:fake_1"));
 
   EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
   EXPECT_TRUE(test_api()->GetDownloadChips().empty());
@@ -1337,7 +1338,7 @@
 }
 
 // Tests how the pinned item section is updated during item addition, removal
-// and finalization.
+// and initialization.
 TEST_F(HoldingSpaceTrayTest, PinnedFilesSection) {
   MarkTimeOfFirstPin();
   StartSession();
@@ -1383,8 +1384,8 @@
   EXPECT_EQ(item_1->id(),
             HoldingSpaceItemView::Cast(pinned_files[1])->item()->id());
 
-  // Finalize partially initialized item, and verify it gets shown.
-  model()->FinalizeOrRemoveItem(item_2->id(), GURL("filesystem:fake_2"));
+  // Full initialize partially initialized item, and verify it gets shown.
+  model()->InitializeOrRemoveItem(item_2->id(), GURL("filesystem:fake_2"));
 
   EXPECT_TRUE(test_api()->GetDownloadChips().empty());
   EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
@@ -1473,9 +1474,9 @@
             HoldingSpaceItemView::Cast(pinned_files[0])->item()->id());
   EXPECT_TRUE(HoldingSpaceItemView::Cast(pinned_files[0])->GetVisible());
 
-  // Finalize a partially initialized item with an empty URL - it should get
-  // removed.
-  model()->FinalizeOrRemoveItem(item_2->id(), GURL());
+  // Fully initialize a partially initialized item with an empty URL - it should
+  // get removed.
+  model()->InitializeOrRemoveItem(item_2->id(), GURL());
 
   pinned_files = test_api()->GetPinnedFileChips();
   ASSERT_EQ(1u, pinned_files.size());
@@ -1484,12 +1485,12 @@
 }
 
 // Verifies the pinned items section is shown and orders items as expected when
-// the model contains a number of finalized items prior to showing UI.
-TEST_F(HoldingSpaceTrayTest, PinnedFilesSectionWithFinalizedItemsOnly) {
+// the model contains a number of initialized items prior to showing UI.
+TEST_F(HoldingSpaceTrayTest, PinnedFilesSectionWithInitializedItemsOnly) {
   MarkTimeOfFirstPin();
   StartSession();
 
-  // Add a number of finalized pinned items.
+  // Add a number of initialized pinned items.
   std::deque<HoldingSpaceItem*> items;
   for (int i = 0; i < 10; ++i) {
     items.push_back(
@@ -1548,7 +1549,7 @@
   // Add a screen recording item and verify recent files section gets shown.
   HoldingSpaceItem* item_1 = AddItem(HoldingSpaceItem::Type::kScreenRecording,
                                      base::FilePath("/tmp/fake_1"));
-  ASSERT_TRUE(item_1->IsFinalized());
+  ASSERT_TRUE(item_1->IsInitialized());
 
   EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
   EXPECT_TRUE(test_api()->RecentFilesBubbleShown());
@@ -1586,7 +1587,7 @@
 }
 
 // Tests that a partially initialized screen recording item shows in the UI in
-// the reverse order from added time rather than finalization time.
+// the reverse order from added time rather than initialization time.
 TEST_F(HoldingSpaceTrayTest,
        PartialScreenRecordingItemWithExistingScreenshotItems) {
   StartSession();
@@ -1623,9 +1624,9 @@
   EXPECT_EQ(screenshot_item_1->id(),
             HoldingSpaceItemView::Cast(screen_capture_chips[2])->item()->id());
 
-  // Finalize the screen recording item and verify it is not shown.
-  model()->FinalizeOrRemoveItem(screen_recording_item->id(),
-                                GURL("filesystem:screen_recording"));
+  // Initialize the screen recording item and verify it is not shown.
+  model()->InitializeOrRemoveItem(screen_recording_item->id(),
+                                  GURL("filesystem:screen_recording"));
 
   EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
   EXPECT_TRUE(test_api()->GetDownloadChips().empty());
@@ -1639,7 +1640,7 @@
             HoldingSpaceItemView::Cast(screen_capture_chips[2])->item()->id());
 
   // Remove one of the fully initialized items, and verify the screen recording
-  // item that was finalized late is shown.
+  // item that was initialized late is shown.
   model()->RemoveItem(screenshot_item_1->id());
 
   screen_capture_chips = test_api()->GetScreenCaptureViews();
@@ -1665,9 +1666,9 @@
   EXPECT_EQ(screen_recording_item->id(),
             HoldingSpaceItemView::Cast(screen_capture_chips[2])->item()->id());
 
-  // Finalize the screen recording item and verify it is shown first.
-  model()->FinalizeOrRemoveItem(screen_recording_item_last->id(),
-                                GURL("filesystem:screen_recording"));
+  // Initialize the screen recording item and verify it is shown first.
+  model()->InitializeOrRemoveItem(screen_recording_item_last->id(),
+                                  GURL("filesystem:screen_recording"));
 
   EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
   EXPECT_TRUE(test_api()->GetDownloadChips().empty());
@@ -1684,7 +1685,7 @@
 }
 
 // Tests that partially initialized screenshot item shows in the UI in the
-// reverse order from added time rather than finalization time.
+// reverse order from added time rather than initialization time.
 TEST_F(HoldingSpaceTrayTest,
        PartialScreenshotItemWithExistingScreenRecordingItems) {
   StartSession();
@@ -1720,9 +1721,9 @@
   EXPECT_EQ(screen_recording_item_1->id(),
             HoldingSpaceItemView::Cast(screen_capture_chips[2])->item()->id());
 
-  // Finalize the screenshot item and verify it is not shown.
-  model()->FinalizeOrRemoveItem(screenshot_item->id(),
-                                GURL("filesystem:fake_1"));
+  // Initialize the screenshot item and verify it is not shown.
+  model()->InitializeOrRemoveItem(screenshot_item->id(),
+                                  GURL("filesystem:fake_1"));
 
   EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
   EXPECT_TRUE(test_api()->GetDownloadChips().empty());
diff --git a/ash/system/message_center/session_state_notification_blocker.cc b/ash/system/message_center/session_state_notification_blocker.cc
index 86511cb..1456c86 100644
--- a/ash/system/message_center/session_state_notification_blocker.cc
+++ b/ash/system/message_center/session_state_notification_blocker.cc
@@ -7,6 +7,7 @@
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/system/message_center/ash_message_center_lock_screen_controller.h"
+#include "ash/system/power/battery_notification.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/public/cpp/notification.h"
 #include "ui/message_center/public/cpp/notification_types.h"
@@ -28,7 +29,18 @@
   SessionControllerImpl* const session_controller =
       Shell::Get()->session_controller();
 
-  return !session_controller->IsRunningInAppMode();
+  SessionState state = session_controller->GetSessionState();
+  static const SessionState kNotificationBlockedStates[] = {
+      SessionState::OOBE, SessionState::LOGIN_PRIMARY,
+      SessionState::LOGIN_SECONDARY, SessionState::LOGGED_IN_NOT_ACTIVE};
+
+  // Do not show notifications in kiosk mode or before session starts.
+  if (session_controller->IsRunningInAppMode() ||
+      base::Contains(kNotificationBlockedStates, state)) {
+    return false;
+  }
+
+  return true;
 }
 
 bool CalculateShouldShowPopup() {
@@ -86,6 +98,13 @@
     return false;
   }
 
+  // Never show notifications in kiosk mode.
+  if (Shell::Get()->session_controller()->IsRunningInAppMode())
+    return false;
+
+  if (notification.id() == BatteryNotification::kNotificationId)
+    return true;
+
   return should_show_notification_;
 }
 
diff --git a/ash/system/message_center/session_state_notification_blocker_unittest.cc b/ash/system/message_center/session_state_notification_blocker_unittest.cc
index a871595..3f4c4b79 100644
--- a/ash/system/message_center/session_state_notification_blocker_unittest.cc
+++ b/ash/system/message_center/session_state_notification_blocker_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "ash/session/test_session_controller_client.h"
+#include "ash/system/power/battery_notification.h"
 #include "ash/test/ash_test_base.h"
 #include "base/command_line.h"
 #include "base/macros.h"
@@ -58,9 +59,9 @@
 
   bool ShouldShowNotification(const message_center::NotifierId& notifier_id) {
     message_center::Notification notification(
-        message_center::NOTIFICATION_TYPE_SIMPLE, "chromeos-id",
-        u"chromeos-title", u"chromeos-message", gfx::Image(),
-        u"chromeos-source", GURL(), notifier_id,
+        message_center::NOTIFICATION_TYPE_SIMPLE,
+        GetNotificationId(notifier_id), u"chromeos-title", u"chromeos-message",
+        gfx::Image(), u"chromeos-source", GURL(), notifier_id,
         message_center::RichNotificationData(), nullptr);
     if (notifier_id.id == kNotifierSystemPriority)
       notification.set_priority(message_center::SYSTEM_PRIORITY);
@@ -85,6 +86,12 @@
   }
 
  private:
+  std::string GetNotificationId(const message_center::NotifierId& notifier_id) {
+    return notifier_id.id == kNotifierSystemPriority
+               ? BatteryNotification::kNotificationId
+               : "chromeos-id";
+  }
+
   int state_changed_count_ = 0;
   std::unique_ptr<message_center::NotificationBlocker> blocker_;
 
@@ -98,26 +105,31 @@
   message_center::NotifierId notifier_id(
       message_center::NotifierType::APPLICATION, "test-notifier");
   EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
+  EXPECT_FALSE(ShouldShowNotification(notifier_id));
 
   // Login screen.
   GetSessionControllerClient()->SetSessionState(SessionState::LOGIN_PRIMARY);
   EXPECT_EQ(0, GetStateChangedCountAndReset());
   EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
+  EXPECT_FALSE(ShouldShowNotification(notifier_id));
 
   // Logged in as a normal user.
   SimulateUserLogin("user@test.com");
-  EXPECT_EQ(0, GetStateChangedCountAndReset());
+  EXPECT_EQ(1, GetStateChangedCountAndReset());
   EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
+  EXPECT_TRUE(ShouldShowNotification(notifier_id));
 
   // Lock.
   SetLockedState(true);
   EXPECT_EQ(1, GetStateChangedCountAndReset());
   EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id));
+  EXPECT_TRUE(ShouldShowNotification(notifier_id));
 
   // Unlock.
   SetLockedState(false);
   EXPECT_EQ(1, GetStateChangedCountAndReset());
   EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
+  EXPECT_TRUE(ShouldShowNotification(notifier_id));
 }
 
 TEST_F(SessionStateNotificationBlockerTest, AlwaysAllowedNotifier) {
@@ -129,26 +141,31 @@
   GetSessionControllerClient()->SetSessionState(SessionState::OOBE);
   EXPECT_EQ(0, GetStateChangedCountAndReset());
   EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
+  EXPECT_TRUE(ShouldShowNotification(notifier_id));
 
   // Login screen.
   GetSessionControllerClient()->SetSessionState(SessionState::LOGIN_PRIMARY);
   EXPECT_EQ(0, GetStateChangedCountAndReset());
   EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
+  EXPECT_TRUE(ShouldShowNotification(notifier_id));
 
   // Logged in as a normal user.
   SimulateUserLogin("user@test.com");
-  EXPECT_EQ(0, GetStateChangedCountAndReset());
+  EXPECT_EQ(1, GetStateChangedCountAndReset());
   EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
+  EXPECT_TRUE(ShouldShowNotification(notifier_id));
 
   // Lock.
   SetLockedState(true);
   EXPECT_EQ(1, GetStateChangedCountAndReset());
   EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
+  EXPECT_TRUE(ShouldShowNotification(notifier_id));
 
   // Unlock.
   SetLockedState(false);
   EXPECT_EQ(1, GetStateChangedCountAndReset());
   EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
+  EXPECT_TRUE(ShouldShowNotification(notifier_id));
 }
 
 TEST_F(SessionStateNotificationBlockerTest, BlockOnPrefService) {
@@ -195,9 +212,11 @@
   message_center::NotifierId notifier_id(
       message_center::NotifierType::SYSTEM_COMPONENT, kNotifierSystemPriority);
   EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
+  EXPECT_TRUE(ShouldShowNotification(notifier_id));
 
   SimulateKioskMode(user_manager::USER_TYPE_KIOSK_APP);
   EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id));
+  EXPECT_FALSE(ShouldShowNotification(notifier_id));
 }
 
 TEST_F(SessionStateNotificationBlockerTest, DelayAfterLogin) {
diff --git a/ash/system/night_light/night_light_controller_unittest.cc b/ash/system/night_light/night_light_controller_unittest.cc
index fd197cb4..7afb9aa 100644
--- a/ash/system/night_light/night_light_controller_unittest.cc
+++ b/ash/system/night_light/night_light_controller_unittest.cc
@@ -215,7 +215,7 @@
     CreateTestUserSessions();
 
     // Simulate user 1 login.
-    SwitchActiveUser(kUser1Email);
+    SimulateNewUserFirstLogin(kUser1Email);
 
     // Start with ambient color pref disabled.
     SetAmbientColorPrefEnabled(false);
@@ -1717,6 +1717,7 @@
   EXPECT_TRUE(controller->GetAutoNightLightNotificationForTesting());
 }
 TEST_F(AutoNightLightTest, CannotDisableNotificationWhenSessionIsBlocked) {
+  BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
   EXPECT_TRUE(Shell::Get()->session_controller()->IsUserSessionBlocked());
 
   // Simulate reaching sunset.
diff --git a/ash/system/power/battery_notification.cc b/ash/system/power/battery_notification.cc
index 47877c7..2d2fd96 100644
--- a/ash/system/power/battery_notification.cc
+++ b/ash/system/power/battery_notification.cc
@@ -27,7 +27,6 @@
 
 namespace {
 
-const char kBatteryNotificationId[] = "battery";
 const char kNotifierBattery[] = "ash.battery";
 
 const gfx::VectorIcon& GetBatteryImageMD(
@@ -96,8 +95,9 @@
     message = message + u"\n" + time_message;
 
   std::unique_ptr<Notification> notification = ash::CreateSystemNotification(
-      message_center::NOTIFICATION_TYPE_SIMPLE, kBatteryNotificationId,
-      std::u16string(), message, std::u16string(), GURL(),
+      message_center::NOTIFICATION_TYPE_SIMPLE,
+      BatteryNotification::kNotificationId, std::u16string(), message,
+      std::u16string(), GURL(),
       message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
                                  kNotifierBattery),
       message_center::RichNotificationData(), nullptr,
@@ -113,6 +113,9 @@
 
 }  // namespace
 
+// static
+const char BatteryNotification::kNotificationId[] = "battery";
+
 BatteryNotification::BatteryNotification(
     MessageCenter* message_center,
     PowerNotificationController::NotificationState notification_state)
@@ -121,14 +124,14 @@
 }
 
 BatteryNotification::~BatteryNotification() {
-  if (message_center_->FindVisibleNotificationById(kBatteryNotificationId))
-    message_center_->RemoveNotification(kBatteryNotificationId, false);
+  if (message_center_->FindVisibleNotificationById(kNotificationId))
+    message_center_->RemoveNotification(kNotificationId, false);
 }
 
 void BatteryNotification::Update(
     PowerNotificationController::NotificationState notification_state) {
-  if (message_center_->FindVisibleNotificationById(kBatteryNotificationId)) {
-    message_center_->UpdateNotification(kBatteryNotificationId,
+  if (message_center_->FindVisibleNotificationById(kNotificationId)) {
+    message_center_->UpdateNotification(kNotificationId,
                                         CreateNotification(notification_state));
   }
 }
diff --git a/ash/system/power/battery_notification.h b/ash/system/power/battery_notification.h
index b399e09..53f271f 100644
--- a/ash/system/power/battery_notification.h
+++ b/ash/system/power/battery_notification.h
@@ -23,6 +23,8 @@
       PowerNotificationController::NotificationState notification_state);
   ~BatteryNotification();
 
+  static const char kNotificationId[];
+
   // Updates the notification if it still exists.
   void Update(
       PowerNotificationController::NotificationState notification_state);
diff --git a/base/pending_task.cc b/base/pending_task.cc
index f7eeb7c..06968059 100644
--- a/base/pending_task.cc
+++ b/base/pending_task.cc
@@ -15,13 +15,11 @@
 PendingTask::PendingTask(const Location& posted_from,
                          OnceClosure task,
                          TimeTicks queue_time,
-                         TimeTicks delayed_run_time,
-                         Nestable nestable)
+                         TimeTicks delayed_run_time)
     : task(std::move(task)),
       posted_from(posted_from),
       queue_time(queue_time),
-      delayed_run_time(delayed_run_time),
-      nestable(nestable) {}
+      delayed_run_time(delayed_run_time) {}
 
 PendingTask::PendingTask(PendingTask&& other) = default;
 
diff --git a/base/pending_task.h b/base/pending_task.h
index b40c334..e3179ea 100644
--- a/base/pending_task.h
+++ b/base/pending_task.h
@@ -28,8 +28,7 @@
   PendingTask(const Location& posted_from,
               OnceClosure task,
               TimeTicks queue_time,
-              TimeTicks delayed_run_time,
-              Nestable nestable = Nestable::kNestable);
+              TimeTicks delayed_run_time);
   PendingTask(PendingTask&& other);
   ~PendingTask();
 
@@ -77,12 +76,6 @@
   int sequence_num = 0;
 
   bool task_backtrace_overflow = false;
-
-  // OK to dispatch from a nested loop.
-  Nestable nestable;
-
-  // Needs high resolution timers.
-  bool is_high_res = false;
 };
 
 }  // namespace base
diff --git a/base/task/sequence_manager/tasks.cc b/base/task/sequence_manager/tasks.cc
index 43c9a02..8fb2187 100644
--- a/base/task/sequence_manager/tasks.cc
+++ b/base/task/sequence_manager/tasks.cc
@@ -15,8 +15,8 @@
     : PendingTask(posted_task.location,
                   std::move(posted_task.callback),
                   posted_task.queue_time,
-                  delayed_run_time,
-                  posted_task.nestable),
+                  delayed_run_time),
+      nestable(posted_task.nestable),
       task_type(posted_task.task_type),
       task_runner(std::move(posted_task.task_runner)),
       enqueue_order_(enqueue_order) {
diff --git a/base/task/sequence_manager/tasks.h b/base/task/sequence_manager/tasks.h
index b043cee..3e548b0 100644
--- a/base/task/sequence_manager/tasks.h
+++ b/base/task/sequence_manager/tasks.h
@@ -109,6 +109,12 @@
 
   bool enqueue_order_set() const { return enqueue_order_; }
 
+  // OK to dispatch from a nested loop.
+  Nestable nestable = Nestable::kNonNestable;
+
+  // Needs high resolution timers.
+  bool is_high_res = false;
+
   TaskType task_type;
 
   // The task runner this task is running on. Can be used by task runners that
diff --git a/base/task/thread_pool/task.cc b/base/task/thread_pool/task.cc
index 2ba82d22..763aa7f0 100644
--- a/base/task/thread_pool/task.cc
+++ b/base/task/thread_pool/task.cc
@@ -24,8 +24,7 @@
     : PendingTask(posted_from,
                   std::move(task),
                   queue_time,
-                  delay.is_zero() ? TimeTicks() : queue_time + delay,
-                  Nestable::kNonNestable) {
+                  delay.is_zero() ? TimeTicks() : queue_time + delay) {
   // ThreadPoolImpl doesn't use |sequence_num| but tracing (toplevel.flow)
   // relies on it being unique. While this subtle dependency is a bit
   // overreaching, ThreadPoolImpl is the only task system that doesn't use
diff --git a/base/util/memory_pressure/BUILD.gn b/base/util/memory_pressure/BUILD.gn
index b966f09..4f32083 100644
--- a/base/util/memory_pressure/BUILD.gn
+++ b/base/util/memory_pressure/BUILD.gn
@@ -6,6 +6,8 @@
 
 source_set("memory_pressure") {
   sources = [
+    "memory_pressure_level_reporter.cc",
+    "memory_pressure_level_reporter.h",
     "memory_pressure_voter.cc",
     "memory_pressure_voter.h",
     "multi_source_memory_pressure_monitor.cc",
@@ -56,6 +58,7 @@
   testonly = true
 
   sources = [
+    "memory_pressure_level_reporter_unittest.cc",
     "memory_pressure_voter_unittest.cc",
     "multi_source_memory_pressure_monitor_unittest.cc",
   ]
diff --git a/base/util/memory_pressure/memory_pressure_level_reporter.cc b/base/util/memory_pressure/memory_pressure_level_reporter.cc
new file mode 100644
index 0000000..d176b53
--- /dev/null
+++ b/base/util/memory_pressure/memory_pressure_level_reporter.cc
@@ -0,0 +1,122 @@
+// 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 "base/util/memory_pressure/memory_pressure_level_reporter.h"
+
+#include "base/bind.h"
+#include "base/memory/memory_pressure_listener.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/strcat.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
+
+namespace util {
+
+MemoryPressureLevelReporter::MemoryPressureLevelReporter(
+    MemoryPressureLevel initial_pressure_level)
+    : current_pressure_level_(initial_pressure_level) {
+  StartPeriodicTimer();
+}
+
+MemoryPressureLevelReporter::~MemoryPressureLevelReporter() {
+  // Make sure that the data about the last interval gets reported.
+  ReportHistogram(base::TimeTicks::Now());
+}
+
+void MemoryPressureLevelReporter::OnMemoryPressureLevelChanged(
+    MemoryPressureLevel new_level) {
+  const base::TimeTicks now = base::TimeTicks::Now();
+  ReportHistogram(now);
+
+  // Records the duration of the latest pressure session, there are 4
+  // transitions of interest:
+  //   - Moderate -> None
+  //   - Moderate -> Critical
+  //   - Critical -> Moderate
+  //   - Critical -> None
+  // |new_level| can be equal to |periodic_reporting_timer_| if this gets called
+  // by the one shot reporting timer, do nothing in this case.
+  if ((new_level != current_pressure_level_) &&
+      (current_pressure_level_ !=
+       MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_NONE)) {
+    constexpr char kHistogramPrefix[] = "Memory.PressureWindowDuration.";
+    std::string histogram_name;
+
+    DCHECK(!current_pressure_level_begin_.is_null());
+    switch (current_pressure_level_) {
+      // From:
+      case MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE: {
+        // To:
+        if (new_level == MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_NONE) {
+          histogram_name = base::StrCat({kHistogramPrefix, "ModerateToNone"});
+        } else {  // MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL
+          histogram_name =
+              base::StrCat({kHistogramPrefix, "ModerateToCritical"});
+        }
+        break;
+      }
+      // From:
+      case MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL: {
+        // To:
+        if (new_level == MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_NONE) {
+          histogram_name = base::StrCat({kHistogramPrefix, "CriticalToNone"});
+        } else {  // MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE
+          histogram_name =
+              base::StrCat({kHistogramPrefix, "CriticalToModerate"});
+        }
+        break;
+      }
+      case MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_NONE:
+      default:
+        NOTREACHED();
+        break;
+    }
+
+    base::UmaHistogramCustomTimes(
+        histogram_name, now - current_pressure_level_begin_,
+        base::TimeDelta::FromSeconds(1), base::TimeDelta::FromMinutes(10), 50);
+  }
+
+  current_pressure_level_begin_ = now;
+  current_pressure_level_ = new_level;
+
+  StartPeriodicTimer();
+}
+
+void MemoryPressureLevelReporter::ReportHistogram(base::TimeTicks now) {
+  auto duration = now - current_pressure_level_begin_;
+  auto duration_s = duration.InSeconds();
+  accumulator_buckets_[current_pressure_level_] +=
+      duration - base::TimeDelta::FromSeconds(duration_s);
+  auto accumulated_seconds =
+      accumulator_buckets_[current_pressure_level_].InSeconds();
+  if (accumulated_seconds > 0) {
+    duration_s += accumulated_seconds;
+    accumulator_buckets_[current_pressure_level_] -=
+        base::TimeDelta::FromSeconds(accumulated_seconds);
+  }
+
+  if (duration_s) {
+    // We can't use UmaHistogramEnumeration here as it doesn't support
+    // |AddCount|.
+    base::LinearHistogram::FactoryGet(
+        "Memory.PressureLevel2", 1, MemoryPressureLevel::kMaxValue + 1,
+        MemoryPressureLevel::kMaxValue + 2,
+        base::HistogramBase::kUmaTargetedHistogramFlag)
+        ->AddCount(current_pressure_level_, duration_s);
+  }
+}
+
+void MemoryPressureLevelReporter::StartPeriodicTimer() {
+  // Don't try to start the timer in tests that don't support it.
+  if (!base::SequencedTaskRunnerHandle::IsSet())
+    return;
+  periodic_reporting_timer_.Start(
+      FROM_HERE, base::TimeDelta::FromMinutes(5),
+      base::BindOnce(&MemoryPressureLevelReporter::OnMemoryPressureLevelChanged,
+                     // Unretained is safe because |this| owns this timer.
+                     base::Unretained(this), current_pressure_level_));
+}
+
+}  // namespace util
\ No newline at end of file
diff --git a/base/util/memory_pressure/memory_pressure_level_reporter.h b/base/util/memory_pressure/memory_pressure_level_reporter.h
new file mode 100644
index 0000000..5855ecd
--- /dev/null
+++ b/base/util/memory_pressure/memory_pressure_level_reporter.h
@@ -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.
+
+#ifndef BASE_UTIL_MEMORY_PRESSURE_MEMORY_PRESSURE_LEVEL_REPORTER_H_
+#define BASE_UTIL_MEMORY_PRESSURE_MEMORY_PRESSURE_LEVEL_REPORTER_H_
+
+#include <array>
+
+#include "base/memory/memory_pressure_listener.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+
+namespace util {
+
+// Report metrics related to memory pressure.
+class MemoryPressureLevelReporter {
+ public:
+  using MemoryPressureLevel = base::MemoryPressureListener::MemoryPressureLevel;
+
+  explicit MemoryPressureLevelReporter(
+      MemoryPressureLevel initial_pressure_level);
+  ~MemoryPressureLevelReporter();
+
+  // Should be called whenever the current memory pressure level changes.
+  void OnMemoryPressureLevelChanged(MemoryPressureLevel new_level);
+
+ private:
+  void ReportHistogram(base::TimeTicks now);
+  void StartPeriodicTimer();
+
+  MemoryPressureLevel current_pressure_level_;
+  base::TimeTicks current_pressure_level_begin_ = base::TimeTicks::Now();
+
+  // The reporting of the pressure level histogram is done in seconds, the
+  // duration in a given pressure state will be floored. This means that some
+  // time will be truncated each time we send a report. This array is used to
+  // accumulate the truncated time and add it to the reported value when it
+  // exceeds one second.
+  std::array<base::TimeDelta, MemoryPressureLevel::kMaxValue + 1>
+      accumulator_buckets_;
+
+  // Timer used to ensure a periodic reporting of the pressure level metric.
+  // Without this there's a risk that a browser crash will cause some data to
+  // be lost.
+  base::OneShotTimer periodic_reporting_timer_;
+};
+
+}  // namespace util
+
+#endif  // BASE_UTIL_MEMORY_PRESSURE_MEMORY_PRESSURE_LEVEL_REPORTER_H_
diff --git a/base/util/memory_pressure/memory_pressure_level_reporter_unittest.cc b/base/util/memory_pressure/memory_pressure_level_reporter_unittest.cc
new file mode 100644
index 0000000..ddf397b
--- /dev/null
+++ b/base/util/memory_pressure/memory_pressure_level_reporter_unittest.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 "base/util/memory_pressure/memory_pressure_level_reporter.h"
+
+#include <memory>
+
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace util {
+
+TEST(MemoryPressureLevelReporterTest, PressureWindowDuration) {
+  base::test::SingleThreadTaskEnvironment task_environment(
+      base::test::TaskEnvironment::MainThreadType::IO,
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+
+  MemoryPressureLevelReporter reporter(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
+  base::HistogramTester histogram_tester;
+
+  // Moderate -> None.
+  task_environment.AdvanceClock(base::TimeDelta::FromSeconds(12));
+  reporter.OnMemoryPressureLevelChanged(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
+  histogram_tester.ExpectTimeBucketCount(
+      "Memory.PressureWindowDuration.ModerateToNone",
+      base::TimeDelta::FromSeconds(12), 1);
+
+  // Moderate -> Critical.
+  reporter.OnMemoryPressureLevelChanged(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
+  task_environment.AdvanceClock(base::TimeDelta::FromSeconds(20));
+  reporter.OnMemoryPressureLevelChanged(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
+  histogram_tester.ExpectTimeBucketCount(
+      "Memory.PressureWindowDuration.ModerateToCritical",
+      base::TimeDelta::FromSeconds(20), 1);
+
+  // Critical -> None
+  task_environment.AdvanceClock(base::TimeDelta::FromSeconds(25));
+  reporter.OnMemoryPressureLevelChanged(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
+  histogram_tester.ExpectTimeBucketCount(
+      "Memory.PressureWindowDuration.CriticalToNone",
+      base::TimeDelta::FromSeconds(25), 1);
+
+  // Critical -> Moderate
+  reporter.OnMemoryPressureLevelChanged(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
+  task_environment.AdvanceClock(base::TimeDelta::FromSeconds(27));
+  reporter.OnMemoryPressureLevelChanged(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
+  histogram_tester.ExpectTimeBucketCount(
+      "Memory.PressureWindowDuration.CriticalToModerate",
+      base::TimeDelta::FromSeconds(27), 1);
+}
+
+TEST(MemoryPressureLevelReporterTest, MemoryPressureHistogram) {
+  base::test::SingleThreadTaskEnvironment task_environment(
+      base::test::TaskEnvironment::MainThreadType::IO,
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+
+  std::unique_ptr<MemoryPressureLevelReporter> reporter =
+      std::make_unique<MemoryPressureLevelReporter>(
+          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
+  base::HistogramTester histogram_tester;
+
+  constexpr base::TimeDelta kDelay = base::TimeDelta::FromSeconds(12);
+  const char* kHistogram = "Memory.PressureLevel2";
+
+  // None -> Moderate.
+  task_environment.AdvanceClock(kDelay);
+  reporter->OnMemoryPressureLevelChanged(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
+  // There one report for a |kdelay| MEMORY_PRESSURE_LEVEL_NONE session.
+  histogram_tester.ExpectBucketCount(
+      kHistogram,
+      static_cast<int>(
+          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
+      kDelay.InSeconds());
+
+  task_environment.AdvanceClock(kDelay);
+  reporter->OnMemoryPressureLevelChanged(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
+  // There one report for a |kdelay| MEMORY_PRESSURE_LEVEL_MODERATE session.
+  histogram_tester.ExpectBucketCount(
+      kHistogram,
+      static_cast<int>(
+          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE),
+      kDelay.InSeconds());
+
+  task_environment.AdvanceClock(kDelay);
+  reporter->OnMemoryPressureLevelChanged(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
+  // There's now two reports for a |kdelay| MEMORY_PRESSURE_LEVEL_NONE session,
+  // for a total of |2*kdelay|.
+  histogram_tester.ExpectBucketCount(
+      kHistogram,
+      static_cast<int>(
+          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
+      (2 * kDelay).InSeconds());
+
+  task_environment.AdvanceClock(kDelay);
+  histogram_tester.ExpectBucketCount(
+      kHistogram,
+      static_cast<int>(
+          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL),
+      0);
+  reporter.reset();
+  // Releasing the reporter should report the data from the current pressure
+  // session.
+  histogram_tester.ExpectBucketCount(
+      kHistogram,
+      static_cast<int>(
+          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL),
+      kDelay.InSeconds());
+}
+
+TEST(MemoryPressureLevelReporterTest, MemoryPressureHistogramAccumulatedTime) {
+  base::test::SingleThreadTaskEnvironment task_environment(
+      base::test::TaskEnvironment::MainThreadType::IO,
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+
+  MemoryPressureLevelReporter reporter(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
+  base::HistogramTester histogram_tester;
+
+  const char* kHistogram = "Memory.PressureLevel2";
+  constexpr base::TimeDelta kHalfASecond =
+      base::TimeDelta::FromMilliseconds(500);
+
+  task_environment.AdvanceClock(kHalfASecond);
+  reporter.OnMemoryPressureLevelChanged(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
+  // The delay is inferior to one second, there should be no data reported.
+  histogram_tester.ExpectBucketCount(
+      kHistogram,
+      static_cast<int>(
+          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
+      0);
+
+  reporter.OnMemoryPressureLevelChanged(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
+  task_environment.AdvanceClock(kHalfASecond);
+  reporter.OnMemoryPressureLevelChanged(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
+  // The delay is inferior to one second, there should be no data reported.
+  histogram_tester.ExpectBucketCount(
+      kHistogram,
+      static_cast<int>(
+          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
+      1);
+}
+
+TEST(MemoryPressureLevelReporterTest,
+     MemoryPressureHistogramPeriodicReporting) {
+  base::test::SingleThreadTaskEnvironment task_environment(
+      base::test::TaskEnvironment::MainThreadType::IO,
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+
+  MemoryPressureLevelReporter reporter(
+      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
+  base::HistogramTester histogram_tester;
+
+  const char* kHistogram = "Memory.PressureLevel2";
+
+  // Advancing the clock by a few seconds shouldn't cause any periodic
+  // reporting.
+  task_environment.FastForwardBy(base::TimeDelta::FromSeconds(10));
+  histogram_tester.ExpectBucketCount(
+      kHistogram,
+      static_cast<int>(
+          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
+      0);
+
+  // Advancing the clock by a few minutes should cause periodic reporting.
+  task_environment.FastForwardBy(base::TimeDelta::FromMinutes(5));
+  histogram_tester.ExpectBucketCount(
+      kHistogram,
+      static_cast<int>(
+          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
+      5 * 60 /* 5 minutes */);
+
+  task_environment.FastForwardBy(base::TimeDelta::FromMinutes(5));
+  histogram_tester.ExpectBucketCount(
+      kHistogram,
+      static_cast<int>(
+          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
+      2 * 5 * 60 /* 2 x 5 minutes */);
+}
+
+}  // namespace util
\ No newline at end of file
diff --git a/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc b/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc
index 32cbc1e..6b725e2 100644
--- a/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc
+++ b/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc
@@ -24,8 +24,8 @@
           base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
       dispatch_callback_(base::BindRepeating(
           &base::MemoryPressureListener::NotifyMemoryPressure)),
-      aggregator_(this) {
-}
+      aggregator_(this),
+      level_reporter_(current_pressure_level_) {}
 
 MultiSourceMemoryPressureMonitor::~MultiSourceMemoryPressureMonitor() {
   // Destroy system evaluator early while the remaining members of this class
@@ -86,6 +86,8 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_NE(current_pressure_level_, level);
 
+  level_reporter_.OnMemoryPressureLevelChanged(level);
+
   TRACE_EVENT_INSTANT(
       "base", "MultiSourceMemoryPressureMonitor::OnMemoryPressureLevelChanged",
       [&](perfetto::EventContext ctx) {
@@ -95,52 +97,6 @@
             base::trace_event::MemoryPressureLevelToTraceEnum(level));
       });
 
-  // Records the duration of the latest pressure session, there are 4
-  // transitions of interest:
-  //   - Moderate -> None
-  //   - Moderate -> Critical
-  //   - Critical -> Moderate
-  //   - Critical -> None
-
-  base::TimeTicks now = base::TimeTicks::Now();
-
-  if (current_pressure_level_ !=
-      MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_NONE) {
-    DCHECK(!last_pressure_change_timestamp_.is_null());
-    std::string histogram_name = "Memory.PressureWindowDuration.";
-    switch (current_pressure_level_) {
-      // From:
-      case MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE: {
-        // To:
-        if (level == MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_NONE) {
-          histogram_name += "ModerateToNone";
-        } else {  // MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL
-          histogram_name += "ModerateToCritical";
-        }
-        break;
-      }
-      // From:
-      case MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL: {
-        // To:
-        if (level == MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_NONE) {
-          histogram_name += "CriticalToNone";
-        } else {  // MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE
-          histogram_name += "CriticalToModerate";
-        }
-        break;
-      }
-      case MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_NONE:
-      default:
-        break;
-    }
-
-    base::UmaHistogramCustomTimes(
-        histogram_name, now - last_pressure_change_timestamp_,
-        base::TimeDelta::FromSeconds(1), base::TimeDelta::FromMinutes(10), 50);
-  }
-
-  last_pressure_change_timestamp_ = now;
-
   current_pressure_level_ = level;
 }
 
diff --git a/base/util/memory_pressure/multi_source_memory_pressure_monitor.h b/base/util/memory_pressure/multi_source_memory_pressure_monitor.h
index c9725ce..032d75c7 100644
--- a/base/util/memory_pressure/multi_source_memory_pressure_monitor.h
+++ b/base/util/memory_pressure/multi_source_memory_pressure_monitor.h
@@ -8,6 +8,7 @@
 #include "base/memory/memory_pressure_monitor.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
+#include "base/util/memory_pressure/memory_pressure_level_reporter.h"
 #include "base/util/memory_pressure/memory_pressure_voter.h"
 
 namespace util {
@@ -76,6 +77,8 @@
   // The timestamp of the last pressure change event.
   base::TimeTicks last_pressure_change_timestamp_;
 
+  MemoryPressureLevelReporter level_reporter_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(MultiSourceMemoryPressureMonitor);
diff --git a/base/util/memory_pressure/multi_source_memory_pressure_monitor_unittest.cc b/base/util/memory_pressure/multi_source_memory_pressure_monitor_unittest.cc
index e22ccb6..53859ee 100644
--- a/base/util/memory_pressure/multi_source_memory_pressure_monitor_unittest.cc
+++ b/base/util/memory_pressure/multi_source_memory_pressure_monitor_unittest.cc
@@ -36,65 +36,4 @@
       base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, base::nullopt);
 }
 
-TEST(MultiSourceMemoryPressureMonitorTest, Histograms) {
-  base::test::SingleThreadTaskEnvironment task_environment(
-      base::test::TaskEnvironment::MainThreadType::IO,
-      base::test::TaskEnvironment::TimeSource::MOCK_TIME);
-
-  MultiSourceMemoryPressureMonitor monitor;
-  base::HistogramTester histogram_tester;
-  monitor.Start();
-  auto* aggregator = monitor.aggregator_for_testing();
-
-  // Moderate -> None.
-  aggregator->OnVoteForTesting(
-      base::nullopt,
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
-  task_environment.AdvanceClock(base::TimeDelta::FromSeconds(12));
-  aggregator->OnVoteForTesting(
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
-  histogram_tester.ExpectTimeBucketCount(
-      "Memory.PressureWindowDuration.ModerateToNone",
-      base::TimeDelta::FromSeconds(12), 1);
-
-  // Moderate -> Critical.
-  aggregator->OnVoteForTesting(
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
-  task_environment.AdvanceClock(base::TimeDelta::FromSeconds(20));
-  aggregator->OnVoteForTesting(
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
-  histogram_tester.ExpectTimeBucketCount(
-      "Memory.PressureWindowDuration.ModerateToCritical",
-      base::TimeDelta::FromSeconds(20), 1);
-
-  // Critical -> None
-  task_environment.AdvanceClock(base::TimeDelta::FromSeconds(25));
-  aggregator->OnVoteForTesting(
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
-  histogram_tester.ExpectTimeBucketCount(
-      "Memory.PressureWindowDuration.CriticalToNone",
-      base::TimeDelta::FromSeconds(25), 1);
-
-  // Critical -> Moderate
-  aggregator->OnVoteForTesting(
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
-  task_environment.AdvanceClock(base::TimeDelta::FromSeconds(27));
-  aggregator->OnVoteForTesting(
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
-  histogram_tester.ExpectTimeBucketCount(
-      "Memory.PressureWindowDuration.CriticalToModerate",
-      base::TimeDelta::FromSeconds(27), 1);
-
-  // Clear vote so aggregator's destructor doesn't think there are loose voters.
-  aggregator->OnVoteForTesting(
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
-      base::nullopt);
-}
-
 }  // namespace util
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 8c7f79d3..4a589b28 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-4.20210503.0.1
+4.20210503.1.1
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index c33f3c65..fc17c96 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -886,6 +886,7 @@
   "java/res/values-v26/styles.xml",
   "java/res/values-v27/styles.xml",
   "java/res/values-v28/styles.xml",
+  "java/res/values-v31/styles.xml",
   "java/res/values-xhdpi/dimens.xml",
   "java/res/values-xlarge/dimens.xml",
   "java/res/values/attrs.xml",
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
index fce9db7..0d6753e 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
@@ -41,6 +41,7 @@
 import static org.chromium.chrome.features.start_surface.StartSurfaceMediator.FEED_VISIBILITY_CONSISTENCY;
 import static org.chromium.chrome.test.util.ViewUtils.VIEW_GONE;
 import static org.chromium.chrome.test.util.ViewUtils.onViewWaiting;
+import static org.chromium.chrome.test.util.ViewUtils.waitForStableView;
 import static org.chromium.chrome.test.util.ViewUtils.waitForView;
 
 import android.app.Activity;
@@ -81,6 +82,7 @@
 import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
 import org.chromium.base.test.params.ParameterSet;
 import org.chromium.base.test.params.ParameterizedRunner;
+import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.DisableIf;
@@ -95,6 +97,8 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.init.AsyncInitializationActivity;
+import org.chromium.chrome.browser.layouts.LayoutStateProvider;
+import org.chromium.chrome.browser.layouts.LayoutType;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.TabModel;
@@ -128,6 +132,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -160,6 +165,11 @@
     private final boolean mUseInstantStart;
     private final boolean mImmediateReturn;
 
+    private CallbackHelper mLayoutChangedCallbackHelper;
+    private LayoutStateProvider.LayoutStateObserver mLayoutObserver;
+    @LayoutType
+    private int mCurrentlyActiveLayout;
+
     public StartSurfaceTest(boolean useInstantStart, boolean immediateReturn) {
         CachedFeatureFlags.setForTesting(ChromeFeatureList.INSTANT_START, useInstantStart);
 
@@ -169,6 +179,8 @@
 
     @Before
     public void setUp() throws IOException {
+        mLayoutChangedCallbackHelper = new CallbackHelper();
+
         int expectedTabs = 1;
         int additionalTabs = expectedTabs - (mImmediateReturn ? 0 : 1);
         if (additionalTabs > 0) {
@@ -192,12 +204,24 @@
             // Create fake TabState files to emulate having one tab in previous session.
             TabAttributeCache.setTitleForTesting(0, "tab title");
             startMainActivityFromLauncher();
+
+            // Assume start surface is shown immediately.
+            mCurrentlyActiveLayout = LayoutType.TAB_SWITCHER;
         } else {
             assertFalse(ReturnToChromeExperimentsUtil.shouldShowTabSwitcher(-1));
             // Cannot use startMainActivityFromLauncher().
             // Otherwise tab switcher could be shown immediately if single-pane is enabled.
             mActivityTestRule.startMainActivityOnBlankPage();
             onViewWaiting(withId(R.id.home_button));
+
+            mLayoutObserver = new LayoutStateProvider.LayoutStateObserver() {
+                @Override
+                public void onFinishedShowing(@LayoutType int layoutType) {
+                    mCurrentlyActiveLayout = layoutType;
+                    mLayoutChangedCallbackHelper.notifyCalled();
+                }
+            };
+            mActivityTestRule.getActivity().getLayoutManager().addObserver(mLayoutObserver);
         }
     }
 
@@ -565,6 +589,8 @@
         onViewWaiting(withId(R.id.search_box_text));
         TextView urlBar = mActivityTestRule.getActivity().findViewById(R.id.url_bar);
         Assert.assertFalse(urlBar.isFocused());
+        waitForStableView(urlBar);
+        waitForStableView(mActivityTestRule.getActivity().findViewById(R.id.search_box_text));
         onView(withId(R.id.search_box_text)).perform(click());
         Assert.assertTrue(TextUtils.isEmpty(urlBar.getText()));
     }
@@ -1966,11 +1992,13 @@
     }
 
     private void waitForOverviewVisible() {
-        CriteriaHelper.pollUiThread(
-                ()
-                        -> mActivityTestRule.getActivity().getLayoutManager() != null
-                        && mActivityTestRule.getActivity().getLayoutManager().overviewVisible(),
-                MAX_TIMEOUT_MS, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+        int callCount = mLayoutChangedCallbackHelper.getCallCount();
+        if (mCurrentlyActiveLayout == LayoutType.TAB_SWITCHER) return;
+        try {
+            mLayoutChangedCallbackHelper.waitForCallback(callCount);
+        } catch (TimeoutException ex) {
+            assert false : "Timeout waiting for browser to enter tab switcher.";
+        }
     }
 }
 
diff --git a/chrome/android/features/start_surface/public/java/src/org/chromium/chrome/features/start_surface/StartSurfaceConfiguration.java b/chrome/android/features/start_surface/public/java/src/org/chromium/chrome/features/start_surface/StartSurfaceConfiguration.java
index b85dc90..a4d583f8c 100644
--- a/chrome/android/features/start_surface/public/java/src/org/chromium/chrome/features/start_surface/StartSurfaceConfiguration.java
+++ b/chrome/android/features/start_surface/public/java/src/org/chromium/chrome/features/start_surface/StartSurfaceConfiguration.java
@@ -13,12 +13,11 @@
 import org.chromium.base.SysUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.IntentHandler;
-import org.chromium.chrome.browser.compositor.layouts.Layout;
-import org.chromium.chrome.browser.compositor.layouts.StaticLayout;
 import org.chromium.chrome.browser.flags.BooleanCachedFieldTrialParameter;
 import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.StringCachedFieldTrialParameter;
+import org.chromium.chrome.browser.layouts.LayoutType;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefChangeRegistrar;
@@ -148,8 +147,8 @@
      * Tab is showing, and resets the flag in the Tab's UserData. This function returns true only
      * when {@link OMNIBOX_FOCUSED_ON_NEW_TAB} is enabled.
      */
-    public static boolean consumeFocusOnOmnibox(Tab tab, Layout layout) {
-        if (tab != null && tab.getUrl().isEmpty() && layout instanceof StaticLayout
+    public static boolean consumeFocusOnOmnibox(Tab tab, @LayoutType int layout) {
+        if (tab != null && tab.getUrl().isEmpty() && layout == LayoutType.BROWSING
                 && StartSurfaceUserData.getFocusOnOmnibox(tab)) {
             assert OMNIBOX_FOCUSED_ON_NEW_TAB.getValue();
             StartSurfaceUserData.setFocusOnOmnibox(tab, false);
diff --git a/chrome/android/java/res/values-sw600dp-v27/styles.xml b/chrome/android/java/res/values-sw600dp-v27/styles.xml
index 7a327ac..32507700 100644
--- a/chrome/android/java/res/values-sw600dp-v27/styles.xml
+++ b/chrome/android/java/res/values-sw600dp-v27/styles.xml
@@ -7,5 +7,5 @@
     <style name="Theme.Chromium.WithWindowAnimation"
         parent="Base.Theme.Chromium.WithWindowAnimation" />
     <style name="Theme.Chromium.WithActionBar" parent="Base.Theme.Chromium.WithActionBar" />
-    <style name="Theme.Chromium.DialogWhenLarge" parent="Base.Theme.Chromium.DialogWhenLarge" />
+    <style name="Base.V27.Theme.Chromium.DialogWhenLarge" parent="Base.Theme.Chromium.DialogWhenLarge" />
 </resources>
diff --git a/chrome/android/java/res/values-v27/styles.xml b/chrome/android/java/res/values-v27/styles.xml
index b131016..f64259a 100644
--- a/chrome/android/java/res/values-v27/styles.xml
+++ b/chrome/android/java/res/values-v27/styles.xml
@@ -14,7 +14,7 @@
         <item name="android:navigationBarDividerColor">@color/bottom_system_nav_divider_color</item>
         <item name="android:windowLightNavigationBar">@bool/window_light_navigation_bar</item>
     </style>
-    <style name="Theme.Chromium.DialogWhenLarge" parent="Base.Theme.Chromium.DialogWhenLarge">
+    <style name="Base.V27.Theme.Chromium.DialogWhenLarge" parent="Base.Theme.Chromium.DialogWhenLarge" >
         <item name="android:navigationBarColor">@color/bottom_system_nav_color</item>
         <item name="android:navigationBarDividerColor">@color/bottom_system_nav_divider_color</item>
         <item name="android:windowLightNavigationBar">@bool/window_light_navigation_bar</item>
diff --git a/chrome/android/java/res/values-v31/styles.xml b/chrome/android/java/res/values-v31/styles.xml
new file mode 100644
index 0000000..0cdc95c
--- /dev/null
+++ b/chrome/android/java/res/values-v31/styles.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<resources>
+    <style name="Theme.Chromium.DialogWhenLarge" parent="Base.V27.Theme.Chromium.DialogWhenLarge" >
+        <!-- TODO(https://crbug.com/1201349): Disable overscroll on S due to
+            consistency issues between browser and web platform. -->
+        <item name="android:overScrollMode">never</item>
+    </style>
+</resources>
diff --git a/chrome/android/java/res/values/styles.xml b/chrome/android/java/res/values/styles.xml
index 0a9a7a41..3c2c755 100644
--- a/chrome/android/java/res/values/styles.xml
+++ b/chrome/android/java/res/values/styles.xml
@@ -142,9 +142,11 @@
         <item name="windowNoTitle">true</item>
         <item name="windowActionBar">false</item>
     </style>
+    <!-- TODO(https://crbug.com/933438): Move v29 override to upstream. -->
     <style name="Base.Theme.Chromium.DialogWhenLarge"
         parent="Base.V17.Theme.Chromium.DialogWhenLarge" />
-    <style name="Theme.Chromium.DialogWhenLarge" parent="Base.Theme.Chromium.DialogWhenLarge" />
+    <style name="Base.V27.Theme.Chromium.DialogWhenLarge" parent="Base.Theme.Chromium.DialogWhenLarge" />
+    <style name="Theme.Chromium.DialogWhenLarge" parent="Base.V27.Theme.Chromium.DialogWhenLarge" />
     <style name="DimmingDialog" parent="Base.Theme.Chromium.DialogWhenLarge">
         <item name="android:windowLightNavigationBar" tools:targetApi="28">false</item>
     </style>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ActivityTabProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/ActivityTabProvider.java
index eb66bf5..1b87a6c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ActivityTabProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ActivityTabProvider.java
@@ -10,11 +10,9 @@
 import org.chromium.base.ObserverList;
 import org.chromium.base.ObserverList.RewindableIterator;
 import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.browser.compositor.layouts.Layout;
-import org.chromium.chrome.browser.compositor.layouts.LayoutManagerImpl;
-import org.chromium.chrome.browser.compositor.layouts.SceneChangeObserver;
-import org.chromium.chrome.browser.compositor.layouts.StaticLayout;
-import org.chromium.chrome.browser.compositor.layouts.phone.SimpleAnimationLayout;
+import org.chromium.chrome.browser.layouts.LayoutStateProvider;
+import org.chromium.chrome.browser.layouts.LayoutStateProvider.LayoutStateObserver;
+import org.chromium.chrome.browser.layouts.LayoutType;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabSelectionType;
@@ -146,11 +144,11 @@
     /** The {@link Tab} that is considered to be the activity's tab. */
     private Tab mActivityTab;
 
-    /** A handle to the {@link LayoutManagerImpl} to get the active layout. */
-    private LayoutManagerImpl mLayoutManager;
+    /** A handle to the {@link LayoutStateProvider} to get the active layout. */
+    private LayoutStateProvider mLayoutStateProvider;
 
-    /** The observer watching scene changes in the {@link LayoutManagerImpl}. */
-    private SceneChangeObserver mSceneChangeObserver;
+    /** The observer watching scene changes in the active layout. */
+    private LayoutStateObserver mLayoutStateObserver;
 
     /** A handle to the {@link TabModelSelector}. */
     private TabModelSelector mTabModelSelector;
@@ -169,7 +167,7 @@
      */
     public ActivityTabProvider() {
         mRewindableIterator = mObservers.rewindableIterator();
-        mSceneChangeObserver = new SceneChangeObserver() {
+        mLayoutStateObserver = new LayoutStateObserver() {
             @Override
             public void onTabSelectionHinted(int tabId) {
                 if (mTabModelSelector == null || mLastHintedTabId == tabId) return;
@@ -182,15 +180,15 @@
             }
 
             @Override
-            public void onSceneChange(Layout layout) {
+            public void onStartedShowing(@LayoutType int layout, boolean showToolbar) {
                 // The {@link SimpleAnimationLayout} is a special case, the intent is not to switch
                 // tabs, but to merely run an animation. In this case, do nothing. If the animation
                 // layout does result in a new tab {@link TabModelObserver#didSelectTab} will
                 // trigger the event instead. If the tab does not change, the event will no
-                if (layout instanceof SimpleAnimationLayout) return;
+                if (LayoutType.SIMPLE_ANIMATION == layout) return;
 
                 Tab tab = mTabModelSelector.getCurrentTab();
-                if (!(layout instanceof StaticLayout)) tab = null;
+                if (layout != LayoutType.BROWSING) tab = null;
                 triggerActivityTabChangeEvent(tab);
             }
         };
@@ -237,12 +235,12 @@
     }
 
     /**
-     * @param layoutManager A {@link LayoutManagerImpl} for watching for scene changes.
+     * @param layoutStateProvider A {@link LayoutStateProvider} for watching for scene changes.
      */
-    public void setLayoutManager(LayoutManagerImpl layoutManager) {
-        assert mLayoutManager == null;
-        mLayoutManager = layoutManager;
-        mLayoutManager.addSceneChangeObserver(mSceneChangeObserver);
+    public void setLayoutStateProvider(LayoutStateProvider layoutStateProvider) {
+        assert mLayoutStateProvider == null;
+        mLayoutStateProvider = layoutStateProvider;
+        mLayoutStateProvider.addObserver(mLayoutStateObserver);
     }
 
     /**
@@ -251,9 +249,9 @@
      */
     private void triggerActivityTabChangeEvent(Tab tab) {
         // Allow the event to trigger before native is ready (before the layout manager is set).
-        if (mLayoutManager != null
-                && !(mLayoutManager.getActiveLayout() instanceof StaticLayout
-                        || mLayoutManager.getActiveLayout() instanceof SimpleAnimationLayout)
+        if (mLayoutStateProvider != null
+                && !(mLayoutStateProvider.isLayoutVisible(LayoutType.BROWSING)
+                        || mLayoutStateProvider.isLayoutVisible(LayoutType.SIMPLE_ANIMATION))
                 && tab != null) {
             return;
         }
@@ -302,8 +300,8 @@
     /** Clean up and detach any observers this object created. */
     public void destroy() {
         mObservers.clear();
-        if (mLayoutManager != null) mLayoutManager.removeSceneChangeObserver(mSceneChangeObserver);
-        mLayoutManager = null;
+        if (mLayoutStateProvider != null) mLayoutStateProvider.removeObserver(mLayoutStateObserver);
+        mLayoutStateProvider = null;
         if (mTabModelObserver != null) mTabModelObserver.destroy();
         if (mTabModelSelectorObserver != null) {
             mTabModelSelector.removeObserver(mTabModelSelectorObserver);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index 88ad86ab..a5a557a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -1989,7 +1989,7 @@
                     getCompositorViewHolder().getLayoutManager().getToolbarSwipeHandler());
         }
 
-        mActivityTabProvider.setLayoutManager(layoutManager);
+        mActivityTabProvider.setLayoutStateProvider(layoutManager);
 
         if (mContextualSearchManager != null) {
             mContextualSearchManager.initialize(contentContainer, layoutManager,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/net/connectivitydetector/ConnectivityDetector.java b/chrome/android/java/src/org/chromium/chrome/browser/net/connectivitydetector/ConnectivityDetector.java
index 636b9c7..4a2c1b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/net/connectivitydetector/ConnectivityDetector.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/net/connectivitydetector/ConnectivityDetector.java
@@ -23,7 +23,6 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.AsyncTask;
 import org.chromium.chrome.browser.content.ContentUtils;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.net.ConnectionType;
 import org.chromium.net.NetworkChangeNotifier;
 
@@ -143,13 +142,6 @@
         @TargetApi(Build.VERSION_CODES.M)
         @Override
         public @ConnectionState int inferConnectionStateFromSystem() {
-            // Skip the system check below in order to force the HTTP probes. This is used for
-            // manual testing purposes.
-            if (ChromeFeatureList.isEnabled(
-                        ChromeFeatureList.OFFLINE_INDICATOR_ALWAYS_HTTP_PROBE)) {
-                return ConnectionState.NONE;
-            }
-
             // NET_CAPABILITY_VALIDATED and NET_CAPABILITY_CAPTIVE_PORTAL are only available on
             // Marshmallow and later versions.
             ConnectivityManager connectivityManager = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 207f980..ae93a17 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -814,6 +814,11 @@
                     updateButtonStatus();
                 }
             }
+
+            @Override
+            public void onFinishedShowing(@LayoutType int layoutType) {
+                maybeFocusOmnibox(layoutType, mActivityTabProvider.get());
+            }
         };
 
         mSceneChangeObserver = new SceneChangeObserver() {
@@ -830,7 +835,6 @@
             @Override
             public void onSceneChange(Layout layout) {
                 mToolbar.setContentAttached(layout.shouldDisplayContentOverlay());
-                maybeFocusOmnibox(layout, mActivityTabProvider.get());
             }
         };
 
@@ -878,7 +882,7 @@
     /**
      * May set Omnibox focused if the Tab has the flag to require focusing the Omnibox.
      */
-    private void maybeFocusOmnibox(Layout layout, Tab tab) {
+    private void maybeFocusOmnibox(@LayoutType int layout, Tab tab) {
         if (StartSurfaceConfiguration.consumeFocusOnOmnibox(tab, layout)) {
             if (mLocationBar.getOmniboxStub() == null
                     || mLocationBar.getOmniboxStub().isUrlBarFocused()) {
@@ -1196,7 +1200,7 @@
                 && !currentTab.getUrl().isEmpty()) {
             mControlContainer.setReadyForBitmapCapture(true);
         }
-        maybeFocusOmnibox(mLayoutManager.getActiveLayout(), currentTab);
+        maybeFocusOmnibox(mLayoutManager.getActiveLayout().getLayoutType(), currentTab);
 
         if (ChromeFeatureList.isEnabled(ChromeFeatureList.TOOLBAR_IPH_ANDROID)) {
             UserEducationHelper userEducationHelper = new UserEducationHelper(mActivity, mHandler);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java
index 68b56db..700b401 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java
@@ -31,6 +31,7 @@
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.layouts.LayoutStateProvider.LayoutStateObserver;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabLaunchType;
@@ -256,17 +257,17 @@
         });
 
         // Enter the tab switcher and select a different tab.
+        setTabSwitcherState(true);
+
         ThreadUtils.runOnUiThreadBlocking(() -> {
-            mActivity.getLayoutManager().showOverview(false);
-            mTestSupport.endAllAnimations();
             assertEquals("The bottom sheet should be hidden.",
                     BottomSheetController.SheetState.HIDDEN, mSheetController.getSheetState());
             mActivity.getTabModelSelector().getCurrentModel().setIndex(
                     0, TabSelectionType.FROM_USER);
-            mActivity.getLayoutManager().hideOverview(false);
-            mTestSupport.endAllAnimations();
         });
 
+        setTabSwitcherState(false);
+
         contentChangeHelper.waitForCallback(0);
         assertEquals("The bottom sheet still should be hidden.",
                 BottomSheetController.SheetState.HIDDEN, mSheetController.getSheetState());
@@ -676,15 +677,41 @@
      * Enter and immediately exit the tab switcher. This function will assert that the sheet is not
      * showing in the tab switcher.
      */
-    private void enterAndExitTabSwitcher() {
+    private void enterAndExitTabSwitcher() throws TimeoutException {
+        setTabSwitcherState(true);
+
+        assertEquals("The bottom sheet should be hidden.", BottomSheetController.SheetState.HIDDEN,
+                mSheetController.getSheetState());
+
+        setTabSwitcherState(false);
+    }
+
+    /**
+     * Set the tab switcher state and wait for that state to be settled.
+     * @param shown Whether the tab switcher should be shown.
+     * @throws TimeoutException
+     */
+    private void setTabSwitcherState(boolean shown) throws TimeoutException {
+        CallbackHelper finishedShowingCallbackHelper = new CallbackHelper();
+        LayoutStateObserver observer = new LayoutStateObserver() {
+            @Override
+            public void onFinishedShowing(int layoutType) {
+                finishedShowingCallbackHelper.notifyCalled();
+            }
+        };
+        mActivity.getLayoutManager().addObserver(observer);
+
         ThreadUtils.runOnUiThreadBlocking(() -> {
-            mActivity.getLayoutManager().showOverview(false);
-            mTestSupport.endAllAnimations();
-            assertEquals("The bottom sheet should be hidden.",
-                    BottomSheetController.SheetState.HIDDEN, mSheetController.getSheetState());
-            mActivity.getLayoutManager().hideOverview(false);
-            mTestSupport.endAllAnimations();
+            if (shown) {
+                mActivity.getLayoutManager().showOverview(false);
+            } else {
+                mActivity.getLayoutManager().hideOverview(false);
+            }
         });
+
+        finishedShowingCallbackHelper.waitForFirst();
+        mActivity.getLayoutManager().removeObserver(observer);
+        ThreadUtils.runOnUiThreadBlocking(mTestSupport::endAllAnimations);
     }
 
     /**
@@ -699,15 +726,13 @@
             }
         });
 
-        int previousCallCount = tabSelectedHelper.getCallCount();
-
         ThreadUtils.runOnUiThreadBlocking(() -> {
             mActivity.getTabCreator(false).createNewTab(new LoadUrlParams("about:blank"),
                     TabLaunchType.FROM_LONGPRESS_BACKGROUND, null);
         });
 
-        tabSelectedHelper.waitForCallback(previousCallCount, 1);
-        ThreadUtils.runOnUiThreadBlocking(() -> mTestSupport.endAllAnimations());
+        tabSelectedHelper.waitForFirst();
+        ThreadUtils.runOnUiThreadBlocking(mTestSupport::endAllAnimations);
     }
 
     /**
@@ -716,6 +741,6 @@
     private void openNewTabInForeground() {
         ChromeTabUtils.fullyLoadUrlInNewTab(
                 InstrumentationRegistry.getInstrumentation(), mActivity, "about:blank", false);
-        ThreadUtils.runOnUiThreadBlocking(() -> mTestSupport.endAllAnimations());
+        ThreadUtils.runOnUiThreadBlocking(mTestSupport::endAllAnimations);
     }
 }
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 379f904..f634834 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-92.0.4489.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-92.0.4492.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index f34532d..aa07c14 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -576,18 +576,18 @@
     Password deleted from this device and your Google Account
   </message>
   <message name="IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT" desc="Message displayed in the moving multiple passwords to the user Google Account banner.">
-    To use them on all your devices, move them to your Google Account
+    To easily use them on all your devices, you can move them to your Google Account
   </message>
    <message name="IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_COUNT" desc="Label shown in the link that open moving multiple password dialog.">
     {COUNT, plural,
-      =1 {{COUNT} password is accessible on this device}
-      other {{COUNT} passwords are accessible on this device}}
+      =1 {{COUNT} password is stored on this device}
+      other {{COUNT} passwords are stored on this device}}
   </message>
-  <message name="IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_FOOTER" desc="Footer for the dialog that asks the user to select which passwords to move to their Google Account.">
-    Passwords will be moved from your device to your Google Account
+  <message name="IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_BODY_TEXT" desc="Text displayed in the dialog that asks the user to select which passwords to move to their Google Account.">
+    Choose which passwords to move. You can access them whenever you're signed in.
   </message>
   <message name="IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_TITLE" desc="Title for the dialog that asks the user to select which passwords to move to their Google Account.">
-    Choose which passwords to move
+    Move passwords from this device to your Google Account?
   </message>
   <message name="IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_SNACKBAR" desc="Text in the snackbar that appears after the user successfuly moved local passwords to their Google Account.">
     Passwords moved to your Google Account
@@ -598,6 +598,12 @@
   <message name="IDS_SETTINGS_PASSWORD_MOVE_TO_ACCOUNT_DIALOG_BODY" desc="Description message for the dialog that confirms whether the user wishes to move a password to their Google Account.">
     Move your password to your Google Account to access it securely wherever you're signed in
   </message>
+  <message name="IDS_SETTINGS_PASSWORD_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_MOVE_BUTTON_TEXT" desc="Text for the button that confirms moving multiple passwords to the user Google Account.">
+    Move passwords
+  </message>
+  <message name="IDS_SETTINGS_PASSWORD_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_CANCEL_BUTTON_TEXT" desc="Text for the button that cancels the action of moving multiple passwords to the user Google Account.">
+    Don't move
+  </message>
   <message name="IDS_SETTINGS_PASSWORD_MOVE_TO_ACCOUNT_DIALOG_MOVE_BUTTON_TEXT" desc="Text for the button that confirms moving a password to the user Google Account.">
     Move
   </message>
@@ -605,7 +611,7 @@
     Cancel
   </message>
   <message name="IDS_SETTINGS_PASSWORD_OPEN_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_BUTTON_TEXT" desc="Text for the button that opens the dialog that moves multiple passwords to the user Google Account.">
-    Move passwords
+    Get started
   </message>
   <message name="IDS_SETTINGS_PASSWORD_REMOVE_DIALOG_TITLE" desc="Title for the dialog that asks the user which versions of a password to remove (device, Google Account or both).">
     Delete password?
@@ -1743,6 +1749,20 @@
   <message name="IDS_SETTINGS_SEARCH_ENGINES_MANAGE_EXTENSION" desc="Text displayed for a button that allows the user to manage a Chrome extension">
     Manage
   </message>
+  <!-- TODO(yoangela): These strings are placeholder strings, to be updated when these strings are finalized -->
+  <message name="IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_OPTIONS" desc="Label for scoped search mode trigger options section">
+    Keyboard shortcuts for search keywords
+  </message>
+  <message name="IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_DESCRIPTION" desc="Text explaining how to use keyword mode">
+    In the address bar, enter the keyword for the website you want to search. Then, use a keyboard shortcut to continue.
+  </message>
+  <message name="IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_SPACE_ENABLED" desc="Label for setting that toggles whether scoped search mode can be triggered by space">
+    Space or Tab
+  </message>
+  <message name="IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_SPACE_DISABLED" desc="Label for setting that toggles whether scoped search mode can be triggered by space">
+    Tab
+  </message>
+
 
   <!-- Site Settings Page -->
   <message name="IDS_SETTINGS_EXCEPTIONS_EMBEDDED_ON_HOST" desc="Template text for a child row in the content Exceptions page view. Controls the permission setting for the parent page when embedded on the specified site.">
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_CANCEL_BUTTON_TEXT.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_CANCEL_BUTTON_TEXT.png.sha1
new file mode 100644
index 0000000..a1333dfc
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_CANCEL_BUTTON_TEXT.png.sha1
@@ -0,0 +1 @@
+31992863fb988809a87f4b033361f17197259c81
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_MOVE_BUTTON_TEXT.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_MOVE_BUTTON_TEXT.png.sha1
new file mode 100644
index 0000000..a1333dfc
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_MOVE_BUTTON_TEXT.png.sha1
@@ -0,0 +1 @@
+31992863fb988809a87f4b033361f17197259c81
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT.png.sha1
index 80acf6a..9d655df 100644
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT.png.sha1
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT.png.sha1
@@ -1 +1 @@
-eca1d45d15c5cfa5cf94eba96f1ca96f763d1887
\ No newline at end of file
+87aa0bf98595c21dd04ae264f209c10534a40285
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_COUNT.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_COUNT.png.sha1
index 80acf6a..9d655df 100644
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_COUNT.png.sha1
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_COUNT.png.sha1
@@ -1 +1 @@
-eca1d45d15c5cfa5cf94eba96f1ca96f763d1887
\ No newline at end of file
+87aa0bf98595c21dd04ae264f209c10534a40285
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_BODY_TEXT.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_BODY_TEXT.png.sha1
new file mode 100644
index 0000000..a1333dfc
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_BODY_TEXT.png.sha1
@@ -0,0 +1 @@
+31992863fb988809a87f4b033361f17197259c81
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_FOOTER.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_FOOTER.png.sha1
deleted file mode 100644
index bfe31349..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_FOOTER.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-886f6d4fe2fb704a0ed8e39365da209ad44fee15
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_TITLE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_TITLE.png.sha1
index bfe31349..a1333dfc 100644
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_TITLE.png.sha1
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_TITLE.png.sha1
@@ -1 +1 @@
-886f6d4fe2fb704a0ed8e39365da209ad44fee15
\ No newline at end of file
+31992863fb988809a87f4b033361f17197259c81
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_OPEN_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_BUTTON_TEXT.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_OPEN_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_BUTTON_TEXT.png.sha1
index 80acf6a..9d655df 100644
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_OPEN_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_BUTTON_TEXT.png.sha1
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_OPEN_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_BUTTON_TEXT.png.sha1
@@ -1 +1 @@
-eca1d45d15c5cfa5cf94eba96f1ca96f763d1887
\ No newline at end of file
+87aa0bf98595c21dd04ae264f209c10534a40285
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_DESCRIPTION.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..2c5493f0
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+0c55720dff7d7452b6441b30c10fbbdb53d3157e
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_OPTIONS.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_OPTIONS.png.sha1
new file mode 100644
index 0000000..2c5493f0
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_OPTIONS.png.sha1
@@ -0,0 +1 @@
+0c55720dff7d7452b6441b30c10fbbdb53d3157e
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_SPACE_DISABLED.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_SPACE_DISABLED.png.sha1
new file mode 100644
index 0000000..2c5493f0
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_SPACE_DISABLED.png.sha1
@@ -0,0 +1 @@
+0c55720dff7d7452b6441b30c10fbbdb53d3157e
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_SPACE_ENABLED.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_SPACE_ENABLED.png.sha1
new file mode 100644
index 0000000..2c5493f0
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_SPACE_ENABLED.png.sha1
@@ -0,0 +1 @@
+0c55720dff7d7452b6441b30c10fbbdb53d3157e
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 8ca72b3..651e476 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -347,8 +347,6 @@
     "component_updater/subresource_filter_component_installer.h",
     "component_updater/trust_token_key_commitments_component_installer.cc",
     "component_updater/trust_token_key_commitments_component_installer.h",
-    "component_updater/zxcvbn_data_component_installer.cc",
-    "component_updater/zxcvbn_data_component_installer.h",
     "consent_auditor/consent_auditor_factory.cc",
     "consent_auditor/consent_auditor_factory.h",
     "content_index/content_index_metrics.cc",
@@ -2347,7 +2345,6 @@
     "//third_party/zlib:minizip",
     "//third_party/zlib/google:compression_utils",
     "//third_party/zlib/google:zip",
-    "//third_party/zxcvbn-cpp",
     "//ui/accessibility",
     "//ui/base",
     "//ui/base:ui_data_pack",
@@ -3483,6 +3480,8 @@
       "component_updater/soda_component_installer.h",
       "component_updater/soda_language_pack_component_installer.cc",
       "component_updater/soda_language_pack_component_installer.h",
+      "component_updater/zxcvbn_data_component_installer.cc",
+      "component_updater/zxcvbn_data_component_installer.h",
       "content_settings/generated_cookie_prefs.cc",
       "content_settings/generated_cookie_prefs.h",
       "content_settings/generated_notification_pref.cc",
@@ -4223,6 +4222,7 @@
       "//courgette:courgette_lib",
       "//services/device/public/cpp/hid",
       "//third_party/sqlite",
+      "//third_party/zxcvbn-cpp",
     ]
     public_deps += [
       "//chrome/common:buildflags",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 082dbf4..13bc63d0 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -177,7 +177,6 @@
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/forcedark/forcedark_switches.h"
 #include "third_party/blink/public/common/switches.h"
-#include "third_party/leveldatabase/leveldb_features.h"
 #include "ui/accessibility/accessibility_features.h"
 #include "ui/accessibility/accessibility_switches.h"
 #include "ui/base/ui_base_features.h"
@@ -3628,11 +3627,6 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(offline_pages::kOfflineIndicatorFeature,
                                     kOfflineIndicatorFeatureVariations,
                                     "OfflineIndicator")},
-    {"offline-indicator-always-http-probe",
-     flag_descriptions::kOfflineIndicatorAlwaysHttpProbeName,
-     flag_descriptions::kOfflineIndicatorAlwaysHttpProbeDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(
-         offline_pages::kOfflineIndicatorAlwaysHttpProbeFeature)},
     {"offline-indicator-v2", flag_descriptions::kOfflineIndicatorV2Name,
      flag_descriptions::kOfflineIndicatorV2Description, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kOfflineIndicatorV2)},
@@ -3982,10 +3976,6 @@
      flag_descriptions::kImmersiveFullscreenDescription, kOsMac,
      FEATURE_VALUE_TYPE(features::kImmersiveFullscreen)},
 #endif  // OS_MAC
-    {"rewrite-leveldb-on-deletion",
-     flag_descriptions::kRewriteLevelDBOnDeletionName,
-     flag_descriptions::kRewriteLevelDBOnDeletionDescription, kOsAll,
-     FEATURE_VALUE_TYPE(leveldb::kLevelDBRewriteFeature)},
     {"passive-listener-default",
      flag_descriptions::kPassiveEventListenerDefaultName,
      flag_descriptions::kPassiveEventListenerDefaultDescription, kOsAll,
@@ -4357,6 +4347,10 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(omnibox::kKeywordSpaceTriggering,
                                     kOmniboxKeywordSpaceTriggeringVariations,
                                     "OmniboxBundledExperimentV1")},
+    {"omnibox-keyword-space-triggering-setting",
+     flag_descriptions::kOmniboxKeywordSpaceTriggeringSettingName,
+     flag_descriptions::kOmniboxKeywordSpaceTriggeringSettingDescription,
+     kOsDesktop, FEATURE_VALUE_TYPE(omnibox::kKeywordSpaceTriggeringSetting)},
 #endif  // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) ||
         // defined(OS_WIN)
 
@@ -4478,11 +4472,6 @@
                                     kOmniboxDynamicMaxAutocompleteVariations,
                                     "OmniboxBundledExperimentV1")},
 
-    {"omnibox-ui-swap-title-and-url",
-     flag_descriptions::kOmniboxUISwapTitleAndUrlName,
-     flag_descriptions::kOmniboxUISwapTitleAndUrlDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(omnibox::kUIExperimentSwapTitleAndUrl)},
-
     {"omnibox-webui-omnibox-popup",
      flag_descriptions::kOmniboxWebUIOmniboxPopupName,
      flag_descriptions::kOmniboxWebUIOmniboxPopupDescription, kOsDesktop,
@@ -4765,10 +4754,6 @@
                                     kTabHoverCardImagesVariations,
                                     "TabHoverCardImages")},
 
-    {"stop-in-background", flag_descriptions::kStopInBackgroundName,
-     flag_descriptions::kStopInBackgroundDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(blink::features::kStopInBackground)},
-
     {"enable-storage-pressure-event",
      flag_descriptions::kStoragePressureEventName,
      flag_descriptions::kStoragePressureEventDescription, kOsAll,
diff --git a/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.cc b/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.cc
index 6cf47b5f..1213d896 100644
--- a/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.cc
+++ b/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.cc
@@ -302,36 +302,37 @@
       error_code, /* is_read_operation = */ false));
 }
 
-// Common success callback for ReadGattCharacteristic and ReadGattDescriptor
-void OnGattReadDone(GattReadCallback callback,
-                    const std::vector<uint8_t>& result) {
+// Common callback (success and error) for ReadGattCharacteristic and
+// ReadGattDescriptor.
+void OnGattRead(
+    GattReadCallback callback,
+    base::Optional<device::BluetoothRemoteGattService::GattErrorCode>
+        error_code,
+    const std::vector<uint8_t>& result) {
   arc::mojom::BluetoothGattValuePtr gattValue =
       arc::mojom::BluetoothGattValue::New();
-  gattValue->status = arc::mojom::BluetoothGattStatus::GATT_SUCCESS;
-  gattValue->value = result;
-  std::move(callback).Run(std::move(gattValue));
-}
 
-// Common error callback for ReadGattCharacteristic and ReadGattDescriptor
-void OnGattReadError(GattReadCallback callback,
-                     BluetoothGattService::GattErrorCode error_code) {
-  arc::mojom::BluetoothGattValuePtr gattValue =
-      arc::mojom::BluetoothGattValue::New();
-  gattValue->status =
-      ConvertGattErrorCodeToStatus(error_code, /* is_read_operation = */ true);
+  if (error_code.has_value()) {
+    gattValue->status = ConvertGattErrorCodeToStatus(
+        error_code.value(), /*is_read_operation=*/true);
+  } else {
+    gattValue->status = arc::mojom::BluetoothGattStatus::GATT_SUCCESS;
+  }
+  gattValue->value = result;
   std::move(callback).Run(std::move(gattValue));
 }
 
 // Callback function for mojom::BluetoothInstance::RequestGattRead
 void OnGattServerRead(
-    BluetoothLocalGattService::Delegate::ValueCallback success_callback,
-    BluetoothLocalGattService::Delegate::ErrorCallback error_callback,
+    BluetoothLocalGattService::Delegate::ValueCallback callback,
     arc::mojom::BluetoothGattStatus status,
     const std::vector<uint8_t>& value) {
-  if (status == arc::mojom::BluetoothGattStatus::GATT_SUCCESS)
-    std::move(success_callback).Run(value);
-  else
-    std::move(error_callback).Run();
+  if (status == arc::mojom::BluetoothGattStatus::GATT_SUCCESS) {
+    std::move(callback).Run(/*error_code=*/base::nullopt, value);
+  } else {
+    std::move(callback).Run(BluetoothRemoteGattService::GATT_ERROR_FAILED,
+                            /*value=*/std::vector<uint8_t>());
+  }
 }
 
 // Callback function for mojom::BluetoothInstance::RequestGattWrite
@@ -878,13 +879,13 @@
     const LocalGattAttribute* attribute,
     int offset,
     mojom::BluetoothGattDBAttributeType attribute_type,
-    ValueCallback success_callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
       arc_bridge_service_->bluetooth(), RequestGattRead);
   if (!bluetooth_instance || !IsGattOffsetValid(offset)) {
-    std::move(error_callback).Run();
+    std::move(callback).Run(BluetoothRemoteGattService::GATT_ERROR_FAILED,
+                            /*value=*/std::vector<uint8_t>());
     return;
   }
 
@@ -893,9 +894,7 @@
   bluetooth_instance->RequestGattRead(
       mojom::BluetoothAddress::From(device->GetAddress()),
       gatt_handle_[attribute->GetIdentifier()], offset, false /* is_long */,
-      attribute_type,
-      base::BindOnce(&OnGattServerRead, std::move(success_callback),
-                     std::move(error_callback)));
+      attribute_type, base::BindOnce(&OnGattServerRead, std::move(callback)));
 }
 
 void ArcBluetoothBridge::OnGattServerPrepareWrite(
@@ -967,12 +966,11 @@
     const BluetoothDevice* device,
     const BluetoothLocalGattCharacteristic* characteristic,
     int offset,
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   OnGattAttributeReadRequest(
       device, characteristic, offset,
       mojom::BluetoothGattDBAttributeType::BTGATT_DB_CHARACTERISTIC,
-      std::move(callback), std::move(error_callback));
+      std::move(callback));
 }
 
 void ArcBluetoothBridge::OnCharacteristicWriteRequest(
@@ -1008,12 +1006,11 @@
     const BluetoothDevice* device,
     const BluetoothLocalGattDescriptor* descriptor,
     int offset,
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   OnGattAttributeReadRequest(
       device, descriptor, offset,
       mojom::BluetoothGattDBAttributeType::BTGATT_DB_DESCRIPTOR,
-      std::move(callback), std::move(error_callback));
+      std::move(callback));
 }
 
 void ArcBluetoothBridge::OnDescriptorWriteRequest(
@@ -1731,10 +1728,8 @@
   DCHECK(characteristic);
   DCHECK(characteristic->GetPermissions() & kGattReadPermission);
 
-  auto split_callback = base::SplitOnceCallback(std::move(callback));
   characteristic->ReadRemoteCharacteristic(
-      base::BindOnce(&OnGattReadDone, std::move(split_callback.first)),
-      base::BindOnce(&OnGattReadError, std::move(split_callback.second)));
+      base::BindOnce(&OnGattRead, std::move(callback)));
 }
 
 void ArcBluetoothBridge::WriteGattCharacteristic(
@@ -1777,10 +1772,8 @@
   DCHECK(descriptor);
   DCHECK(descriptor->GetPermissions() & kGattReadPermission);
 
-  auto split_callback = base::SplitOnceCallback(std::move(callback));
   descriptor->ReadRemoteDescriptor(
-      base::BindOnce(&OnGattReadDone, std::move(split_callback.first)),
-      base::BindOnce(&OnGattReadError, std::move(split_callback.second)));
+      base::BindOnce(&OnGattRead, std::move(callback)));
 }
 
 void ArcBluetoothBridge::WriteGattDescriptor(
diff --git a/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.h b/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.h
index 9a24e479..6ce5a52 100644
--- a/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.h
+++ b/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.h
@@ -160,8 +160,7 @@
       const device::BluetoothDevice* device,
       const device::BluetoothLocalGattCharacteristic* characteristic,
       int offset,
-      ValueCallback callback,
-      ErrorCallback error_callback) override;
+      ValueCallback callback) override;
 
   void OnCharacteristicWriteRequest(
       const device::BluetoothDevice* device,
@@ -184,8 +183,7 @@
       const device::BluetoothDevice* device,
       const device::BluetoothLocalGattDescriptor* descriptor,
       int offset,
-      ValueCallback callback,
-      ErrorCallback error_callback) override;
+      ValueCallback callback) override;
 
   void OnDescriptorWriteRequest(
       const device::BluetoothDevice* device,
@@ -467,8 +465,7 @@
       const LocalGattAttribute* attribute,
       int offset,
       mojom::BluetoothGattDBAttributeType attribute_type,
-      ValueCallback success_callback,
-      ErrorCallback error_callback);
+      ValueCallback callback);
 
   // Common code for OnCharacteristicWriteRequest and OnDescriptorWriteRequest
   // |is_prepare| is only set when a local characteristic receives a prepare
diff --git a/chrome/browser/ash/login/existing_user_controller.cc b/chrome/browser/ash/login/existing_user_controller.cc
index 3fd2a99..b143591 100644
--- a/chrome/browser/ash/login/existing_user_controller.cc
+++ b/chrome/browser/ash/login/existing_user_controller.cc
@@ -1513,8 +1513,14 @@
 void ExistingUserController::ShowError(SigninError error,
                                        const std::string& details) {
   VLOG(1) << details;
-  GetLoginDisplayHost()->GetSigninUI()->ShowSigninError(error, details,
-                                                        num_login_attempts_);
+  auto* signin_ui = GetLoginDisplayHost()->GetSigninUI();
+  if (!signin_ui) {
+    DCHECK(session_manager::SessionManager::Get()->IsInSecondaryLoginScreen());
+    // Silently ignore the error on the secondary login screen. The screen is
+    // being deprecated anyway.
+    return;
+  }
+  signin_ui->ShowSigninError(error, details, num_login_attempts_);
 }
 
 void ExistingUserController::SendAccessibilityAlert(
@@ -1614,9 +1620,8 @@
   if (status == CrosSettingsProvider::PERMANENTLY_UNTRUSTED) {
     // If the `cros_settings_` are permanently untrusted, show an error message
     // and refuse to log in.
-    GetLoginDisplayHost()->GetSigninUI()->ShowSigninError(
-        SigninError::kOwnerKeyLost, /*details=*/std::string(),
-        /*login_attempts=*/1);
+    ++num_login_attempts_;
+    ShowError(SigninError::kOwnerKeyLost, /*details=*/std::string());
 
     // Re-enable clicking on other windows and the status area. Do not start the
     // auto-login timer though. Without trusted `cros_settings_`, no auto-login
diff --git a/chrome/browser/ash/login/ui/login_display_host.h b/chrome/browser/ash/login/ui/login_display_host.h
index e4cc22a6..fe8cf23 100644
--- a/chrome/browser/ash/login/ui/login_display_host.h
+++ b/chrome/browser/ash/login/ui/login_display_host.h
@@ -222,7 +222,8 @@
   virtual void RemoveObserver(Observer* observer) = 0;
 
   // Return sign-in UI instance, guaranteed to be non-null
-  // during sign-in process. Result should not be stored.
+  // during OOBE/Login process. Returns nullptr on the secondary login screen.
+  // Result should not be stored.
   virtual SigninUI* GetSigninUI() = 0;
 
  protected:
diff --git a/chrome/browser/ash/login/ui/login_display_host_common.cc b/chrome/browser/ash/login/ui/login_display_host_common.cc
index cf80f6d..ffcceb8 100644
--- a/chrome/browser/ash/login/ui/login_display_host_common.cc
+++ b/chrome/browser/ash/login/ui/login_display_host_common.cc
@@ -401,6 +401,8 @@
 }
 
 SigninUI* LoginDisplayHostCommon::GetSigninUI() {
+  if (!GetWizardController())
+    return nullptr;
   return this;
 }
 
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
index 0e1de1b..93e3de1 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
@@ -86,7 +86,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/leveldatabase/env_chromium.h"
-#include "third_party/leveldatabase/leveldb_features.h"
 #include "third_party/re2/src/re2/re2.h"
 #include "url/gurl.h"
 
@@ -304,8 +303,7 @@
     : public BrowsingDataRemoverBrowserTestBase {
  public:
   BrowsingDataRemoverBrowserTest() {
-    std::vector<base::Feature> enabled_features = {
-        leveldb::kLevelDBRewriteFeature};
+    std::vector<base::Feature> enabled_features = {};
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
     enabled_features.push_back(media::kExternalClearKeyForTesting);
 #endif
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc
index 1a82466a..83b3ce919 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc
@@ -26,7 +26,6 @@
 #include "content/public/test/download_test_observer.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
-#include "third_party/leveldatabase/leveldb_features.h"
 #include "ui/base/models/tree_model.h"
 
 namespace {
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
index b74a8f1b..969b7c8 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
@@ -4,11 +4,13 @@
 
 #include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
 
+#include <string>
 #include <vector>
 
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/privacy_screen_dlp_helper.h"
 #include "base/bind.h"
+#include "base/check.h"
 #include "base/containers/contains.h"
 #include "base/stl_util.h"
 #include "base/syslog_logging.h"
@@ -35,6 +37,26 @@
 // a quick switch from one confidential data to another.
 const base::TimeDelta kPrivacyScreenOffDelay =
     base::TimeDelta::FromMilliseconds(500);
+
+// Reports events to `reporting_manager`.
+void ReportEvent(content::WebContents* web_contents,
+                 DlpRulesManager::Restriction restriction,
+                 DlpRulesManager::Level level,
+                 const DlpReportingManager* reporting_manager) {
+  DCHECK(reporting_manager);
+
+  DlpRulesManager* rules_manager =
+      DlpRulesManagerFactory::GetForPrimaryProfile();
+  if (!rules_manager)
+    return;
+
+  const std::string src_url = rules_manager->GetSourceUrlPattern(
+      web_contents->GetLastCommittedURL(), restriction, level);
+
+  if (restriction == DlpRulesManager::Restriction::kPrinting)
+    reporting_manager->ReportPrintingEvent(src_url, level);
+}
+
 }  // namespace
 
 static DlpContentManager* g_dlp_content_manager = nullptr;
@@ -102,10 +124,9 @@
   if (level == DlpRulesManager::Level::kBlock ||
       level == DlpRulesManager::Level::kReport) {
     SYSLOG(INFO) << "DLP blocked printing";
-
     if (reporting_manager_)
-      reporting_manager_->ReportPrintingEvent(web_contents,
-                                              DlpRulesManager::Level::kBlock);
+      ReportEvent(web_contents, DlpRulesManager::Restriction::kPrinting, level,
+                  reporting_manager_);
   }
 
   return level == DlpRulesManager::Level::kBlock;
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager_unittest.cc b/chrome/browser/chromeos/policy/dlp/dlp_content_manager_unittest.cc
index a2fde99..d7804314 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager_unittest.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager_unittest.cc
@@ -7,15 +7,22 @@
 #include <memory>
 
 #include "ash/public/cpp/privacy_screen_dlp_helper.h"
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/optional.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
+#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_content_manager_test_helper.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_histogram_helper.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_policy_event.pb.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_reporting_manager_test_helper.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_factory.h"
+#include "chrome/browser/chromeos/policy/dlp/mock_dlp_rules_manager.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/reporting/client/mock_report_queue.h"
+#include "components/user_manager/scoped_user_manager.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_renderer_host.h"
@@ -29,6 +36,10 @@
 namespace policy {
 
 namespace {
+
+constexpr char kEmailId[] = "test@example.com";
+constexpr char kGaiaId[] = "12345";
+
 const DlpContentRestrictionSet kEmptyRestrictionSet;
 const DlpContentRestrictionSet kScreenshotRestricted(
     DlpContentRestriction::kScreenshot,
@@ -49,8 +60,19 @@
 }  // namespace
 
 class DlpContentManagerTest : public testing::Test {
+ public:
+  std::unique_ptr<KeyedService> SetDlpRulesManager(
+      content::BrowserContext* context) {
+    auto dlp_rules_manager = std::make_unique<MockDlpRulesManager>();
+    mock_rules_manager_ = dlp_rules_manager.get();
+    return dlp_rules_manager;
+  }
+
  protected:
-  DlpContentManagerTest() : profile_(std::make_unique<TestingProfile>()) {}
+  DlpContentManagerTest()
+      : profile_(std::make_unique<TestingProfile>()),
+        user_manager_(new ash::FakeChromeUserManager()),
+        scoped_user_manager_(base::WrapUnique(user_manager_)) {}
   DlpContentManagerTest(const DlpContentManagerTest&) = delete;
   DlpContentManagerTest& operator=(const DlpContentManagerTest&) = delete;
   ~DlpContentManagerTest() override = default;
@@ -76,18 +98,42 @@
         std::move(report_queue));
   }
 
+  void SetupDlpRulesManager() {
+    DlpRulesManagerFactory::GetInstance()->SetTestingFactory(
+        profile(),
+        base::BindRepeating(&DlpContentManagerTest::SetDlpRulesManager,
+                            base::Unretained(this)));
+    ASSERT_TRUE(DlpRulesManagerFactory::GetForPrimaryProfile());
+  }
+
+  void LoginFakeUser() {
+    AccountId account_id = AccountId::FromUserEmailGaiaId(kEmailId, kGaiaId);
+    profile_->SetIsNewProfile(true);
+    user_manager::User* user =
+        user_manager_->AddUserWithAffiliationAndTypeAndProfile(
+            account_id, false /*is_affiliated*/,
+            user_manager::USER_TYPE_REGULAR, profile());
+    user_manager_->UserLoggedIn(account_id, user->username_hash(),
+                                false /* browser_restart */,
+                                false /* is_child */);
+  }
+
   DlpContentManager* GetManager() { return helper_.GetContentManager(); }
 
+  TestingProfile* profile() { return profile_.get(); }
+
   DlpContentManagerTestHelper helper_;
   content::BrowserTaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   base::HistogramTester histogram_tester_;
-
   std::vector<DlpPolicyEvent> events_;
+  MockDlpRulesManager* mock_rules_manager_ = nullptr;
 
  private:
   content::RenderViewHostTestEnabler rvh_test_enabler_;
   const std::unique_ptr<TestingProfile> profile_;
+  ash::FakeChromeUserManager* user_manager_;
+  user_manager::ScopedUserManager scoped_user_manager_;
 };
 
 TEST_F(DlpContentManagerTest, NoConfidentialDataShown) {
@@ -261,6 +307,8 @@
 }
 
 TEST_F(DlpContentManagerTest, PrintingRestricted) {
+  LoginFakeUser();
+
   std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
   EXPECT_EQ(GetManager()->GetConfidentialRestrictions(web_contents.get()),
             kEmptyRestrictionSet);
@@ -272,6 +320,12 @@
       GetDlpHistogramPrefix() + dlp::kPrintingBlockedUMA, false, 1);
 
   SetReportQueueForReportingManager();
+  SetupDlpRulesManager();
+  const std::string src_pattern("example.com");
+  EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern)
+      .Times(1)
+      .WillOnce(::testing::Return(src_pattern));
+
   helper_.ChangeConfidentiality(web_contents.get(), kPrintingRestricted);
   EXPECT_EQ(GetManager()->GetConfidentialRestrictions(web_contents.get()),
             kPrintingRestricted);
@@ -280,9 +334,10 @@
       GetDlpHistogramPrefix() + dlp::kPrintingBlockedUMA, true, 1);
   histogram_tester_.ExpectBucketCount(
       GetDlpHistogramPrefix() + dlp::kPrintingBlockedUMA, false, 1);
-  EXPECT_THAT(
-      events_[0],
-      IsDlpPolicyEvent(CreatePrintingRestrictedDlpEvent(web_contents.get())));
+
+  EXPECT_EQ(events_.size(), 1u);
+  EXPECT_THAT(events_[0],
+              IsDlpPolicyEvent(CreatePrintingRestrictedDlpEvent(src_pattern)));
 
   helper_.DestroyWebContents(web_contents.get());
   EXPECT_EQ(GetManager()->GetConfidentialRestrictions(web_contents.get()),
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.cc b/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.cc
index 656ab83..445bec2e 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.cc
@@ -16,7 +16,6 @@
 #include "components/reporting/client/report_queue.h"
 #include "components/reporting/util/status.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/web_contents.h"
 #include "url/gurl.h"
 
 namespace policy {
@@ -54,13 +53,13 @@
   }
 }
 
-DlpPolicyEvent* CreateDlpPolicyEvent(content::WebContents* source,
+DlpPolicyEvent* CreateDlpPolicyEvent(const std::string& src_pattern,
                                      DlpRulesManager::Level level,
                                      DlpRulesManager::Restriction restriction) {
   DlpPolicyEvent* event = new DlpPolicyEvent();
 
   DlpPolicyEventSource* event_source = new DlpPolicyEventSource();
-  event_source->set_url(source->GetURL().spec());
+  event_source->set_url(src_pattern);
   event->set_allocated_source(event_source);
 
   // TODO(1187479, marcgrimme): add proper destination as soon as available
@@ -94,7 +93,7 @@
 }
 
 void DlpReportingManager::ReportPrintingEvent(
-    content::WebContents* web_contents,
+    const std::string& src_pattern,
     DlpRulesManager::Level level) const {
   // TODO(1187506, marcgrimme) Refactor to handle gracefully with user
   // interaction when queue is not ready.
@@ -106,7 +105,7 @@
   reporting::ReportQueue::EnqueueCallback callback = base::BindOnce(
       &DlpReportingManager::OnEventEnqueued, base::Unretained(this));
   report_queue_->Enqueue(
-      CreateDlpPolicyEvent(web_contents, level,
+      CreateDlpPolicyEvent(src_pattern, level,
                            DlpRulesManager::Restriction::kPrinting),
       reporting::Priority::IMMEDIATE, std::move(callback));
 }
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.h b/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.h
index 997550e..cc7d065 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.h
@@ -11,16 +11,12 @@
 #include "components/reporting/client/report_queue.h"
 #include "components/reporting/util/statusor.h"
 
-namespace content {
-class WebContents;
-}  // namespace content
-
 class DlpPolicyEvent;
 
 namespace policy {
 // helper function to create DlpPolicyEvents to be enqueued or used to test
 // against.
-DlpPolicyEvent* CreateDlpPolicyEvent(content::WebContents* source,
+DlpPolicyEvent* CreateDlpPolicyEvent(const std::string& src_pattern,
                                      DlpRulesManager::Level level,
                                      DlpRulesManager::Restriction restriction);
 
@@ -41,7 +37,7 @@
 
   // The different methods that cause report events from the specific
   // restrictions.
-  void ReportPrintingEvent(content::WebContents* contents,
+  void ReportPrintingEvent(const std::string& src_pattern,
                            DlpRulesManager::Level level) const;
 
   ReportQueueSetterCallback GetReportQueueSetter();
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager_test_helper.cc b/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager_test_helper.cc
index f36c7f8..3facb2c 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager_test_helper.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager_test_helper.cc
@@ -9,7 +9,6 @@
 #include "chrome/browser/chromeos/policy/dlp/dlp_policy_event.pb.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
-#include "content/public/browser/web_contents.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 using ::testing::Matcher;
@@ -44,8 +43,8 @@
 }
 
 DlpPolicyEvent CreatePrintingRestrictedDlpEvent(
-    content::WebContents* contents) {
+    const std::string& src_pattern) {
   return *policy::CreateDlpPolicyEvent(
-      contents, policy::DlpRulesManager::Level::kBlock,
+      src_pattern, policy::DlpRulesManager::Level::kBlock,
       policy::DlpRulesManager::Restriction::kPrinting);
 }
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager_test_helper.h b/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager_test_helper.h
index 57bbd30..731063cf1 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager_test_helper.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager_test_helper.h
@@ -7,14 +7,10 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace content {
-class WebContents;
-}  // namespace content
-
 class DlpPolicyEvent;
 
 ::testing::Matcher<const DlpPolicyEvent&> IsDlpPolicyEvent(
     const DlpPolicyEvent& event);
-DlpPolicyEvent CreatePrintingRestrictedDlpEvent(content::WebContents* contents);
+DlpPolicyEvent CreatePrintingRestrictedDlpEvent(const std::string& src_pattern);
 
 #endif  // CHROME_BROWSER_CHROMEOS_POLICY_DLP_DLP_REPORTING_MANAGER_TEST_HELPER_H_
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager_unittest.cc b/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager_unittest.cc
index cccc2dce..a63dbe59 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager_unittest.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_reporting_manager_unittest.cc
@@ -64,11 +64,10 @@
 
 TEST_F(DlpReportingManagerTest, IsPrintingRestricted) {
   std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
-  manager_.ReportPrintingEvent(web_contents.get(),
-                               DlpRulesManager::Level::kBlock);
+  auto src_pattern = web_contents->GetLastCommittedURL().spec();
+  manager_.ReportPrintingEvent(src_pattern, DlpRulesManager::Level::kBlock);
 
-  EXPECT_THAT(
-      events_[0],
-      IsDlpPolicyEvent(CreatePrintingRestrictedDlpEvent(web_contents.get())));
+  EXPECT_THAT(events_[0],
+              IsDlpPolicyEvent(CreatePrintingRestrictedDlpEvent(src_pattern)));
 }
 }  // namespace policy
diff --git a/chrome/browser/component_updater/registration.cc b/chrome/browser/component_updater/registration.cc
index 71e027c..9af6c2f 100644
--- a/chrome/browser/component_updater/registration.cc
+++ b/chrome/browser/component_updater/registration.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/component_updater/sth_set_component_remover.h"
 #include "chrome/browser/component_updater/subresource_filter_component_installer.h"
 #include "chrome/browser/component_updater/trust_token_key_commitments_component_installer.h"
-#include "chrome/browser/component_updater/zxcvbn_data_component_installer.h"
 #include "chrome/common/buildflags.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/pref_names.h"
@@ -56,6 +55,7 @@
 
 #if !defined(OS_ANDROID)
 #include "chrome/browser/component_updater/soda_component_installer.h"
+#include "chrome/browser/component_updater/zxcvbn_data_component_installer.h"
 #include "chrome/browser/resource_coordinator/tab_manager.h"
 #include "media/base/media_switches.h"
 #endif
@@ -179,7 +179,9 @@
   RegisterHyphenationComponent(cus);
 #endif
 
+#if !defined(OS_ANDROID)
   RegisterZxcvbnDataComponent(cus);
+#endif  // !defined(OS_ANDROID)
 
   RegisterAutofillStatesComponent(cus, g_browser_process->local_state());
 
diff --git a/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest.cc b/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest.cc
index b43678d..7868c6d 100644
--- a/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest.cc
+++ b/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest.cc
@@ -224,6 +224,12 @@
   std::move(std::get<k>(args)).Run(p0);
 }
 
+ACTION_TEMPLATE(InvokeCallbackArgument,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_2_VALUE_PARAMS(p0, p1)) {
+  std::move(std::get<k>(args)).Run(p0, p1);
+}
+
 ACTION_TEMPLATE(InvokeCallbackWithScopedPtrArg,
                 HAS_2_TEMPLATE_PARAMS(int, k, typename, T),
                 AND_1_VALUE_PARAMS(p0)) {
@@ -703,11 +709,12 @@
       .WillRepeatedly(Return(chrc0_.get()));
 
   std::vector<uint8_t> value;
-  EXPECT_CALL(*chrc0_, ReadRemoteCharacteristic_(_, _))
+  EXPECT_CALL(*chrc0_, ReadRemoteCharacteristic_(_))
       .Times(2)
-      .WillOnce(InvokeCallbackArgument<1>(
-          BluetoothRemoteGattService::GATT_ERROR_FAILED))
-      .WillOnce(InvokeCallbackArgument<0>(value));
+      .WillOnce(InvokeCallbackArgument<0>(
+          BluetoothRemoteGattService::GATT_ERROR_FAILED,
+          /*value=*/std::vector<uint8_t>()))
+      .WillOnce(InvokeCallbackArgument<0>(base::nullopt, value));
 
   ExtensionTestMessageListener listener("ready", true);
   listener.set_failure_message("fail");
@@ -968,23 +975,30 @@
       .WillRepeatedly(Return(desc0_.get()));
 
   std::vector<uint8_t> value;
-  EXPECT_CALL(*desc0_, ReadRemoteDescriptor_(_, _))
+  EXPECT_CALL(*desc0_, ReadRemoteDescriptor_(_))
       .Times(8)
-      .WillOnce(InvokeCallbackArgument<1>(
-          BluetoothRemoteGattService::GATT_ERROR_FAILED))
-      .WillOnce(InvokeCallbackArgument<1>(
-          BluetoothRemoteGattService::GATT_ERROR_INVALID_LENGTH))
-      .WillOnce(InvokeCallbackArgument<1>(
-          BluetoothRemoteGattService::GATT_ERROR_NOT_PERMITTED))
-      .WillOnce(InvokeCallbackArgument<1>(
-          BluetoothRemoteGattService::GATT_ERROR_NOT_AUTHORIZED))
-      .WillOnce(InvokeCallbackArgument<1>(
-          BluetoothRemoteGattService::GATT_ERROR_NOT_PAIRED))
-      .WillOnce(InvokeCallbackArgument<1>(
-          BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED))
-      .WillOnce(InvokeCallbackArgument<1>(
-          BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS))
-      .WillOnce(InvokeCallbackArgument<0>(value));
+      .WillOnce(InvokeCallbackArgument<0>(
+          BluetoothRemoteGattService::GATT_ERROR_FAILED,
+          /*value=*/std::vector<uint8_t>()))
+      .WillOnce(InvokeCallbackArgument<0>(
+          BluetoothRemoteGattService::GATT_ERROR_INVALID_LENGTH,
+          /*value=*/std::vector<uint8_t>()))
+      .WillOnce(InvokeCallbackArgument<0>(
+          BluetoothRemoteGattService::GATT_ERROR_NOT_PERMITTED,
+          /*value=*/std::vector<uint8_t>()))
+      .WillOnce(InvokeCallbackArgument<0>(
+          BluetoothRemoteGattService::GATT_ERROR_NOT_AUTHORIZED,
+          /*value=*/std::vector<uint8_t>()))
+      .WillOnce(InvokeCallbackArgument<0>(
+          BluetoothRemoteGattService::GATT_ERROR_NOT_PAIRED,
+          /*value=*/std::vector<uint8_t>()))
+      .WillOnce(InvokeCallbackArgument<0>(
+          BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED,
+          /*value=*/std::vector<uint8_t>()))
+      .WillOnce(InvokeCallbackArgument<0>(
+          BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS,
+          /*value=*/std::vector<uint8_t>()))
+      .WillOnce(InvokeCallbackArgument<0>(/*error_code=*/base::nullopt, value));
 
   ExtensionTestMessageListener listener("ready", true);
   listener.set_failure_message("fail");
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index efeec04..2b552805 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -355,6 +355,8 @@
   // Search page.
   (*s_allowlist)[DefaultSearchManager::kDefaultSearchProviderDataPrefName] =
       settings_api::PrefType::PREF_TYPE_DICTIONARY;
+  (*s_allowlist)[::omnibox::kKeywordSpaceTriggeringEnabled] =
+      settings_api::PrefType::PREF_TYPE_BOOLEAN;
 
   // Site Settings prefs.
   (*s_allowlist)[::content_settings::kCookiePrimarySetting] =
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 6c6d42f..9ed327a 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3781,11 +3781,6 @@
     "expiry_milestone": 88
   },
   {
-    "name": "offline-indicator-always-http-probe",
-    "owners": [ "sclittle", "srsudar", "offline-dev" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "offline-indicator-choice",
     "owners": [ "sclittle", "srsudar", "offline-dev" ],
     "expiry_milestone": 76
@@ -3921,6 +3916,11 @@
     "expiry_milestone": 99
   },
   {
+    "name": "omnibox-keyword-space-triggering-setting",
+    "owners": [ "yoangela", "chrome-omnibox-team@google.com" ],
+    "expiry_milestone": 99
+  },
+  {
     "name": "omnibox-local-history-zero-suggest",
     "owners": ["rkgibson@google.com", "stkhapugin", "chrome-omnibox-team@google.com"],
     "expiry_milestone": 92
@@ -4081,11 +4081,6 @@
     "expiry_milestone": 89
   },
   {
-    "name": "omnibox-ui-swap-title-and-url",
-    "owners": [ "tommycli", "chrome-omnibox-team@google.com" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "omnibox-webui-omnibox-popup",
     "owners": [ "tommycli", "chrome-omnibox-team@google.com" ],
     "expiry_milestone": 99
@@ -4545,11 +4540,6 @@
     "expiry_milestone": 89
   },
   {
-    "name": "rewrite-leveldb-on-deletion",
-    "owners": [ "dullweber" ],
-    "expiry_milestone": 75
-  },
-  {
     "name": "run-video-capture-service-in-browser",
     "owners": [ "agpalak", "guidou" ],
     "expiry_milestone": 95
@@ -4914,11 +4904,6 @@
     "expiry_milestone": 94
   },
   {
-    "name": "stop-in-background",
-    "owners": [ "chrome-catan@google.com" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "storage-access-api",
     "owners": [ "brandm@microsoft.com" ],
     "expiry_milestone": 90
@@ -5055,11 +5040,6 @@
     "expiry_milestone": -1
   },
   {
-    "name": "touch-events",
-    "owners": [ "eirage", "input-dev" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "touch-selection-strategy",
     "owners": [ "omrilio" ],
     "expiry_milestone": 80
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 4cc1628..e7dc905 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1712,6 +1712,12 @@
     "Controls whether keyword mode can be triggered by space, double space, or "
     "neither.";
 
+const char kOmniboxKeywordSpaceTriggeringSettingName[] =
+    "Omnibox Keyword Space Triggering Setting";
+const char kOmniboxKeywordSpaceTriggeringSettingDescription[] =
+    "Adds a setting to the search engines setting page to control whether "
+    "spacebar activates keyword mode.";
+
 const char kOmniboxMostVisitedTilesName[] = "Omnibox Most Visited Tiles";
 const char kOmniboxMostVisitedTilesDescription[] =
     "Display a list of frquently visited pages from history as a single row "
@@ -1858,11 +1864,6 @@
     "Google head non personalized search suggestions provided by a compact on "
     "device model for non-incognito";
 
-const char kOmniboxUISwapTitleAndUrlName[] = "Omnibox UI Swap Title and URL";
-const char kOmniboxUISwapTitleAndUrlDescription[] =
-    "In the omnibox dropdown, shows titles before URLs when both are "
-    "available.";
-
 const char kOmniboxWebUIOmniboxPopupName[] = "WebUI Omnibox Popup";
 const char kOmniboxWebUIOmniboxPopupDescription[] =
     "If enabled, uses WebUI to render the omnibox suggestions popup, similar "
@@ -2114,11 +2115,6 @@
     "Enables recording additional web app related debugging data to be "
     "displayed in: chrome://internals/web-app";
 
-const char kRewriteLevelDBOnDeletionName[] =
-    "Rewrite LevelDB instances after full deletions";
-const char kRewriteLevelDBOnDeletionDescription[] =
-    "Rewrite LevelDB instances to remove traces of deleted data from disk.";
-
 const char kRestrictGamepadAccessName[] = "Restrict gamepad access";
 const char kRestrictGamepadAccessDescription[] =
     "Enables Permissions Policy and Secure Context restrictions on the Gamepad "
@@ -2348,11 +2344,6 @@
     "Controls whether site isolation should use origins instead of scheme and "
     "eTLD+1.";
 
-const char kStopInBackgroundName[] = "Stop in background";
-const char kStopInBackgroundDescription[] =
-    "Stop scheduler task queues, in the background, "
-    " after a grace period.";
-
 const char kStorageAccessAPIName[] = "Storage Access API";
 const char kStorageAccessAPIDescription[] =
     "Enables the Storage Access API, allowing websites to request storage "
@@ -3128,12 +3119,6 @@
 extern const char kMessagesForAndroidSaveCardDescription[] =
     "When enabled, save card infobars will use the new Messages UI.";
 
-const char kOfflineIndicatorAlwaysHttpProbeName[] = "Always http probe";
-const char kOfflineIndicatorAlwaysHttpProbeDescription[] =
-    "Always do http probe to detect network connectivity for offline indicator "
-    "as opposed to just taking the connection state from the system."
-    "Used for testing.";
-
 const char kOfflineIndicatorChoiceName[] = "Offline indicator choices";
 const char kOfflineIndicatorChoiceDescription[] =
     "Show an offline indicator while offline.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 41ce68f..8fb225a 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -992,6 +992,9 @@
 extern const char kOmniboxKeywordSpaceTriggeringName[];
 extern const char kOmniboxKeywordSpaceTriggeringDescription[];
 
+extern const char kOmniboxKeywordSpaceTriggeringSettingName[];
+extern const char kOmniboxKeywordSpaceTriggeringSettingDescription[];
+
 extern const char kOmniboxExperimentalSuggestScoringName[];
 extern const char kOmniboxExperimentalSuggestScoringDescription[];
 
@@ -1068,9 +1071,6 @@
 extern const char kOmniboxOnDeviceHeadSuggestionsNonIncognitoName[];
 extern const char kOmniboxOnDeviceHeadSuggestionsNonIncognitoDescription[];
 
-extern const char kOmniboxUISwapTitleAndUrlName[];
-extern const char kOmniboxUISwapTitleAndUrlDescription[];
-
 extern const char kOmniboxWebUIOmniboxPopupName[];
 extern const char kOmniboxWebUIOmniboxPopupDescription[];
 
@@ -1223,9 +1223,6 @@
 extern const char kRecordWebAppDebugInfoName[];
 extern const char kRecordWebAppDebugInfoDescription[];
 
-extern const char kRewriteLevelDBOnDeletionName[];
-extern const char kRewriteLevelDBOnDeletionDescription[];
-
 extern const char kRestrictGamepadAccessName[];
 extern const char kRestrictGamepadAccessDescription[];
 
@@ -1353,9 +1350,6 @@
 extern const char kSplitCacheByNetworkIsolationKeyName[];
 extern const char kSplitCacheByNetworkIsolationKeyDescription[];
 
-extern const char kStopInBackgroundName[];
-extern const char kStopInBackgroundDescription[];
-
 extern const char kStoragePressureEventName[];
 extern const char kStoragePressureEventDescription[];
 
@@ -1819,9 +1813,6 @@
 extern const char kMessagesForAndroidSaveCardName[];
 extern const char kMessagesForAndroidSaveCardDescription[];
 
-extern const char kOfflineIndicatorAlwaysHttpProbeName[];
-extern const char kOfflineIndicatorAlwaysHttpProbeDescription[];
-
 extern const char kOfflineIndicatorChoiceName[];
 extern const char kOfflineIndicatorChoiceDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 28b8602..bc9d3aa 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -277,7 +277,6 @@
     &language::kTranslateIntent,
     &messages::kMessagesForAndroidInfrastructure,
     &offline_pages::kOfflineIndicatorFeature,
-    &offline_pages::kOfflineIndicatorAlwaysHttpProbeFeature,
     &offline_pages::kOfflinePagesCTFeature,    // See crbug.com/620421.
     &offline_pages::kOfflinePagesCTV2Feature,  // See crbug.com/734753.
     &offline_pages::kOfflinePagesDescriptiveFailStatusFeature,
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index c455ddd..5a977e6 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -378,8 +378,6 @@
     public static final String METRICS_SETTINGS_ANDROID = "MetricsSettingsAndroid";
     public static final String NOTIFICATION_SUSPENDER = "NotificationSuspender";
     public static final String OFFLINE_INDICATOR = "OfflineIndicator";
-    public static final String OFFLINE_INDICATOR_ALWAYS_HTTP_PROBE =
-            "OfflineIndicatorAlwaysHttpProbe";
     public static final String OFFLINE_INDICATOR_V2 = "OfflineIndicatorV2";
     public static final String OFFLINE_MEASUREMENTS_BACKGROUND_TASK =
             "OfflineMeasurementsBackgroundTask";
diff --git a/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc b/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc
index 43ed012..80ad5c1 100644
--- a/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc
+++ b/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc
@@ -47,8 +47,16 @@
 
   const std::string* str_priority_result =
       sequencing_information.FindStringKey("priority");
+  if (!str_priority_result) {
+    LOG(ERROR) << "Field priority is missing from SequencingInformation: "
+               << sequencing_information;
+    return base::nullopt;
+  }
+
   Priority priority;
   if (!Priority_Parse(*str_priority_result, &priority)) {
+    LOG(ERROR) << "Unable to parse field priority in SequencingInformation: "
+               << sequencing_information;
     return base::nullopt;
   }
   return priority;
diff --git a/chrome/browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc b/chrome/browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc
index 6ea2e69..03e10b8c 100644
--- a/chrome/browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc
+++ b/chrome/browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc
@@ -130,12 +130,11 @@
 // Immitates the server response for successful record upload. Since additional
 // steps and tests require the response from the server to be accurate, ASSERTS
 // that the |request| must be valid, and on a valid request updates |response|.
-void SucceedResponseFromRequest(const base::Value& request,
-                                bool force_confirm_by_server,
-                                base::Value& response) {
-  base::Value seq_info{base::Value::Type::DICTIONARY};
-  RetrieveFinalSequencingInformation(request, seq_info);
-  response.SetPath("lastSucceedUploadedRecord", std::move(seq_info));
+void SucceedResponseFromRequestHelper(const base::Value& request,
+                                      bool force_confirm_by_server,
+                                      base::Value& response,
+                                      base::Value& sequencing_info) {
+  RetrieveFinalSequencingInformation(request, sequencing_info);
 
   // If force_confirm is true, process that.
   if (force_confirm_by_server) {
@@ -150,6 +149,38 @@
   }
 }
 
+void SucceedResponseFromRequest(const base::Value& request,
+                                bool force_confirm_by_server,
+                                base::Value& response) {
+  base::Value sequencing_info{base::Value::Type::DICTIONARY};
+  SucceedResponseFromRequestHelper(request, force_confirm_by_server, response,
+                                   sequencing_info);
+  response.SetPath("lastSucceedUploadedRecord", std::move(sequencing_info));
+}
+
+void SucceedResponseFromRequestMissingPriority(const base::Value& request,
+                                               bool force_confirm_by_server,
+                                               base::Value& response) {
+  base::Value sequencing_info{base::Value::Type::DICTIONARY};
+  SucceedResponseFromRequestHelper(request, force_confirm_by_server, response,
+                                   sequencing_info);
+  // Remove priority field.
+  sequencing_info.RemoveKey("priority");
+  response.SetPath("lastSucceedUploadedRecord", std::move(sequencing_info));
+}
+
+void SucceedResponseFromRequestInvalidPriority(const base::Value& request,
+                                               bool force_confirm_by_server,
+                                               base::Value& response) {
+  base::Value sequencing_info{base::Value::Type::DICTIONARY};
+  SucceedResponseFromRequestHelper(request, force_confirm_by_server, response,
+                                   sequencing_info);
+  sequencing_info.RemoveKey("priority");
+  // Set priority field to an invalid value.
+  sequencing_info.SetStringKey("priority", "abc");
+  response.SetPath("lastSucceedUploadedRecord", std::move(sequencing_info));
+}
+
 // Immitates the server response for failed record upload. Since additional
 // steps and tests require the response from the server to be accurate, ASSERTS
 // that the |request| must be valid, and on a valid request updates |response|.
@@ -264,6 +295,64 @@
   EXPECT_THAT(response, ResponseEquals(expected_response));
 }
 
+TEST_P(RecordHandlerImplTest, MissingPriorityField) {
+  static constexpr int64_t kNumTestRecords = 10;
+  static constexpr int64_t kGenerationId = 1234;
+  auto test_records = BuildTestRecordsVector(kNumTestRecords, kGenerationId);
+  const auto force_confirm_by_server = force_confirm();
+
+  EXPECT_CALL(*client_, UploadEncryptedReport(_, _, _))
+      .WillOnce(WithArgs<0, 2>(
+          Invoke([&force_confirm_by_server](
+                     base::Value request,
+                     policy::CloudPolicyClient::ResponseCallback callback) {
+            base::Value response{base::Value::Type::DICTIONARY};
+            SucceedResponseFromRequestMissingPriority(
+                request, force_confirm_by_server, response);
+            std::move(callback).Run(std::move(response));
+          })));
+
+  test::TestEvent<SignedEncryptionInfo> encryption_key_attached_event;
+  test::TestEvent<DmServerUploadService::CompletionResponse> responder_event;
+
+  RecordHandlerImpl handler(client_.get());
+  handler.HandleRecords(need_encryption_key(), std::move(test_records),
+                        responder_event.cb(),
+                        encryption_key_attached_event.cb());
+
+  auto response = responder_event.result();
+  EXPECT_EQ(response.status().error_code(), error::INTERNAL);
+}
+
+TEST_P(RecordHandlerImplTest, InvalidPriorityField) {
+  static constexpr int64_t kNumTestRecords = 10;
+  static constexpr int64_t kGenerationId = 1234;
+  auto test_records = BuildTestRecordsVector(kNumTestRecords, kGenerationId);
+  const auto force_confirm_by_server = force_confirm();
+
+  EXPECT_CALL(*client_, UploadEncryptedReport(_, _, _))
+      .WillOnce(WithArgs<0, 2>(
+          Invoke([&force_confirm_by_server](
+                     base::Value request,
+                     policy::CloudPolicyClient::ResponseCallback callback) {
+            base::Value response{base::Value::Type::DICTIONARY};
+            SucceedResponseFromRequestInvalidPriority(
+                request, force_confirm_by_server, response);
+            std::move(callback).Run(std::move(response));
+          })));
+
+  test::TestEvent<SignedEncryptionInfo> encryption_key_attached_event;
+  test::TestEvent<DmServerUploadService::CompletionResponse> responder_event;
+
+  RecordHandlerImpl handler(client_.get());
+  handler.HandleRecords(need_encryption_key(), std::move(test_records),
+                        responder_event.cb(),
+                        encryption_key_attached_event.cb());
+
+  auto response = responder_event.result();
+  EXPECT_EQ(response.status().error_code(), error::INTERNAL);
+}
+
 TEST_P(RecordHandlerImplTest, ReportsUploadFailure) {
   static constexpr int64_t kNumTestRecords = 10;
   static constexpr int64_t kGenerationId = 1234;
diff --git a/chrome/browser/resources/chromeos/login/test_api/README.md b/chrome/browser/resources/chromeos/login/test_api/README.md
new file mode 100644
index 0000000..653c262
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/test_api/README.md
@@ -0,0 +1,13 @@
+# OOBE Test API
+
+This directory contains is a API for automating OOBE and login process in
+various tests.
+
+The following tests use this API:
+* Catapult telemetry tests, located in Chromium under `third_party/catapult`,
+  mostly `third_party/catapult/telemetry/telemetry/internal/backends/chrome/oobe.py`.
+* Autotests, located in ChromiumOS under `src/third_party/autotests`, mostly
+  `src/third_party/autotest/files/client/common_lib/cros/enrollment.py` and
+  files under `src/third_party/autotest/files/client/site_tests/`.
+* Tast tests, located in ChromiumOS under `src/platform/tast-tests/`, mostly
+  under `src/platform/tast-tests/src/chromiumos/tast/local/bundles/cros/`.
diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js
index e83c3d79..84a4a08d 100644
--- a/chrome/browser/resources/print_preview/data/destination_store.js
+++ b/chrome/browser/resources/print_preview/data/destination_store.js
@@ -217,12 +217,6 @@
     /** @private {boolean} */
     this.initialDestinationSelected_ = false;
 
-    /** @private {boolean} */
-    this.initialized_ = false;
-
-    /** @private {boolean} */
-    this.readyToReloadCookieDestinations_ = false;
-
     /**
      * Maps user account to the list of origins for which destinations are
      * already loaded.
@@ -396,15 +390,15 @@
       this.createLocalDrivePrintDestination_();
     }
     // </if>
+    // <if expr="not chromeos">
+    // Start cloud printers, so that we can determine whether to show Drive.
+    this.startLoadCloudDestinations();
+    // </if>
 
     // Nothing recent, no system default ==> try to get a fallback printer as
     // destinationsInserted_ may never be called.
     if (this.typesToSearch_.size === 0) {
       this.tryToSelectInitialDestination_();
-      this.initialized_ = true;
-      if (this.readyToReloadCookieDestinations_) {
-        this.reloadUserCookieBasedDestinations(this.activeUser_);
-      }
       return;
     }
 
@@ -418,10 +412,6 @@
         this.startLoadDestinations_(printerType);
       }
     }
-    this.initialized_ = true;
-    if (this.readyToReloadCookieDestinations_) {
-      this.reloadUserCookieBasedDestinations(this.activeUser_);
-    }
   }
 
   /** @private */
@@ -834,11 +824,6 @@
    * @param {string} account
    */
   reloadUserCookieBasedDestinations(account) {
-    if (!this.initialized_) {
-      this.readyToReloadCookieDestinations_ = true;
-      return;
-    }
-
     const origins = this.loadedCloudOrigins_.get(account) || [];
     if (origins.includes(DestinationOrigin.COOKIES)) {
       this.dispatchEvent(
@@ -1164,13 +1149,15 @@
     const payload = event.detail;
     const searchingCloudPrintersDone =
         this.typesToSearch_.has(PrinterType.CLOUD_PRINTER) &&
-        !this.cloudPrintInterface_.isCloudDestinationSearchInProgress();
+        !this.cloudPrintInterface_.isCloudDestinationSearchInProgress() &&
+        !!payload.user;
     if (searchingCloudPrintersDone) {
       this.typesToSearch_.delete(PrinterType.CLOUD_PRINTER);
     }
     if (payload.printers && payload.printers.length > 0) {
       this.insertDestinations_(payload.printers);
-    } else if (searchingCloudPrintersDone) {
+    }
+    if (searchingCloudPrintersDone) {
       this.tryToSelectInitialDestination_();
     }
     if (payload.searchDone) {
diff --git a/chrome/browser/resources/print_preview/data/user_manager.js b/chrome/browser/resources/print_preview/data/user_manager.js
index 844f5e6..9826659 100644
--- a/chrome/browser/resources/print_preview/data/user_manager.js
+++ b/chrome/browser/resources/print_preview/data/user_manager.js
@@ -69,37 +69,18 @@
     this.initialized_ = false;
   },
 
-  /**
-   * @param {?Array<string>} userAccounts
-   * @param {boolean} syncAvailable
-   */
-  initUserAccounts(userAccounts, syncAvailable) {
+  initUserAccounts() {
     assert(!this.initialized_);
     this.initialized_ = true;
 
-    if (!userAccounts) {
-      assert(this.cloudPrintDisabled);
-      this.activeUser = '';
+    if (this.cloudPrintDisabled) {
       return;
     }
 
-    // If cloud print is enabled, listen for account changes.
-    assert(!this.cloudPrintDisabled);
-    if (syncAvailable) {
-      this.addWebUIListener(
-          'user-accounts-updated', this.updateUsers_.bind(this));
-      this.updateUsers_(userAccounts);
-    } else {
-      // Request the cookies destinations from the Google Cloud Print server
-      // directly. We have to do this in incognito mode in order to get the
-      // user's login state.
+    this.addWebUIListener('check-for-account-update', () => {
       this.destinationStore.startLoadCloudDestinations(
           DestinationOrigin.COOKIES);
-      this.addWebUIListener('check-for-account-update', () => {
-        this.destinationStore.startLoadCloudDestinations(
-            DestinationOrigin.COOKIES);
-      });
-    }
+    });
   },
 
   /** @private */
diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js
index 5d642dd..ef33d40 100644
--- a/chrome/browser/resources/print_preview/native_layer.js
+++ b/chrome/browser/resources/print_preview/native_layer.js
@@ -80,8 +80,6 @@
  *   pdfPrinterDisabled: boolean,
  *   destinationsManaged: boolean,
  *   cloudPrintURL: (string | undefined),
- *   userAccounts: (Array<string> | undefined),
- *   syncAvailable: boolean,
  *   isDriveMounted: (boolean | undefined),
  * }}
  * @see corresponding field name definitions in print_preview_handler.cc
diff --git a/chrome/browser/resources/print_preview/ui/app.js b/chrome/browser/resources/print_preview/ui/app.js
index fa32fe1..c3f930bc 100644
--- a/chrome/browser/resources/print_preview/ui/app.js
+++ b/chrome/browser/resources/print_preview/ui/app.js
@@ -338,7 +338,6 @@
       this.$.sidebar.init(
           settings.isInAppKioskMode, settings.printerName,
           settings.serializedDefaultDestinationSelectionRulesStr,
-          settings.userAccounts || null, settings.syncAvailable,
           settings.pdfPrinterDisabled, settings.isDriveMounted || false);
       this.destinationsManaged_ = settings.destinationsManaged;
       this.isInKioskAutoPrintMode_ = settings.isInKioskAutoPrintMode;
diff --git a/chrome/browser/resources/print_preview/ui/destination_settings.js b/chrome/browser/resources/print_preview/ui/destination_settings.js
index 3576776..c0b4bb2 100644
--- a/chrome/browser/resources/print_preview/ui/destination_settings.js
+++ b/chrome/browser/resources/print_preview/ui/destination_settings.js
@@ -290,14 +290,10 @@
         on Chrome OS.
    * @param {string} serializedDefaultDestinationRulesStr String with rules
    *     for selecting a default destination.
-   * @param {?Array<string>} userAccounts The signed in user accounts.
-   * @param {boolean} syncAvailable Whether sync is available. Used to
-   *     determine whether to wait for user info updates from the handler, or
-   *     to always send requests to the Google Cloud Print server.
    */
   init(
       defaultPrinter, pdfPrinterDisabled, isDriveMounted,
-      serializedDefaultDestinationRulesStr, userAccounts, syncAvailable) {
+      serializedDefaultDestinationRulesStr) {
     const cloudPrintInterface = CloudPrintInterfaceImpl.getInstance();
     this.pdfPrinterDisabled_ = pdfPrinterDisabled;
     let recentDestinations =
@@ -310,8 +306,7 @@
       this.invitationStore_ = new InvitationStore();
       this.invitationStore_.setCloudPrintInterface(cloudPrintInterface);
       beforeNextRender(this, () => {
-        this.shadowRoot.querySelector('#userManager')
-            .initUserAccounts(userAccounts, syncAvailable);
+        this.shadowRoot.querySelector('#userManager').initUserAccounts();
         recentDestinations = recentDestinations.slice(
             0, this.getRecentDestinationsDisplayCount_(recentDestinations));
         this.destinationStore_.init(
diff --git a/chrome/browser/resources/print_preview/ui/sidebar.js b/chrome/browser/resources/print_preview/ui/sidebar.js
index 1e6053e..0f04156 100644
--- a/chrome/browser/resources/print_preview/ui/sidebar.js
+++ b/chrome/browser/resources/print_preview/ui/sidebar.js
@@ -145,15 +145,13 @@
    * @param {string} defaultPrinter The system default printer ID.
    * @param {string} serializedDestinationSelectionRulesStr String with rules
    *     for selecting the default destination.
-   * @param {?Array<string>} userAccounts The signed in user accounts.
-   * @param {boolean} syncAvailable
    * @param {boolean} pdfPrinterDisabled Whether the PDF printer is disabled.
    * @param {boolean} isDriveMounted Whether Google Drive is mounted. Only used
         on Chrome OS.
    */
   init(
       appKioskMode, defaultPrinter, serializedDestinationSelectionRulesStr,
-      userAccounts, syncAvailable, pdfPrinterDisabled, isDriveMounted) {
+      pdfPrinterDisabled, isDriveMounted) {
     this.isInAppKioskMode_ = appKioskMode;
     pdfPrinterDisabled = this.isInAppKioskMode_ || pdfPrinterDisabled;
     // If PDF printing is disabled, then Save to Drive also needs to be disabled
@@ -161,7 +159,7 @@
     isDriveMounted = !pdfPrinterDisabled && isDriveMounted;
     this.$.destinationSettings.init(
         defaultPrinter, pdfPrinterDisabled, isDriveMounted,
-        serializedDestinationSelectionRulesStr, userAccounts, syncAvailable);
+        serializedDestinationSelectionRulesStr);
   },
 
   /**
diff --git a/chrome/browser/resources/settings/autofill_page/password_move_multiple_passwords_to_account_dialog.html b/chrome/browser/resources/settings/autofill_page/password_move_multiple_passwords_to_account_dialog.html
index 6061042..b5309ea 100644
--- a/chrome/browser/resources/settings/autofill_page/password_move_multiple_passwords_to_account_dialog.html
+++ b/chrome/browser/resources/settings/autofill_page/password_move_multiple_passwords_to_account_dialog.html
@@ -20,6 +20,7 @@
   <div slot="title">$i18n{passwordMovePasswordsToAccountDialogTitle}</div>
 
   <div slot="body">
+    <div>$i18n{passwordMovePasswordsToAccountDialogBodyText}</div>
     <dom-repeat id="devicePasswordList" items="[[passwordsToMove]]"
         class="cr-separators list-with-header">
       <template>
@@ -39,19 +40,17 @@
   <div slot="button-container">
     <cr-button class="cancel-button" id="cancelButton"
         on-click="onCancelButtonClick_">
-      $i18n{passwordMoveToAccountDialogCancelButtonText}
+      $i18n{passwordMoveMultiplePasswordsToAccountDialogCancelButtonText}
     </cr-button>
     <cr-button class="action-button" id="moveButton"
         on-click="onMoveButtonClick_">
-      $i18n{passwordMoveToAccountDialogMoveButtonText}
+      $i18n{passwordMoveMultiplePasswordsToAccountDialogMoveButtonText}
     </cr-button>
   </div>
   <div slot="footer" id="footer">
     <div class="cr-row two-line ">
       <settings-avatar-icon></settings-avatar-icon>
       <div class="flex cr-padded-text">
-        <div id="movingPasswordsCountLabel">
-          $i18n{passwordMovePasswordsToAccountDialogFooter}</div>
         <div class="secondary">[[accountEmail]]</div>
       </div>
     </div>
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_page.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_page.js
index 363f74d..4ab64578 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_page.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_page.js
@@ -294,7 +294,7 @@
         this.subpageType_ = OncMojo.getNetworkTypeFromString(type);
       }
 
-      if (queryParams.get('showCellularSetup') === 'true') {
+      if (!oldRoute && queryParams.get('showCellularSetup') === 'true') {
         const pageName = queryParams.get('showPsimFlow') === 'true' ?
             cellularSetup.CellularSetupPageName.PSIM_FLOW_UI :
             cellularSetup.CellularSetupPageName.ESIM_FLOW_UI;
@@ -305,7 +305,8 @@
         this.pendingShowCellularSetupDialogAttemptPageName_ = pageName;
       }
 
-      this.showSimLockDialog_ = !!queryParams.get('showSimLockDialog') &&
+      this.showSimLockDialog_ = !oldRoute &&
+          !!queryParams.get('showSimLockDialog') &&
           this.subpageType_ === mojom.NetworkType.kCellular &&
           loadTimeData.getBoolean('updatedCellularActivationUi');
     } else if (route === settings.routes.KNOWN_NETWORKS) {
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js
index b2cef125..b851b72 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js
@@ -263,6 +263,9 @@
       // |hasPin| to true. This prevents setupPinButton UI delays, except in the
       // small chance that CrOS fails to remove the quick unlock capability. See
       // https://crbug.com/1054327 for details.
+      if (!this.hasPin) {
+        return;
+      }
       this.hasPin = false;
       this.setModes.call(null, [], [], (result) => {
         assert(result, 'Failed to clear quick unlock modes');
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/OWNERS b/chrome/browser/resources/settings/clear_browsing_data_dialog/OWNERS
new file mode 100644
index 0000000..cc11485
--- /dev/null
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/OWNERS
@@ -0,0 +1 @@
+sauski@google.com
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engines_page.html b/chrome/browser/resources/settings/search_engines_page/search_engines_page.html
index eeb770e..95a7141 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engines_page.html
+++ b/chrome/browser/resources/settings/search_engines_page/search_engines_page.html
@@ -3,6 +3,29 @@
         border-top: var(--cr-separator-line);
       }
     </style>
+    <div class="cr-row first" hidden="[[!showKeywordTriggerSetting_]]">
+      <div class="flex cr-padded-text"
+          hidden="[[!showKeywordTriggerSetting_]]">
+        <h2>$i18n{searchEnginesTriggerOptions}</h2>
+        $i18n{searchEnginesTriggerDescription}
+      </div>
+    </div>
+    <div class="list-frame" hidden="[[!showKeywordTriggerSetting_]]">
+      <settings-radio-group
+          pref="{{prefs.omnibox.keyword_space_triggering_enabled}}">
+        <controlled-radio-button class="list-item" name="true"
+            pref="{{prefs.omnibox.keyword_space_triggering_enabled}}"
+            label="$i18n{searchEnginesTriggerSpaceEnabled}"
+            no-extension-indicator>
+        </controlled-radio-button>
+        <controlled-radio-button class="list-item" name="false"
+            pref="{{prefs.omnibox.keyword_space_triggering_enabled}}"
+            label="$i18n{searchEnginesTriggerSpaceDisabled}"
+            no-extension-indicator>
+        </controlled-radio-button>
+      </settings-radio-group>
+    </div>
+
     <div class="cr-row first">
       <h2>$i18n{searchEnginesDefault}</h2>
     </div>
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engines_page.js b/chrome/browser/resources/settings/search_engines_page/search_engines_page.js
index 29ab6d2..695789c 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engines_page.js
+++ b/chrome/browser/resources/settings/search_engines_page/search_engines_page.js
@@ -11,6 +11,8 @@
 import 'chrome://resources/cr_elements/shared_vars_css.m.js';
 import 'chrome://resources/js/cr.m.js';
 import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
+import '../controls/controlled_radio_button.js';
+import '../controls/settings_radio_group';
 import './search_engine_dialog.js';
 import './search_engines_list.js';
 import './omnibox_extension_entry.js';
@@ -23,6 +25,7 @@
 import {afterNextRender, html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {GlobalScrollTargetBehavior} from '../global_scroll_target_behavior.js';
+import {loadTimeData} from '../i18n_setup';
 import {routes} from '../route.js';
 
 import {SearchEngine, SearchEnginesBrowserProxyImpl, SearchEnginesInfo} from './search_engines_browser_proxy.js';
@@ -35,6 +38,14 @@
   behaviors: [GlobalScrollTargetBehavior, WebUIListenerBehavior],
 
   properties: {
+    /**
+     * Preferences state.
+     */
+    prefs: {
+      type: Object,
+      notify: true,
+    },
+
     /** @type {!Array<!SearchEngine>} */
     defaultEngines: Array,
 
@@ -106,6 +117,12 @@
       type: Boolean,
       value: false,
     },
+
+    /** @private */
+    showKeywordTriggerSetting_: {
+      type: Boolean,
+      value: () => loadTimeData.getBoolean('showKeywordTriggerSetting'),
+    },
   },
 
   // Since the iron-list for extensions is enclosed in a dom-if, observe both
diff --git a/chrome/browser/resources/settings/search_page/search_page.html b/chrome/browser/resources/settings/search_page/search_page.html
index 0304c49..f6913ff 100644
--- a/chrome/browser/resources/settings/search_page/search_page.html
+++ b/chrome/browser/resources/settings/search_page/search_page.html
@@ -60,7 +60,8 @@
         page-title="$i18n{searchEnginesManage}"
         search-label="$i18n{searchEnginesSearch}"
         search-term="{{searchEnginesFilter_}}">
-      <settings-search-engines-page filter="[[searchEnginesFilter_]]">
+      <settings-search-engines-page prefs="{{prefs}}"
+          filter="[[searchEnginesFilter_]]">
     </settings-subpage>
   </template>
 </settings-animated-pages>
diff --git a/chrome/browser/resources/welcome/BUILD.gn b/chrome/browser/resources/welcome/BUILD.gn
index 5cce5543..f0c7e59 100644
--- a/chrome/browser/resources/welcome/BUILD.gn
+++ b/chrome/browser/resources/welcome/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//chrome/common/features.gni")
-import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/grit/grit_rule.gni")
 import("//tools/grit/preprocess_if_expr.gni")
 import("//tools/polymer/html_to_js.gni")
diff --git a/chrome/browser/resources/welcome/google_apps/BUILD.gn b/chrome/browser/resources/welcome/google_apps/BUILD.gn
index 70956c9c..1dbefe6 100644
--- a/chrome/browser/resources/welcome/google_apps/BUILD.gn
+++ b/chrome/browser/resources/welcome/google_apps/BUILD.gn
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/polymer/html_to_js.gni")
 
 html_to_js("web_components") {
diff --git a/chrome/browser/resources/welcome/set_as_default/BUILD.gn b/chrome/browser/resources/welcome/set_as_default/BUILD.gn
index 6392247a..1b06844 100644
--- a/chrome/browser/resources/welcome/set_as_default/BUILD.gn
+++ b/chrome/browser/resources/welcome/set_as_default/BUILD.gn
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/polymer/html_to_js.gni")
 
 html_to_js("web_components") {
diff --git a/chrome/browser/resources/welcome/shared/BUILD.gn b/chrome/browser/resources/welcome/shared/BUILD.gn
index 5a4b26e..b147ab0 100644
--- a/chrome/browser/resources/welcome/shared/BUILD.gn
+++ b/chrome/browser/resources/welcome/shared/BUILD.gn
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/polymer/html_to_js.gni")
 
 html_to_js("web_components") {
diff --git a/chrome/browser/signin/identity_manager_factory.cc b/chrome/browser/signin/identity_manager_factory.cc
index ef00fe1..a731e396 100644
--- a/chrome/browser/signin/identity_manager_factory.cc
+++ b/chrome/browser/signin/identity_manager_factory.cc
@@ -68,7 +68,9 @@
       }));
 }
 
-IdentityManagerFactory::~IdentityManagerFactory() {}
+IdentityManagerFactory::~IdentityManagerFactory() {
+  signin::SetIdentityManagerProvider({});
+}
 
 // static
 signin::IdentityManager* IdentityManagerFactory::GetForProfile(
diff --git a/chrome/browser/signin/identity_manager_provider.cc b/chrome/browser/signin/identity_manager_provider.cc
index f492261..ca42e58 100644
--- a/chrome/browser/signin/identity_manager_provider.cc
+++ b/chrome/browser/signin/identity_manager_provider.cc
@@ -20,7 +20,13 @@
 
 void SetIdentityManagerProvider(const IdentityManagerProvider& provider) {
   IdentityManagerProvider& instance = GetIdentityManagerProvider();
-  DCHECK(!instance);
+
+  // Exactly one of `provider` or `instance` should be non-null.
+  if (provider)
+    DCHECK(!instance);
+  else
+    DCHECK(instance);
+
   instance = provider;
 }
 
diff --git a/chrome/browser/site_isolation/OWNERS b/chrome/browser/site_isolation/OWNERS
index 8dc95e0..f5534f7 100644
--- a/chrome/browser/site_isolation/OWNERS
+++ b/chrome/browser/site_isolation/OWNERS
@@ -1,4 +1,3 @@
-acolwell@chromium.org
 alexmos@chromium.org
 creis@chromium.org
 kenrb@chromium.org
diff --git a/chrome/browser/ui/ash/assistant/assistant_client_impl.cc b/chrome/browser/ui/ash/assistant/assistant_client_impl.cc
index f89b391..5e29ee0 100644
--- a/chrome/browser/ui/ash/assistant/assistant_client_impl.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_client_impl.cc
@@ -188,9 +188,9 @@
 }
 
 void AssistantClientImpl::OnUserProfileLoaded(const AccountId& account_id) {
-  if (!assistant_state_observer_.IsObservingSources() && !initialized_ &&
+  if (!assistant_state_observation_.IsObserving() && !initialized_ &&
       ash::AssistantState::Get()) {
-    assistant_state_observer_.Add(ash::AssistantState::Get());
+    assistant_state_observation_.Observe(ash::AssistantState::Get());
   }
 }
 
diff --git a/chrome/browser/ui/ash/assistant/assistant_client_impl.h b/chrome/browser/ui/ash/assistant/assistant_client_impl.h
index 7f30c27b..e255d727 100644
--- a/chrome/browser/ui/ash/assistant/assistant_client_impl.h
+++ b/chrome/browser/ui/ash/assistant/assistant_client_impl.h
@@ -11,7 +11,7 @@
 #include "ash/public/cpp/assistant/assistant_client.h"
 #include "ash/public/cpp/assistant/assistant_state.h"
 #include "base/macros.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/ui/ash/assistant/device_actions.h"
 #include "chromeos/assistant/buildflags.h"
 #include "chromeos/services/assistant/public/cpp/assistant_client.h"
@@ -116,8 +116,8 @@
   Profile* profile_ = nullptr;
   signin::IdentityManager* identity_manager_ = nullptr;
 
-  ScopedObserver<ash::AssistantStateBase, ash::AssistantStateObserver>
-      assistant_state_observer_{this};
+  base::ScopedObservation<ash::AssistantStateBase, ash::AssistantStateObserver>
+      assistant_state_observation_{this};
 
   DISALLOW_COPY_AND_ASSIGN(AssistantClientImpl);
 };
diff --git a/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc b/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc
index 9b9b97b..7b1a626 100644
--- a/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc
@@ -13,7 +13,7 @@
 #include "ash/system/message_center/unified_message_center_view.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/unified/unified_system_tray.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/strings/string_util.h"
 #include "base/test/bind.h"
 #include "base/test/icu_test_util.h"
@@ -52,8 +52,9 @@
       return;                                                                 \
     }                                                                         \
     MockMessageCenterObserver mock;                                           \
-    ScopedObserver<MessageCenter, MessageCenterObserver> observer_{&mock};    \
-    observer_.Add(MessageCenter::Get());                                      \
+    base::ScopedObservation<MessageCenter, MessageCenterObserver>             \
+        observation_{&mock};                                                  \
+    observation_.Observe(MessageCenter::Get());                               \
                                                                               \
     base::RunLoop run_loop;                                                   \
     EXPECT_CALL(mock, OnNotificationAdded)                                    \
@@ -289,8 +290,9 @@
                        ShouldTickNotificationsAtRegularIntervals) {
   // Observe notifications.
   MockMessageCenterObserver mock;
-  ScopedObserver<MessageCenter, MessageCenterObserver> scoped_observer{&mock};
-  scoped_observer.Add(MessageCenter::Get());
+  base::ScopedObservation<MessageCenter, MessageCenterObserver>
+      scoped_observation{&mock};
+  scoped_observation.Observe(MessageCenter::Get());
 
   // Show Assistant UI (once ready).
   tester()->StartAssistantAndWaitForReady();
diff --git a/chrome/browser/ui/ash/assistant/assistant_web_view_impl_browsertest.cc b/chrome/browser/ui/ash/assistant/assistant_web_view_impl_browsertest.cc
index c424a35..9f5632d 100644
--- a/chrome/browser/ui/ash/assistant/assistant_web_view_impl_browsertest.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_web_view_impl_browsertest.cc
@@ -10,7 +10,7 @@
 #include "ash/public/cpp/assistant/assistant_web_view.h"
 #include "ash/public/cpp/assistant/assistant_web_view_factory.h"
 #include "base/run_loop.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/ui/ash/assistant/assistant_test_mixin.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
@@ -37,26 +37,28 @@
 
 // Macros ----------------------------------------------------------------------
 
-#define EXPECT_PREFERRED_SIZE(web_view_, expected_preferred_size_)       \
-  {                                                                      \
-    MockViewObserver mock;                                               \
-    ScopedObserver<views::View, views::ViewObserver> observer{&mock};    \
-    observer.Add(static_cast<views::View*>(web_view_));                  \
-                                                                         \
-    base::RunLoop run_loop;                                              \
-    EXPECT_CALL(mock, OnViewPreferredSizeChanged)                        \
-        .WillOnce(testing::Invoke([&](views::View* view) {               \
-          EXPECT_EQ(expected_preferred_size_, view->GetPreferredSize()); \
-          run_loop.QuitClosure().Run();                                  \
-        }));                                                             \
-    run_loop.Run();                                                      \
+#define EXPECT_PREFERRED_SIZE(web_view_, expected_preferred_size_)         \
+  {                                                                        \
+    MockViewObserver mock;                                                 \
+    base::ScopedObservation<views::View, views::ViewObserver> observation{ \
+        &mock};                                                            \
+    observation.Observe(static_cast<views::View*>(web_view_));             \
+                                                                           \
+    base::RunLoop run_loop;                                                \
+    EXPECT_CALL(mock, OnViewPreferredSizeChanged)                          \
+        .WillOnce(testing::Invoke([&](views::View* view) {                 \
+          EXPECT_EQ(expected_preferred_size_, view->GetPreferredSize());   \
+          run_loop.QuitClosure().Run();                                    \
+        }));                                                               \
+    run_loop.Run();                                                        \
   }
 
 #define EXPECT_DID_STOP_LOADING(web_view_)                                     \
   {                                                                            \
     MockAssistantWebViewObserver mock;                                         \
-    ScopedObserver<AssistantWebView, AssistantWebView::Observer> obs{&mock};   \
-    obs.Add(web_view_);                                                        \
+    base::ScopedObservation<AssistantWebView, AssistantWebView::Observer> obs{ \
+        &mock};                                                                \
+    obs.Observe(web_view_);                                                    \
                                                                                \
     base::RunLoop run_loop;                                                    \
     EXPECT_CALL(mock, DidStopLoading).WillOnce(testing::Invoke([&run_loop]() { \
@@ -65,40 +67,42 @@
     run_loop.Run();                                                            \
   }
 
-#define EXPECT_DID_SUPPRESS_NAVIGATION(web_view_, expected_url_,             \
-                                       expected_disposition_,                \
-                                       expected_from_user_gesture_)          \
-  {                                                                          \
-    MockAssistantWebViewObserver mock;                                       \
-    ScopedObserver<AssistantWebView, AssistantWebView::Observer> obs{&mock}; \
-    obs.Add(web_view_);                                                      \
-                                                                             \
-    base::RunLoop run_loop;                                                  \
-    EXPECT_CALL(mock, DidSuppressNavigation)                                 \
-        .WillOnce(testing::Invoke([&](const GURL& url,                       \
-                                      WindowOpenDisposition disposition,     \
-                                      bool from_user_gesture) {              \
-          EXPECT_EQ(expected_url_, url);                                     \
-          EXPECT_EQ(expected_disposition_, disposition);                     \
-          EXPECT_EQ(expected_from_user_gesture_, from_user_gesture);         \
-          run_loop.QuitClosure().Run();                                      \
-        }));                                                                 \
-    run_loop.Run();                                                          \
+#define EXPECT_DID_SUPPRESS_NAVIGATION(web_view_, expected_url_,               \
+                                       expected_disposition_,                  \
+                                       expected_from_user_gesture_)            \
+  {                                                                            \
+    MockAssistantWebViewObserver mock;                                         \
+    base::ScopedObservation<AssistantWebView, AssistantWebView::Observer> obs{ \
+        &mock};                                                                \
+    obs.Observe(web_view_);                                                    \
+                                                                               \
+    base::RunLoop run_loop;                                                    \
+    EXPECT_CALL(mock, DidSuppressNavigation)                                   \
+        .WillOnce(testing::Invoke([&](const GURL& url,                         \
+                                      WindowOpenDisposition disposition,       \
+                                      bool from_user_gesture) {                \
+          EXPECT_EQ(expected_url_, url);                                       \
+          EXPECT_EQ(expected_disposition_, disposition);                       \
+          EXPECT_EQ(expected_from_user_gesture_, from_user_gesture);           \
+          run_loop.QuitClosure().Run();                                        \
+        }));                                                                   \
+    run_loop.Run();                                                            \
   }
 
-#define EXPECT_DID_CHANGE_CAN_GO_BACK(web_view_, expected_can_go_back_)      \
-  {                                                                          \
-    MockAssistantWebViewObserver mock;                                       \
-    ScopedObserver<AssistantWebView, AssistantWebView::Observer> obs{&mock}; \
-    obs.Add(web_view_);                                                      \
-                                                                             \
-    base::RunLoop run_loop;                                                  \
-    EXPECT_CALL(mock, DidChangeCanGoBack)                                    \
-        .WillOnce(testing::Invoke([&](bool can_go_back) {                    \
-          EXPECT_EQ(expected_can_go_back_, can_go_back);                     \
-          run_loop.QuitClosure().Run();                                      \
-        }));                                                                 \
-    run_loop.Run();                                                          \
+#define EXPECT_DID_CHANGE_CAN_GO_BACK(web_view_, expected_can_go_back_)        \
+  {                                                                            \
+    MockAssistantWebViewObserver mock;                                         \
+    base::ScopedObservation<AssistantWebView, AssistantWebView::Observer> obs{ \
+        &mock};                                                                \
+    obs.Observe(web_view_);                                                    \
+                                                                               \
+    base::RunLoop run_loop;                                                    \
+    EXPECT_CALL(mock, DidChangeCanGoBack)                                      \
+        .WillOnce(testing::Invoke([&](bool can_go_back) {                      \
+          EXPECT_EQ(expected_can_go_back_, can_go_back);                       \
+          run_loop.QuitClosure().Run();                                        \
+        }));                                                                   \
+    run_loop.Run();                                                            \
   }
 
 // Helpers ---------------------------------------------------------------------
diff --git a/chrome/browser/ui/ash/assistant/device_actions.cc b/chrome/browser/ui/ash/assistant/device_actions.cc
index 23e8bb1..5cb48fe 100644
--- a/chrome/browser/ui/ash/assistant/device_actions.cc
+++ b/chrome/browser/ui/ash/assistant/device_actions.cc
@@ -218,8 +218,8 @@
 
   app_list_subscribers_.AddObserver(subscriber);
 
-  if (prefs && !scoped_prefs_observer_.IsObserving(prefs))
-    scoped_prefs_observer_.Add(prefs);
+  if (prefs && !scoped_prefs_observations_.IsObservingSource(prefs))
+    scoped_prefs_observations_.AddObservation(prefs);
 }
 
 void DeviceActions::RemoveAppListEventSubscriber(
diff --git a/chrome/browser/ui/ash/assistant/device_actions.h b/chrome/browser/ui/ash/assistant/device_actions.h
index 73d1e57..4cc685c 100644
--- a/chrome/browser/ui/ash/assistant/device_actions.h
+++ b/chrome/browser/ui/ash/assistant/device_actions.h
@@ -10,7 +10,7 @@
 #include <vector>
 
 #include "ash/public/cpp/android_intent_helper.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_multi_source_observation.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/ash/assistant/device_actions_delegate.h"
 #include "chromeos/services/assistant/public/cpp/assistant_service.h"
@@ -57,8 +57,8 @@
 
   std::unique_ptr<DeviceActionsDelegate> delegate_;
 
-  ScopedObserver<ArcAppListPrefs, ArcAppListPrefs::Observer>
-      scoped_prefs_observer_{this};
+  base::ScopedMultiSourceObservation<ArcAppListPrefs, ArcAppListPrefs::Observer>
+      scoped_prefs_observations_{this};
   base::ObserverList<chromeos::assistant::AppListEventSubscriber>
       app_list_subscribers_;
   DISALLOW_COPY_AND_ASSIGN(DeviceActions);
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.cc
index b313fce4..56722815 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.cc
@@ -48,7 +48,7 @@
 }
 
 // Returns whether the item is backed by an Android file. Can be used with
-// non-finalized items.
+// non-initialized items.
 bool ItemBackedByAndroidFile(const HoldingSpaceItem* item) {
   return file_manager::util::GetAndroidFilesPath().IsParent(item->file_path());
 }
@@ -242,9 +242,9 @@
   // mounted in a reasonable amount of time. The primary goal of handling the
   // delayed volume mount is to support volumes that are mounted asynchronously
   // during the startup.
-  clear_non_finalized_items_timer_.Start(
+  clear_non_initialized_items_timer_.Start(
       FROM_HERE, base::TimeDelta::FromMinutes(1),
-      base::BindOnce(&HoldingSpaceFileSystemDelegate::ClearNonFinalizedItems,
+      base::BindOnce(&HoldingSpaceFileSystemDelegate::ClearNonInitializedItems,
                      base::Unretained(this)));
 }
 
@@ -255,20 +255,20 @@
   const bool arc_file_system_disconnected =
       IsArcFileSystemDisconnected(profile());
   for (const HoldingSpaceItem* item : items) {
-    if (item->IsFinalized()) {
+    if (item->IsInitialized()) {
       // Watch the directory containing `item`'s backing file. If the directory
       // is already being watched, this will no-op.
       AddWatchForParent(item->file_path());
       continue;
     }
 
-    // If the item has not yet been finalized, check whether it's path can be
+    // If the item has not yet been initialized, check whether it's path can be
     // resolved to a file system URL - failure to do so may indicate that the
     // volume to which it path belongs is not mounted there. If the file system
     // URL cannot be resolved, leave the item in partially initialized state.
     // Validity will be checked when the associated mount point is mounted.
     // NOTE: Items will not be kept in partially initialized state indefinitely
-    // - see `clear_non_finalized_items_timer_`.
+    // - see `clear_non_initialized_items_timer_`.
     // NOTE: This does not work well for removable devices, as all removable
     // devices have the same top level "external" mount point (media/removable),
     // so a removable device path will be successfully resolved even if the
@@ -280,8 +280,8 @@
     if (file_system_url.is_empty())
       continue;
 
-    // Defer validity checks (and finalization) for android files if the
-    // ARC file system connection is not yet ready.
+    // Defer validity checks (and initialization) for android files if the ARC
+    // file system connection is not yet ready.
     if (arc_file_system_disconnected && ItemBackedByAndroidFile(item))
       continue;
 
@@ -306,7 +306,7 @@
   AddWatchForParent(item->file_path());
 }
 
-void HoldingSpaceFileSystemDelegate::OnHoldingSpaceItemFinalized(
+void HoldingSpaceFileSystemDelegate::OnHoldingSpaceItemInitialized(
     const HoldingSpaceItem* item) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   AddWatchForParent(item->file_path());
@@ -321,7 +321,7 @@
   // Check validity of partially initialized items under the volume's mount
   // path.
   for (auto& item : model()->items()) {
-    if (item->IsFinalized())
+    if (item->IsInitialized())
       continue;
     if (!volume.mount_path().IsParent(item->file_path()))
       continue;
@@ -514,21 +514,21 @@
       },
       arc_file_system_disconnected, &invalid_paths));
 
-  std::vector<const HoldingSpaceItem*> items_to_finalize;
+  std::vector<const HoldingSpaceItem*> items_to_initialize;
   for (auto& item : model()->items()) {
-    // Defer finalization of items backed by android files if the connection to
-    // ARC file system service has been lost.
+    // Defer initialization of items backed by android files if the connection
+    // to ARC file system service has been lost.
     if (arc_file_system_disconnected && ItemBackedByAndroidFile(item.get()))
       continue;
 
-    if (!item->IsFinalized() &&
+    if (!item->IsInitialized() &&
         base::Contains(valid_paths, item->file_path())) {
-      items_to_finalize.push_back(item.get());
+      items_to_initialize.push_back(item.get());
     }
   }
 
-  for (auto* item : items_to_finalize) {
-    model()->FinalizeOrRemoveItem(
+  for (auto* item : items_to_initialize) {
+    model()->InitializeOrRemoveItem(
         item->id(),
         holding_space_util::ResolveFileSystemUrl(profile(), item->file_path()));
   }
@@ -547,11 +547,12 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   // The watch for `file_path` should only be removed if no holding space items
   // exist in the model which are backed by files it directly parents.
-  const bool remove_watch = std::none_of(
-      model()->items().begin(), model()->items().end(),
-      [&file_path](const auto& item) {
-        return item->IsFinalized() && item->file_path().DirName() == file_path;
-      });
+  const bool remove_watch =
+      std::none_of(model()->items().begin(), model()->items().end(),
+                   [&file_path](const auto& item) {
+                     return item->IsInitialized() &&
+                            item->file_path().DirName() == file_path;
+                   });
 
   if (!remove_watch)
     return;
@@ -571,17 +572,17 @@
       parent_path));
 }
 
-void HoldingSpaceFileSystemDelegate::ClearNonFinalizedItems() {
+void HoldingSpaceFileSystemDelegate::ClearNonInitializedItems() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   model()->RemoveIf(base::BindRepeating(
       [](Profile* profile, const HoldingSpaceItem* item) {
-        if (item->IsFinalized())
+        if (item->IsInitialized())
           return false;
 
         // Do not remove items whose path can be resolved to a file system URL.
         // In this case, the associated mount point has been mounted, but the
-        // finalization may have been delayed - for example due to issues/delays
-        // with initializing ARC.
+        // initialization may have been delayed - for example due to
+        // issues/delays with initializing ARC.
         const GURL url = holding_space_util::ResolveFileSystemUrl(
             profile, item->file_path());
         return url.is_empty();
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.h b/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.h
index 0c32dce7..e828bfe3 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.h
@@ -30,8 +30,8 @@
 
 // A delegate of `HoldingSpaceKeyedService` tasked with verifying validity of
 // files backing holding space items. The delegate:
-// *  Finalizes partially initialized items loaded from persistent storage once
-//    the validity of the backing file path was verified.
+// *  Fully initializes partially initialized items loaded from persistent
+//    storage once the validity of the backing file path was verified.
 // *  Monitors the file system for removal, rename, and move of files backing
 //    holding space items.
 class HoldingSpaceFileSystemDelegate
@@ -58,7 +58,7 @@
   void OnHoldingSpaceItemsRemoved(
       const std::vector<const HoldingSpaceItem*>& items) override;
   void OnHoldingSpaceItemUpdated(const HoldingSpaceItem* item) override;
-  void OnHoldingSpaceItemFinalized(const HoldingSpaceItem* item) override;
+  void OnHoldingSpaceItemInitialized(const HoldingSpaceItem* item) override;
 
   // file_manager::VolumeManagerObserver:
   void OnVolumeMounted(chromeos::MountError error_code,
@@ -116,10 +116,10 @@
   // holding space model.
   void RemoveItemsParentedByPath(const base::FilePath& parent_path);
 
-  // Clears all non-finalized items from holding space model - runs with a delay
-  // after profile initialization to clean up items from volumes that have not
-  // been mounted during startup.
-  void ClearNonFinalizedItems();
+  // Clears all non-initialized items from holding space model - runs with a
+  // delay after profile initialization to clean up items from volumes that have
+  // not been mounted during startup.
+  void ClearNonInitializedItems();
 
   // The `file_system_watcher_` is tasked with watching the file system for
   // changes on behalf of the delegate. It does so on a non-UI sequence. As
@@ -137,10 +137,10 @@
   // `pending_file_path_validity_checks_` is scheduled.
   bool file_path_validity_checks_scheduled_ = false;
 
-  // A timer to run clean-up task for items that have not been finalized within
-  // a reasonable amount of time from start-up. (E.g. if the volume they belong
-  // to has not been yet mounted).
-  base::OneShotTimer clear_non_finalized_items_timer_;
+  // A timer to run clean-up task for items that have not been initialized
+  // within a reasonable amount of time from start-up. (E.g. if the volume they
+  // belong to has not been yet mounted).
+  base::OneShotTimer clear_non_initialized_items_timer_;
 
   base::ScopedObservation<
       arc::ConnectionHolder<arc::mojom::FileSystemInstance,
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_browsertest.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_browsertest.cc
index c7e2a3f..80c71b38 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_browsertest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_browsertest.cc
@@ -65,7 +65,7 @@
               (const std::vector<const HoldingSpaceItem*>& items),
               (override));
   MOCK_METHOD(void,
-              OnHoldingSpaceItemFinalized,
+              OnHoldingSpaceItemInitialized,
               (const HoldingSpaceItem* item),
               (override));
 };
@@ -193,9 +193,9 @@
 }
 
 // Waits for a holding space item matching the provided `predicate` to be added
-// to the holding space model and finalized. Returns immediately if the item
-// already exists and is finalized.
-void WaitForItemFinalization(
+// to the holding space model and initialized. Returns immediately if the item
+// already exists and is initialized.
+void WaitForItemInitialization(
     base::RepeatingCallback<bool(const HoldingSpaceItem*)> predicate) {
   WaitForItemAddition(predicate);
 
@@ -205,7 +205,7 @@
       [&predicate](const auto& item) { return predicate.Run(item.get()); });
 
   DCHECK(item_it != model->items().end());
-  if (item_it->get()->IsFinalized())
+  if (item_it->get()->IsInitialized())
     return;
 
   testing::NiceMock<MockHoldingSpaceModelObserver> mock;
@@ -214,7 +214,7 @@
   observer.Observe(model);
 
   base::RunLoop run_loop;
-  ON_CALL(mock, OnHoldingSpaceItemFinalized)
+  ON_CALL(mock, OnHoldingSpaceItemInitialized)
       .WillByDefault([&](const HoldingSpaceItem* item) {
         if (item == item_it->get())
           run_loop.Quit();
@@ -233,10 +233,10 @@
 }
 
 // Waits for a holding space item with the provided `item_id` to be added to the
-// holding space model and finalized. Returns immediately if the item already
-// exists and is finalized.
-void WaitForItemFinalization(const std::string& item_id) {
-  WaitForItemFinalization(
+// holding space model and initialized. Returns immediately if the item already
+// exists and is initialized.
+void WaitForItemInitialization(const std::string& item_id) {
+  WaitForItemInitialization(
       base::BindLambdaForTesting([&item_id](const HoldingSpaceItem* item) {
         return item->id() == item_id;
       }));
@@ -457,7 +457,7 @@
   // Verify that holding space model gets restored on resume.
   chromeos::FakePowerManagerClient::Get()->SendSuspendDone();
 
-  WaitForItemFinalization(item_id);
+  WaitForItemInitialization(item_id);
   EXPECT_TRUE(holding_space_model->GetItem(item_id));
 }
 
@@ -475,7 +475,7 @@
 
 IN_PROC_BROWSER_TEST_P(HoldingSpaceKeyedServiceBrowserTest,
                        RestoreItemsOnRestart) {
-  WaitForItemFinalization(
+  WaitForItemInitialization(
       base::BindLambdaForTesting([this](const HoldingSpaceItem* item) {
         return item->type() == HoldingSpaceItem::Type::kDownload &&
                item->file_path() == GetPredefinedTestFile(0);
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.cc
index 0831ff2e..627dc21 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.cc
@@ -40,7 +40,7 @@
 void HoldingSpaceKeyedServiceDelegate::OnHoldingSpaceItemsRemoved(
     const std::vector<const HoldingSpaceItem*>& items) {}
 
-void HoldingSpaceKeyedServiceDelegate::OnHoldingSpaceItemFinalized(
+void HoldingSpaceKeyedServiceDelegate::OnHoldingSpaceItemInitialized(
     const HoldingSpaceItem* item) {}
 
 void HoldingSpaceKeyedServiceDelegate::OnPersistenceRestored() {}
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h
index fa23cae1..9fce7457 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h
@@ -49,7 +49,7 @@
       const std::vector<const HoldingSpaceItem*>& items) override;
   void OnHoldingSpaceItemsRemoved(
       const std::vector<const HoldingSpaceItem*>& items) override;
-  void OnHoldingSpaceItemFinalized(const HoldingSpaceItem* item) override;
+  void OnHoldingSpaceItemInitialized(const HoldingSpaceItem* item) override;
 
   // Invoked when holding space persistence has been restored.
   virtual void OnPersistenceRestored();
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
index 04fd9b8..4ae33e4 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
@@ -179,24 +179,24 @@
       model_observer_{this};
 };
 
-class ItemsFinalizedWaiter : public HoldingSpaceModelObserver {
+class ItemsInitializedWaiter : public HoldingSpaceModelObserver {
  public:
   // Predicate that determines whether the waiter should wait for an item to be
-  // finalized.
+  // initialized.
   using ItemFilter =
       base::RepeatingCallback<bool(const HoldingSpaceItem* item)>;
 
-  explicit ItemsFinalizedWaiter(HoldingSpaceModel* model) : model_(model) {}
-  ItemsFinalizedWaiter(const ItemsFinalizedWaiter&) = delete;
-  ItemsFinalizedWaiter& operator=(const ItemsFinalizedWaiter&) = delete;
-  ~ItemsFinalizedWaiter() override = default;
+  explicit ItemsInitializedWaiter(HoldingSpaceModel* model) : model_(model) {}
+  ItemsInitializedWaiter(const ItemsInitializedWaiter&) = delete;
+  ItemsInitializedWaiter& operator=(const ItemsInitializedWaiter&) = delete;
+  ~ItemsInitializedWaiter() override = default;
 
   // NOTE: The filter defaults to all items.
   void Wait(const ItemFilter& filter = ItemFilter()) {
     ASSERT_FALSE(wait_loop_);
 
     filter_ = filter;
-    if (FilteredItemsFinalized())
+    if (FilteredItemsInitialized())
       return;
 
     base::ScopedObservation<HoldingSpaceModel, HoldingSpaceModelObserver>
@@ -211,21 +211,21 @@
 
   void OnHoldingSpaceItemsRemoved(
       const std::vector<const HoldingSpaceItem*>& items) override {
-    if (FilteredItemsFinalized())
+    if (FilteredItemsInitialized())
       wait_loop_->Quit();
   }
 
-  void OnHoldingSpaceItemFinalized(const HoldingSpaceItem* item) override {
-    if (FilteredItemsFinalized())
+  void OnHoldingSpaceItemInitialized(const HoldingSpaceItem* item) override {
+    if (FilteredItemsInitialized())
       wait_loop_->Quit();
   }
 
  private:
-  bool FilteredItemsFinalized() const {
+  bool FilteredItemsInitialized() const {
     for (auto& item : model_->items()) {
       if (filter_ && !filter_.Run(item.get()))
         continue;
-      if (!item->IsFinalized())
+      if (!item->IsInitialized())
         return false;
     }
     return true;
@@ -1040,7 +1040,7 @@
   ASSERT_EQ(secondary_holding_space_model,
             secondary_holding_space_service->model_for_testing());
 
-  ItemsFinalizedWaiter(secondary_holding_space_model).Wait();
+  ItemsInitializedWaiter(secondary_holding_space_model).Wait();
   ASSERT_EQ(secondary_holding_space_model->items().size(),
             restored_holding_space_items.size());
 
@@ -1077,7 +1077,7 @@
   HoldingSpaceKeyedService* const primary_holding_space_service =
       HoldingSpaceKeyedServiceFactory::GetInstance()->GetService(GetProfile());
 
-  std::vector<std::string> finalized_items_before_delayed_mount;
+  std::vector<std::string> initialized_items_before_delayed_mount;
   HoldingSpaceModel::ItemList restored_holding_space_items;
   base::ListValue persisted_holding_space_items_after_restoration;
   base::ListValue persisted_holding_space_items_after_delayed_mount;
@@ -1135,7 +1135,7 @@
           // the persistent storage.
           persisted_holding_space_items_before_restoration->Append(
               fresh_holding_space_item->Serialize());
-          finalized_items_before_delayed_mount.push_back(
+          initialized_items_before_delayed_mount.push_back(
               fresh_holding_space_item->id());
           persisted_holding_space_items_after_restoration.Append(
               fresh_holding_space_item->Serialize());
@@ -1163,7 +1163,7 @@
   EXPECT_EQ(secondary_holding_space_model,
             secondary_holding_space_service->model_for_testing());
 
-  ItemsFinalizedWaiter(secondary_holding_space_model)
+  ItemsInitializedWaiter(secondary_holding_space_model)
       .Wait(
           /*filter=*/base::BindLambdaForTesting(
               [&downloads_mount](const HoldingSpaceItem* item) -> bool {
@@ -1171,12 +1171,12 @@
                     item->file_path());
               }));
 
-  std::vector<std::string> finalized_items;
+  std::vector<std::string> initialized_items;
   for (const auto& item : secondary_holding_space_model->items()) {
-    if (item->IsFinalized())
-      finalized_items.push_back(item->id());
+    if (item->IsInitialized())
+      initialized_items.push_back(item->id());
   }
-  EXPECT_EQ(finalized_items_before_delayed_mount, finalized_items);
+  EXPECT_EQ(initialized_items_before_delayed_mount, initialized_items);
 
   // Verify persisted holding space items.
   EXPECT_EQ(*secondary_profile->GetPrefs()->GetList(
@@ -1186,7 +1186,7 @@
   delayed_mount->CreateFile(delayed_mount_file_name, "fake");
   delayed_mount->Mount(secondary_profile);
 
-  ItemsFinalizedWaiter(secondary_holding_space_model).Wait();
+  ItemsInitializedWaiter(secondary_holding_space_model).Wait();
 
   EXPECT_EQ(secondary_holding_space_model->items().size(),
             restored_holding_space_items.size());
@@ -1197,7 +1197,7 @@
     const auto& restored_item = restored_holding_space_items[i];
     SCOPED_TRACE(testing::Message() << "Item at index " << i);
 
-    EXPECT_TRUE(item->IsFinalized());
+    EXPECT_TRUE(item->IsInitialized());
 
     EXPECT_EQ(item->id(), restored_item->id());
     EXPECT_EQ(item->type(), restored_item->type());
@@ -1314,7 +1314,7 @@
   EXPECT_EQ(secondary_holding_space_model,
             secondary_holding_space_service->model_for_testing());
 
-  ItemsFinalizedWaiter(secondary_holding_space_model).Wait();
+  ItemsInitializedWaiter(secondary_holding_space_model).Wait();
   ASSERT_EQ(secondary_holding_space_model->items().size(),
             restored_holding_space_items.size());
 
@@ -1324,7 +1324,7 @@
     const auto& restored_item = restored_holding_space_items[i];
     SCOPED_TRACE(testing::Message() << "Item at index " << i);
 
-    EXPECT_TRUE(item->IsFinalized());
+    EXPECT_TRUE(item->IsInitialized());
 
     EXPECT_EQ(item->id(), restored_item->id());
     EXPECT_EQ(item->type(), restored_item->type());
@@ -1362,7 +1362,7 @@
   HoldingSpaceKeyedService* const primary_holding_space_service =
       HoldingSpaceKeyedServiceFactory::GetInstance()->GetService(GetProfile());
 
-  std::vector<std::string> finalized_items_before_delayed_mount;
+  std::vector<std::string> initialized_items_before_delayed_mount;
   HoldingSpaceModel::ItemList restored_holding_space_items;
   base::ListValue persisted_holding_space_items_after_restoration;
   base::ListValue persisted_holding_space_items_after_delayed_mount;
@@ -1388,7 +1388,7 @@
           // the persistent storage.
           persisted_holding_space_items_before_restoration->Append(
               fresh_holding_space_item->Serialize());
-          finalized_items_before_delayed_mount.push_back(
+          initialized_items_before_delayed_mount.push_back(
               fresh_holding_space_item->id());
           persisted_holding_space_items_after_restoration.Append(
               fresh_holding_space_item->Serialize());
@@ -1417,14 +1417,14 @@
   EXPECT_EQ(secondary_holding_space_model,
             secondary_holding_space_service->model_for_testing());
 
-  ItemsFinalizedWaiter(secondary_holding_space_model).Wait();
+  ItemsInitializedWaiter(secondary_holding_space_model).Wait();
 
-  std::vector<std::string> finalized_items;
+  std::vector<std::string> initialized_items;
   for (const auto& item : secondary_holding_space_model->items()) {
-    if (item->IsFinalized())
-      finalized_items.push_back(item->id());
+    if (item->IsInitialized())
+      initialized_items.push_back(item->id());
   }
-  EXPECT_EQ(finalized_items_before_delayed_mount, finalized_items);
+  EXPECT_EQ(initialized_items_before_delayed_mount, initialized_items);
 
   // Verify persisted holding space items.
   EXPECT_EQ(*secondary_profile->GetPrefs()->GetList(
@@ -1432,7 +1432,7 @@
             persisted_holding_space_items_after_restoration);
 
   delayed_mount_2->Mount(secondary_profile);
-  ItemsFinalizedWaiter(secondary_holding_space_model).Wait();
+  ItemsInitializedWaiter(secondary_holding_space_model).Wait();
 
   EXPECT_EQ(secondary_holding_space_model->items().size(),
             restored_holding_space_items.size());
@@ -1443,7 +1443,7 @@
     const auto& restored_item = restored_holding_space_items[i];
     SCOPED_TRACE(testing::Message() << "Item at index " << i);
 
-    EXPECT_TRUE(item->IsFinalized());
+    EXPECT_TRUE(item->IsInitialized());
 
     EXPECT_EQ(item->id(), restored_item->id());
     EXPECT_EQ(item->type(), restored_item->type());
@@ -1589,7 +1589,7 @@
   ASSERT_EQ(secondary_holding_space_model,
             secondary_holding_space_service->model_for_testing());
 
-  ItemsFinalizedWaiter(secondary_holding_space_model).Wait();
+  ItemsInitializedWaiter(secondary_holding_space_model).Wait();
 
   ASSERT_EQ(secondary_holding_space_model->items().size(),
             restored_holding_space_items.size());
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc b/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc
index 4263ca2..39ff7d9 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc
@@ -284,7 +284,7 @@
               (const std::vector<const HoldingSpaceItem*>& items),
               (override));
   MOCK_METHOD(void,
-              OnHoldingSpaceItemFinalized,
+              OnHoldingSpaceItemInitialized,
               (const HoldingSpaceItem* item),
               (override));
 };
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
index 660f145..24a3ed43 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
@@ -23,7 +23,6 @@
 #include "ash/shelf/shelf_view.h"
 #include "ash/shelf/shelf_view_test_api.h"
 #include "ash/shelf/shelf_widget.h"
-#include "ash/shelf/test/widget_animation_waiter.h"
 #include "ash/shell.h"
 #include "ash/wm/desks/desk.h"
 #include "ash/wm/desks/desks_controller.h"
@@ -126,6 +125,7 @@
 #include "ui/events/test/event_generator.h"
 #include "ui/events/types/event_type.h"
 #include "ui/views/controls/menu/menu_item_view.h"
+#include "ui/views/test/widget_animation_waiter.h"
 
 namespace {
 
@@ -200,9 +200,9 @@
   ash::ShelfView* shelf_view = controller->shelf()->GetShelfViewForTesting();
 
   // Observe hotseat animation before animation starts. Because
-  // ash::WidgetAnimationWaiter only reacts to completion of the animation whose
-  // animation scheduling is recorded in ash::WidgetAnimationWaiter.
-  ash::WidgetAnimationWaiter waiter(shelf_view->GetWidget());
+  // views::WidgetAnimationWaiter only reacts to completion of the animation
+  // whose animation scheduling is recorded in views::WidgetAnimationWaiter.
+  views::WidgetAnimationWaiter waiter(shelf_view->GetWidget());
 
   ui::test::EventGenerator event_generator(controller->GetRootWindow());
   event_generator.GestureScrollSequence(
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
index a4c3da2..fbeffe5 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
@@ -735,7 +735,13 @@
 
 // Tests that the tapping gesture with cntl/cmd key on a link open the
 // backgournd tab.
-IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, TapGestureWithCtrlKey) {
+// crbug.com/1192343: flaky on Mac
+#if defined(OS_MAC)
+#define MAYBE_TapGestureWithCtrlKey DISABLED_TapGestureWithCtrlKey
+#else
+#define MAYBE_TapGestureWithCtrlKey TapGestureWithCtrlKey
+#endif
+IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, MAYBE_TapGestureWithCtrlKey) {
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
 
   GURL url(embedded_test_server()->GetURL(
diff --git a/chrome/browser/ui/webui/chromeos/in_session_password_change/lock_screen_reauth_handler.cc b/chrome/browser/ui/webui/chromeos/in_session_password_change/lock_screen_reauth_handler.cc
index e3ff66a..e7b7f39 100644
--- a/chrome/browser/ui/webui/chromeos/in_session_password_change/lock_screen_reauth_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/in_session_password_change/lock_screen_reauth_handler.cc
@@ -176,6 +176,8 @@
                     login::ExtractSamlPasswordAttributesEnabled());
   params.SetBoolean("doSamlRedirect", ShouldDoSamlRedirect(context.email));
   params.SetString("clientVersion", version_info::GetVersionNumber());
+  params.SetBoolean("readOnlyEmail", true);
+
   AllowJavascript();
   CallJavascriptFunction("$(\'main-element\').loadAuthenticator", params);
 }
diff --git a/chrome/browser/ui/webui/policy/policy_ui.cc b/chrome/browser/ui/webui/policy/policy_ui.cc
index dc454eb..67b58c6 100644
--- a/chrome/browser/ui/webui/policy/policy_ui.cc
+++ b/chrome/browser/ui/webui/policy/policy_ui.cc
@@ -34,8 +34,6 @@
     {"labelAssetId", IDS_POLICY_LABEL_ASSET_ID},
     {"labelClientId", IDS_POLICY_LABEL_CLIENT_ID},
     {"labelDirectoryApiId", IDS_POLICY_LABEL_DIRECTORY_API_ID},
-    {"labelEnterpriseEnrollmentDomain",
-     IDS_POLICY_LABEL_ENTERPRISE_ENROLLMENT_DOMAIN},
     {"labelGaiaId", IDS_POLICY_LABEL_GAIA_ID},
     {"labelIsAffiliated", IDS_POLICY_LABEL_IS_AFFILIATED},
     {"labelLocation", IDS_POLICY_LABEL_LOCATION},
diff --git a/chrome/browser/ui/webui/policy/policy_ui_handler.cc b/chrome/browser/ui/webui/policy/policy_ui_handler.cc
index 44063e3..3b31515d 100644
--- a/chrome/browser/ui/webui/policy/policy_ui_handler.cc
+++ b/chrome/browser/ui/webui/policy/policy_ui_handler.cc
@@ -387,7 +387,6 @@
   void GetStatus(base::DictionaryValue* dict) override;
 
  private:
-  std::string enterprise_enrollment_domain_;
   std::string enterprise_domain_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(DeviceCloudPolicyStatusProviderChromeOS);
@@ -452,7 +451,6 @@
  public:
   DeviceActiveDirectoryPolicyStatusProvider(
       policy::ActiveDirectoryPolicyManager* policy_manager,
-      const std::string& enterprise_realm,
       const std::string& enterprise_domain_manager);
 
   ~DeviceActiveDirectoryPolicyStatusProvider() override = default;
@@ -461,7 +459,6 @@
   void GetStatus(base::DictionaryValue* dict) override;
 
  private:
-  std::string enterprise_realm_;
   std::string enterprise_domain_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(DeviceActiveDirectoryPolicyStatusProvider);
@@ -639,7 +636,6 @@
         policy::BrowserPolicyConnectorChromeOS* connector)
     : CloudPolicyCoreStatusProvider(
           connector->GetDeviceCloudPolicyManager()->core()) {
-  enterprise_enrollment_domain_ = connector->GetEnterpriseEnrollmentDomain();
   enterprise_domain_manager_ = connector->GetEnterpriseDomainManager();
 }
 
@@ -649,7 +645,6 @@
 void DeviceCloudPolicyStatusProviderChromeOS::GetStatus(
     base::DictionaryValue* dict) {
   GetStatusFromCore(core_, dict);
-  dict->SetString("enterpriseEnrollmentDomain", enterprise_enrollment_domain_);
   dict->SetString("enterpriseDomainManager", enterprise_domain_manager_);
   GetOffHoursStatus(dict);
 }
@@ -757,16 +752,13 @@
 DeviceActiveDirectoryPolicyStatusProvider::
     DeviceActiveDirectoryPolicyStatusProvider(
         policy::ActiveDirectoryPolicyManager* policy_manager,
-        const std::string& enterprise_realm,
         const std::string& enterprise_domain_manager)
     : UserActiveDirectoryPolicyStatusProvider(policy_manager, nullptr),
-      enterprise_realm_(enterprise_realm),
       enterprise_domain_manager_(enterprise_domain_manager) {}
 
 void DeviceActiveDirectoryPolicyStatusProvider::GetStatus(
     base::DictionaryValue* dict) {
   UserActiveDirectoryPolicyStatusProvider::GetStatus(dict);
-  dict->SetString("enterpriseEnrollmentDomain", enterprise_realm_);
   dict->SetString("enterpriseDomainManager", enterprise_domain_manager_);
 }
 
@@ -891,7 +883,7 @@
       device_status_provider_ =
           std::make_unique<DeviceActiveDirectoryPolicyStatusProvider>(
               connector->GetDeviceActiveDirectoryPolicyManager(),
-              connector->GetRealm(), connector->GetEnterpriseDomainManager());
+              connector->GetEnterpriseDomainManager());
     } else {
       device_status_provider_ =
           std::make_unique<DeviceCloudPolicyStatusProviderChromeOS>(connector);
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index 257c74b..6df9754 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -36,8 +36,6 @@
 #include "chrome/browser/printing/print_view_manager.h"
 #include "chrome/browser/printing/printer_manager_dialog.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/account_consistency_mode_manager.h"
-#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/chrome_pages.h"
@@ -62,8 +60,6 @@
 #include "components/prefs/pref_service.h"
 #include "components/printing/browser/prefs_util.h"
 #include "components/printing/common/cloud_print_cdd_conversion.h"
-#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
-#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
 #include "components/url_formatter/url_formatter.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/render_frame_host.h"
@@ -189,12 +185,6 @@
 const char kDestinationsManaged[] = "destinationsManaged";
 // Name of a dictionary field holding the cloud print URL.
 const char kCloudPrintURL[] = "cloudPrintURL";
-// Name of a dictionary field holding the signed in user accounts.
-const char kUserAccounts[] = "userAccounts";
-// Name of a dictionary field indicating whether sync is available. If false,
-// Print Preview will always send a request to the Google Cloud Print server on
-// load, to check the user's sign in state.
-const char kSyncAvailable[] = "syncAvailable";
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // Name of a dictionary field indicating whether the user's Drive directory is
 // mounted.
@@ -319,9 +309,7 @@
   ReportUserActionHistogram(UserActionBuckets::kPreviewStarted);
 }
 
-PrintPreviewHandler::~PrintPreviewHandler() {
-  UnregisterForGaiaCookieChanges();
-}
+PrintPreviewHandler::~PrintPreviewHandler() = default;
 
 void PrintPreviewHandler::RegisterMessages() {
   web_ui()->RegisterMessageCallback(
@@ -375,10 +363,7 @@
 
 void PrintPreviewHandler::OnJavascriptAllowed() {
   print_preview_ui()->SetPreviewUIId();
-  // Now that the UI is initialized, any future account changes will require
-  // a printer list refresh.
   ReadPrinterTypeDenyListFromPrefs();
-  RegisterForGaiaCookieChanges();
 }
 
 void PrintPreviewHandler::OnJavascriptDisallowed() {
@@ -389,7 +374,6 @@
   preview_callbacks_.clear();
   preview_failures_.clear();
   printer_type_deny_list_.clear();
-  UnregisterForGaiaCookieChanges();
 }
 
 WebContents* PrintPreviewHandler::preview_web_contents() {
@@ -710,11 +694,6 @@
 }
 
 void PrintPreviewHandler::OnSignInTabClosed() {
-  if (identity_manager_) {
-    // Sign in state will be reported in OnAccountsInCookieJarUpdated, so no
-    // need to do anything here.
-    return;
-  }
   FireWebUIListener("check-for-account-update");
 }
 
@@ -786,21 +765,6 @@
                      weak_factory_.GetWeakPtr(), callback_id));
 }
 
-void PrintPreviewHandler::GetUserAccountList(base::Value* settings) {
-  base::Value account_list(base::Value::Type::LIST);
-  if (identity_manager_) {
-    const std::vector<gaia::ListedAccount>& accounts =
-        identity_manager_->GetAccountsInCookieJar().signed_in_accounts;
-    for (const gaia::ListedAccount& account : accounts) {
-      account_list.Append(account.email);
-    }
-    settings->SetKey(kSyncAvailable, base::Value(true));
-  } else {
-    settings->SetKey(kSyncAvailable, base::Value(false));
-  }
-  settings->SetKey(kUserAccounts, std::move(account_list));
-}
-
 void PrintPreviewHandler::SendInitialSettings(
     const std::string& callback_id,
     const std::string& default_printer) {
@@ -861,9 +825,6 @@
   }
 
   GetLocaleInformation(&initial_settings);
-  if (IsCloudPrintEnabled()) {
-    GetUserAccountList(&initial_settings);
-  }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   drive::DriveIntegrationService* drive_service =
@@ -923,18 +884,6 @@
   return dialog_controller->GetInitiator(preview_web_contents());
 }
 
-void PrintPreviewHandler::OnAccountsInCookieUpdated(
-    const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
-    const GoogleServiceAuthError& error) {
-  base::Value account_list(base::Value::Type::LIST);
-  const std::vector<gaia::ListedAccount>& accounts =
-      accounts_in_cookie_jar_info.signed_in_accounts;
-  for (const auto& account : accounts) {
-    account_list.Append(account.email);
-  }
-  FireWebUIListener("user-accounts-updated", std::move(account_list));
-}
-
 void PrintPreviewHandler::OnPrintPreviewReady(int preview_uid, int request_id) {
   std::string callback_id = GetCallbackId(request_id);
   if (callback_id.empty())
@@ -1127,36 +1076,9 @@
   }
 }
 
-void PrintPreviewHandler::RegisterForGaiaCookieChanges() {
-  DCHECK(!identity_manager_);
-  cloud_print_enabled_ =
-      !base::Contains(printer_type_deny_list_, PrinterType::kCloud) &&
-      GetPrefs()->GetBoolean(prefs::kCloudPrintSubmitEnabled);
-
-  if (!cloud_print_enabled_)
-    return;
-
-  Profile* profile = Profile::FromWebUI(web_ui());
-  if (!AccountConsistencyModeManager::IsMirrorEnabledForProfile(profile) &&
-      !AccountConsistencyModeManager::IsDiceEnabledForProfile(profile)) {
-    return;
-  }
-
-  identity_manager_ = IdentityManagerFactory::GetForProfile(profile);
-  identity_manager_->AddObserver(this);
-}
-
-void PrintPreviewHandler::UnregisterForGaiaCookieChanges() {
-  if (!identity_manager_)
-    return;
-
-  identity_manager_->RemoveObserver(this);
-  identity_manager_ = nullptr;
-  cloud_print_enabled_ = false;
-}
-
 bool PrintPreviewHandler::IsCloudPrintEnabled() {
-  return cloud_print_enabled_;
+  return !base::Contains(printer_type_deny_list_, PrinterType::kCloud) &&
+         GetPrefs()->GetBoolean(prefs::kCloudPrintSubmitEnabled);
 }
 
 void PrintPreviewHandler::BadMessageReceived() {
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
index 4ea6dfc..c4a174e0 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -19,7 +19,6 @@
 #include "chrome/common/buildflags.h"
 #include "components/prefs/pref_service.h"
 #include "components/printing/common/print.mojom.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
 #include "content/public/browser/web_ui_message_handler.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "printing/backend/print_backend.h"
@@ -43,8 +42,7 @@
 class PrintPreviewUI;
 
 // The handler for Javascript messages related to the print preview dialog.
-class PrintPreviewHandler : public content::WebUIMessageHandler,
-                            public signin::IdentityManager::Observer {
+class PrintPreviewHandler : public content::WebUIMessageHandler {
  public:
   PrintPreviewHandler();
   ~PrintPreviewHandler() override;
@@ -54,11 +52,6 @@
   void OnJavascriptAllowed() override;
   void OnJavascriptDisallowed() override;
 
-  // IdentityManager::Observer implementation.
-  void OnAccountsInCookieUpdated(
-      const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
-      const GoogleServiceAuthError& error) override;
-
   // Called when print preview failed. |request_id| identifies the request that
   // failed.
   void OnPrintPreviewFailed(int request_id);
@@ -125,11 +118,6 @@
   // Gets the initiator for the print preview dialog.
   virtual content::WebContents* GetInitiator();
 
-  // Register/unregister from notifications of changes done to the GAIA
-  // cookie. Protected so unit tests can override.
-  virtual void RegisterForGaiaCookieChanges();
-  virtual void UnregisterForGaiaCookieChanges();
-
  private:
   friend class PrintPreviewPdfGeneratedBrowserTest;
   FRIEND_TEST_ALL_PREFIXES(PrintPreviewPdfGeneratedBrowserTest,
@@ -279,16 +267,9 @@
   // Whether we have already logged the number of printers this session.
   bool has_logged_printers_count_ = false;
 
-  // Whether Google Cloud Print is enabled for the active profile.
-  bool cloud_print_enabled_ = false;
-
   // The settings used for the most recent preview request.
   base::Value last_preview_settings_;
 
-  // Pointer to the identity manager service so that print preview can listen
-  // for GAIA cookie changes.
-  signin::IdentityManager* identity_manager_ = nullptr;
-
   // Handles requests for extension printers. Created lazily by calling
   // GetPrinterHandler().
   std::unique_ptr<PrinterHandler> extension_printer_handler_;
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
index 3976130a..a55c93f 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
@@ -298,9 +298,6 @@
 
   bool IsCloudPrintEnabled() override { return true; }
 
-  void RegisterForGaiaCookieChanges() override {}
-  void UnregisterForGaiaCookieChanges() override {}
-
   void BadMessageReceived() override { bad_messages_++; }
 
   content::WebContents* GetInitiator() override { return initiator_; }
@@ -485,10 +482,6 @@
                                         base::Value::Type::BOOLEAN));
     ASSERT_TRUE(
         settings->FindKeyOfType("cloudPrintURL", base::Value::Type::STRING));
-    ASSERT_TRUE(
-        settings->FindKeyOfType("userAccounts", base::Value::Type::LIST));
-    ASSERT_TRUE(
-        settings->FindKeyOfType("syncAvailable", base::Value::Type::BOOLEAN));
   }
 
   // Returns |policy_name| entry from initial settings policies.
diff --git a/chrome/browser/ui/webui/settings/OWNERS b/chrome/browser/ui/webui/settings/OWNERS
index 03f5a91..34f25ac 100644
--- a/chrome/browser/ui/webui/settings/OWNERS
+++ b/chrome/browser/ui/webui/settings/OWNERS
@@ -9,6 +9,7 @@
 per-file safe_browsing_handler*=msramek@chromium.org
 per-file safe_browsing_handler*=sauski@google.com
 per-file safety_check_handler*=andzaytsev@google.com
+per-file settings_clear_browsing_data_handler*=sauski@google.com
 per-file settings_cookies_view_handler*=sauski@google.com
 per-file settings_localized_strings_provider*=sauski@google.com
 per-file settings_security_key_handler*=file://device/fido/OWNERS
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 9f836a0..2dfaa79 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -981,14 +981,18 @@
        IDS_SETTINGS_PASSWORD_DELETED_PASSWORD_FROM_ACCOUNT_AND_DEVICE},
       {"passwordMovePasswordsToAccount",
        IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT},
-      {"passwordMovePasswordsToAccountDialogFooter",
-       IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_FOOTER},
+      {"passwordMovePasswordsToAccountDialogBodyText",
+       IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_BODY_TEXT},
       {"passwordMovePasswordsToAccountDialogTitle",
        IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_TITLE},
       {"passwordMoveToAccountDialogTitle",
        IDS_SETTINGS_PASSWORD_MOVE_TO_ACCOUNT_DIALOG_TITLE},
       {"passwordMoveToAccountDialogBody",
        IDS_SETTINGS_PASSWORD_MOVE_TO_ACCOUNT_DIALOG_BODY},
+      {"passwordMoveMultiplePasswordsToAccountDialogMoveButtonText",
+       IDS_SETTINGS_PASSWORD_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_MOVE_BUTTON_TEXT},
+      {"passwordMoveMultiplePasswordsToAccountDialogCancelButtonText",
+       IDS_SETTINGS_PASSWORD_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_CANCEL_BUTTON_TEXT},
       {"passwordMoveToAccountDialogMoveButtonText",
        IDS_SETTINGS_PASSWORD_MOVE_TO_ACCOUNT_DIALOG_MOVE_BUTTON_TEXT},
       {"passwordMoveToAccountDialogCancelButtonText",
@@ -1607,8 +1611,22 @@
        IDS_SETTINGS_SEARCH_ENGINES_REMOVE_FROM_LIST},
       {"searchEnginesManageExtension",
        IDS_SETTINGS_SEARCH_ENGINES_MANAGE_EXTENSION},
+      // TODO(yoangela): Placeholder strings, update when these strings are
+      // finalized.
+      {"searchEnginesTriggerOptions",
+       IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_OPTIONS},
+      {"searchEnginesTriggerDescription",
+       IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_DESCRIPTION},
+      {"searchEnginesTriggerSpaceEnabled",
+       IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_SPACE_ENABLED},
+      {"searchEnginesTriggerSpaceDisabled",
+       IDS_SETTINGS_SEARCH_ENGINES_TRIGGER_SPACE_DISABLED},
+
   };
   html_source->AddLocalizedStrings(kLocalizedStrings);
+  html_source->AddBoolean(
+      "showKeywordTriggerSetting",
+      base::FeatureList::IsEnabled(omnibox::kKeywordSpaceTriggeringSetting));
 }
 
 void AddSiteSettingsStrings(content::WebUIDataSource* html_source,
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
index 05b12129..74fe267 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
@@ -18,6 +18,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
+#include "base/notreached.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -59,6 +60,7 @@
 #include "chrome/browser/ui/webui/signin/signin_ui_error.h"
 #include "chrome/browser/ui/webui/signin/signin_utils.h"
 #include "chrome/browser/ui/webui/signin/signin_utils_desktop.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/common/search/selected_colors_info.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/chromium_strings.h"
@@ -219,20 +221,6 @@
   }
 }
 
-void SetProfileName(const base::FilePath& profile_path,
-                    const std::u16string name) {
-  ProfileAttributesEntry* entry =
-      g_browser_process->profile_manager()
-          ->GetProfileAttributesStorage()
-          .GetProfileAttributesWithPath(profile_path);
-  if (!entry) {
-    NOTREACHED();
-    return;
-  }
-
-  entry->SetLocalProfileName(name, /*is_default_name=*/false);
-}
-
 void UnlockProfileAndHideLoginUI(const base::FilePath profile_path,
                                  InlineLoginHandlerImpl* handler) {
   SetProfileLocked(profile_path, false);
@@ -282,7 +270,26 @@
           profiles::GetDefaultNameForNewSignedInProfileWithIncompleteInfo(
               primary_account);
     }
-    SetProfileName(profile->GetPath(), profile_name);
+    ProfileAttributesEntry* entry =
+        g_browser_process->profile_manager()
+            ->GetProfileAttributesStorage()
+            .GetProfileAttributesWithPath(profile->GetPath());
+    if (entry) {
+      entry->SetLocalProfileName(profile_name, /*is_default_name=*/false);
+      // TODO(https://crbug.com/1186969): move the following code into a new
+      // `Profile::SetIsHidden()` method.
+      entry->SetIsOmitted(false);
+      if (!profile->GetPrefs()->GetBoolean(prefs::kForceEphemeralProfiles)) {
+        // Unmark this profile ephemeral so that it isn't deleted upon next
+        // startup. Profiles should never be made non-ephemeral if ephemeral
+        // mode is forced by policy.
+        entry->SetIsEphemeral(false);
+      }
+    } else {
+      DVLOG(1) << "ProfileAttributesEntry not found for profile:"
+               << profile->GetPath();
+    }
+
     Browser* browser = chrome::FindBrowserWithProfile(profile);
 
     // Don't show the customization bubble if a valid policy theme is set.
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 803ce2d..63e29d9 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1620031841-0620eda5ee1a0ce1fde9fd0b4ffda765e8f8e538.profdata
+chrome-win32-master-1620053793-814fac073bd5ab3908df9500e32cffda7bf27a63.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 7da9ecb..192091d0 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1620031841-73280d6f9a6ae8910679ea9cb38345d41f90ec6f.profdata
+chrome-win64-master-1620053793-88b8c7b880727d634faa0ec34d1d5b34f9f4da48.profdata
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 7b1bb59..8678851 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -252,7 +252,7 @@
     "https://support.google.com/chrome/?p=safety_tip";
 
 const char kSearchHistoryUrlInClearBrowsingData[] =
-    "https://myactivity.google.com/myactivity?product=19&utm_source=chrome_cbd";
+    "https://myactivity.google.com/product/search?utm_source=chrome_cbd";
 
 const char kSeeMoreSecurityTipsURL[] =
     "https://support.google.com/accounts/answer/32040";
diff --git a/chrome/installer/setup/install_worker.cc b/chrome/installer/setup/install_worker.cc
index 99a80dc4..6e3fba4 100644
--- a/chrome/installer/setup/install_worker.cc
+++ b/chrome/installer/setup/install_worker.cc
@@ -29,6 +29,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/version.h"
 #include "base/win/registry.h"
+#include "base/win/security_util.h"
+#include "base/win/sid.h"
 #include "base/win/win_util.h"
 #include "base/win/windows_version.h"
 #include "build/branding_buildflags.h"
@@ -71,7 +73,6 @@
 constexpr wchar_t kLpacChromeInstallFilesCapabilitySid[] =
     L"S-1-15-3-1024-2302894289-466761758-1166120688-1039016420-2430351297-"
     L"4240214049-4028510897-3317428798";
-constexpr wchar_t kAuthenticatedUsersSid[] = L"AU";
 
 void AddInstallerCopyTasks(const InstallParams& install_params,
                            WorkItemList* install_list) {
@@ -704,17 +705,20 @@
       base::BindOnce(
           [](const base::FilePath& target_path, const base::FilePath& temp_path,
              const CallbackWorkItem& work_item) {
-            std::vector<const wchar_t*> sids = {
-                kChromeInstallFilesCapabilitySid,
-                kLpacChromeInstallFilesCapabilitySid};
-            bool success_target = GrantAccessToPath(
-                target_path, sids, FILE_GENERIC_READ | FILE_GENERIC_EXECUTE,
-                CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE);
-            bool success_temp = GrantAccessToPath(
-                temp_path, sids, FILE_GENERIC_READ | FILE_GENERIC_EXECUTE,
-                CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE);
+            auto sids = base::win::Sid::FromSddlStringVector(
+                {kChromeInstallFilesCapabilitySid,
+                 kLpacChromeInstallFilesCapabilitySid});
+            bool success = false;
+            if (sids) {
+              bool success_target = base::win::GrantAccessToPath(
+                  target_path, *sids, FILE_GENERIC_READ | FILE_GENERIC_EXECUTE,
+                  CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE);
+              bool success_temp = base::win::GrantAccessToPath(
+                  temp_path, *sids, FILE_GENERIC_READ | FILE_GENERIC_EXECUTE,
+                  CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE);
+              success = success_target && success_temp;
+            }
 
-            bool success = (success_target && success_temp);
             base::UmaHistogramBoolean("Setup.Install.AddAppContainerAce",
                                       success);
             return success;
@@ -735,8 +739,14 @@
             base::BindOnce(
                 [](const base::FilePath& histogram_storage_dir,
                    const CallbackWorkItem& work_item) {
-                  return GrantAccessToPath(
-                      histogram_storage_dir, {kAuthenticatedUsersSid},
+                  auto sid = base::win::Sid::FromKnownSid(
+                      base::win::WellKnownSid::kAuthenticatedUser);
+                  if (!sid)
+                    return false;
+                  std::vector<base::win::Sid> sids;
+                  sids.push_back(std::move(*sid));
+                  return base::win::GrantAccessToPath(
+                      histogram_storage_dir, sids,
                       FILE_GENERIC_READ | FILE_DELETE_CHILD,
                       CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE);
                 },
diff --git a/chrome/installer/setup/setup_util.cc b/chrome/installer/setup/setup_util.cc
index ab3b5f1..f4bc0a7 100644
--- a/chrome/installer/setup/setup_util.cc
+++ b/chrome/installer/setup/setup_util.cc
@@ -6,9 +6,7 @@
 
 #include "chrome/installer/setup/setup_util.h"
 
-#include <aclapi.h>
 #include <objbase.h>
-#include <sddl.h>
 #include <stddef.h>
 #include <windows.h>
 #include <wtsapi32.h>
@@ -20,7 +18,6 @@
 #include <set>
 #include <string>
 #include <utility>
-#include <vector>
 
 #include "base/base64.h"
 #include "base/bind.h"
@@ -191,12 +188,6 @@
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
 }
 
-struct LocalFreeDeleter {
-  void operator()(void* ptr) const { ::LocalFree(ptr); }
-};
-
-typedef std::unique_ptr<void, LocalFreeDeleter> LocalFreeUniquePtr;
-
 }  // namespace
 
 const char kUnPackStatusMetricsName[] = "Setup.Install.LzmaUnPackStatus";
@@ -835,66 +826,4 @@
   }
 }
 
-bool GrantAccessToPath(const base::FilePath& path,
-                       const std::vector<const wchar_t*>& sids,
-                       ACCESS_MASK access_mask,
-                       DWORD inheritance) {
-  DCHECK(!path.empty());
-  if (sids.empty())
-    return true;
-
-  std::wstring object_name = path.value();
-  PSECURITY_DESCRIPTOR sd = nullptr;
-  PACL dacl = nullptr;
-
-  // Get the existing DACL.
-  DWORD error = ::GetNamedSecurityInfo(object_name.c_str(), SE_FILE_OBJECT,
-                                       DACL_SECURITY_INFORMATION, nullptr,
-                                       nullptr, &dacl, nullptr, &sd);
-  if (error != ERROR_SUCCESS) {
-    ::SetLastError(error);
-    DPLOG(ERROR) << "Failed getting DACL for path \"" << path.value() << "\"";
-    return false;
-  }
-  LocalFreeUniquePtr sd_ptr(sd);
-
-  std::vector<LocalFreeUniquePtr> sid_lifetime;
-  sid_lifetime.reserve(sids.size());
-  std::vector<EXPLICIT_ACCESS> access_entries(sids.size());
-  auto entries_interator = access_entries.begin();
-  for (const wchar_t* sid : sids) {
-    EXPLICIT_ACCESS& new_access = *entries_interator++;
-    new_access.grfAccessMode = GRANT_ACCESS;
-    new_access.grfAccessPermissions = access_mask;
-    new_access.grfInheritance = inheritance;
-    PSID psid;
-    if (!::ConvertStringSidToSid(sid, &psid))
-      return false;
-    sid_lifetime.emplace_back(psid);
-    ::BuildTrusteeWithSid(&new_access.Trustee, psid);
-  }
-
-  PACL new_dacl = nullptr;
-  error = ::SetEntriesInAcl(access_entries.size(), access_entries.data(), dacl,
-                            &new_dacl);
-  if (error != ERROR_SUCCESS) {
-    ::SetLastError(error);
-    DPLOG(ERROR) << "Failed adding ACEs to DACL for path \"" << path.value()
-                 << "\"";
-    return false;
-  }
-  LocalFreeUniquePtr new_dacl_ptr(new_dacl);
-
-  error = ::SetNamedSecurityInfo(&object_name[0], SE_FILE_OBJECT,
-                                 DACL_SECURITY_INFORMATION, nullptr, nullptr,
-                                 new_dacl, nullptr);
-  if (error != ERROR_SUCCESS) {
-    ::SetLastError(error);
-    DPLOG(ERROR) << "Failed setting DACL for path \"" << path.value() << "\"";
-    return false;
-  }
-
-  return true;
-}
-
 }  // namespace installer
diff --git a/chrome/installer/setup/setup_util.h b/chrome/installer/setup/setup_util.h
index 4d35549..601c425 100644
--- a/chrome/installer/setup/setup_util.h
+++ b/chrome/installer/setup/setup_util.h
@@ -163,13 +163,6 @@
                                    const base::Version& new_version,
                                    WorkItemList* list);
 
-// Adds allowed ACE entries to a file or directory |path| from a list of SIDs
-// with allowed |access_mask| and |inheritance| flags.
-bool GrantAccessToPath(const base::FilePath& path,
-                       const std::vector<const wchar_t*>& sids,
-                       ACCESS_MASK access_mask,
-                       DWORD inheritance);
-
 }  // namespace installer
 
 #endif  // CHROME_INSTALLER_SETUP_SETUP_UTIL_H_
diff --git a/chrome/installer/setup/setup_util_unittest.cc b/chrome/installer/setup/setup_util_unittest.cc
index c7eebad..f25f1d1 100644
--- a/chrome/installer/setup/setup_util_unittest.cc
+++ b/chrome/installer/setup/setup_util_unittest.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/installer/setup/setup_util_unittest.h"
 
-#include <aclapi.h>
-#include <sddl.h>
 #include <shlobj.h>
 #include <windows.h>
 
@@ -877,113 +875,4 @@
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
 }
 
-namespace {
-
-constexpr wchar_t kBaseDacl[] = L"D:P(A;;FA;;;WD)";
-constexpr wchar_t kTest1Dacl[] = L"D:PAI(A;;FR;;;AU)(A;;FA;;;WD)";
-constexpr wchar_t kTest2Dacl[] = L"D:PAI(A;;FA;;;BA)(A;;FA;;;AU)(A;;FA;;;WD)";
-
-constexpr wchar_t kBaseDirDacl[] = L"D:P(A;OICI;FA;;;WD)";
-constexpr wchar_t kTest1InheritedDacl[] = L"D:(A;ID;FA;;;WD)";
-constexpr wchar_t kBaseDir2Dacl[] = L"D:PAI(A;OICI;FR;;;AU)(A;OICI;FA;;;WD)";
-constexpr wchar_t kTest2InheritedDacl[] = L"D:AI(A;ID;FR;;;AU)(A;ID;FA;;;WD)";
-
-constexpr wchar_t kNoWriteDacDacl[] = L"D:(D;;WD;;;OW)(A;;FRSD;;;WD)";
-
-constexpr wchar_t kAuthenticatedUsersSid[] = L"AU";
-
-struct LocalFreeDeleter {
-  void operator()(void* ptr) const { ::LocalFree(ptr); }
-};
-
-std::wstring GetFileDacl(const base::FilePath& path) {
-  PSECURITY_DESCRIPTOR sd;
-  if (::GetNamedSecurityInfo(path.value().c_str(), SE_FILE_OBJECT,
-                             DACL_SECURITY_INFORMATION, nullptr, nullptr,
-                             nullptr, nullptr, &sd) != ERROR_SUCCESS) {
-    return std::wstring();
-  }
-  auto sd_ptr = std::unique_ptr<void, LocalFreeDeleter>(sd);
-  LPWSTR sddl;
-  if (!::ConvertSecurityDescriptorToStringSecurityDescriptor(
-          sd, SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &sddl, nullptr)) {
-    return std::wstring();
-  }
-  auto sddl_ptr = std::unique_ptr<void, LocalFreeDeleter>(sddl);
-  return sddl;
-}
-
-bool CreateWithDacl(const base::FilePath& path,
-                    const wchar_t* sddl,
-                    bool directory) {
-  PSECURITY_DESCRIPTOR sd;
-  if (!::ConvertStringSecurityDescriptorToSecurityDescriptor(
-          sddl, SDDL_REVISION_1, &sd, nullptr)) {
-    return false;
-  }
-  auto sd_ptr = std::unique_ptr<void, LocalFreeDeleter>(sd);
-  SECURITY_ATTRIBUTES security_attr = {};
-  security_attr.nLength = sizeof(security_attr);
-  security_attr.lpSecurityDescriptor = sd;
-  if (directory)
-    return !!::CreateDirectory(path.value().c_str(), &security_attr);
-
-  return base::win::ScopedHandle(::CreateFile(path.value().c_str(), GENERIC_ALL,
-                                              0, &security_attr, CREATE_ALWAYS,
-                                              0, nullptr))
-      .IsValid();
-}
-
-}  // namespace
-
-TEST(SetupUtilTest, GrantAccessToPathErrorCase) {
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-  base::FilePath path = temp_dir.GetPath().Append(L"test");
-  EXPECT_FALSE(GrantAccessToPath(path, {kAuthenticatedUsersSid},
-                                 FILE_GENERIC_READ, NO_INHERITANCE));
-  ASSERT_TRUE(CreateWithDacl(path, kBaseDacl, false));
-  EXPECT_TRUE(GrantAccessToPath(path, {kAuthenticatedUsersSid},
-                                FILE_GENERIC_READ, NO_INHERITANCE));
-  EXPECT_FALSE(
-      GrantAccessToPath(path, {L"X-1-2-3"}, FILE_GENERIC_READ, NO_INHERITANCE));
-  path = temp_dir.GetPath().Append(L"test_nowritedac");
-  ASSERT_TRUE(CreateWithDacl(path, kNoWriteDacDacl, false));
-  EXPECT_FALSE(GrantAccessToPath(path, {kAuthenticatedUsersSid},
-                                 FILE_GENERIC_READ, NO_INHERITANCE));
-}
-
-TEST(SetupUtilTest, GrantAccessToPathFile) {
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-  base::FilePath path = temp_dir.GetPath().Append(L"test");
-  ASSERT_TRUE(CreateWithDacl(path, kBaseDacl, false));
-  EXPECT_EQ(kBaseDacl, GetFileDacl(path));
-  EXPECT_TRUE(GrantAccessToPath(path, {kAuthenticatedUsersSid},
-                                FILE_GENERIC_READ, NO_INHERITANCE));
-  EXPECT_EQ(kTest1Dacl, GetFileDacl(path));
-  EXPECT_TRUE(GrantAccessToPath(path, {L"S-1-5-11", L"BA"}, GENERIC_ALL,
-                                NO_INHERITANCE));
-  EXPECT_EQ(kTest2Dacl, GetFileDacl(path));
-}
-
-TEST(SetupUtilTest, GrantAccessToPathDirectory) {
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-  base::FilePath path = temp_dir.GetPath().Append(L"testdir");
-  ASSERT_TRUE(CreateWithDacl(path, kBaseDirDacl, true));
-  EXPECT_EQ(kBaseDirDacl, GetFileDacl(path));
-  base::FilePath file_path = path.Append(L"test");
-  base::File file(file_path,
-                  base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
-  ASSERT_TRUE(file.IsValid());
-  file.Close();
-  EXPECT_EQ(kTest1InheritedDacl, GetFileDacl(file_path));
-  EXPECT_TRUE(GrantAccessToPath(path, {kAuthenticatedUsersSid},
-                                FILE_GENERIC_READ,
-                                OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE));
-  EXPECT_EQ(kBaseDir2Dacl, GetFileDacl(path));
-  EXPECT_EQ(kTest2InheritedDacl, GetFileDacl(file_path));
-}
-
 }  // namespace installer
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index b192429..cc93261 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3581,7 +3581,6 @@
     "../browser/component_updater/origin_trials_component_installer_unittest.cc",
     "../browser/component_updater/subresource_filter_component_installer_unittest.cc",
     "../browser/component_updater/trust_token_key_commitments_component_installer_unittest.cc",
-    "../browser/component_updater/zxcvbn_data_component_installer_unittest.cc",
     "../browser/content_index/content_index_provider_unittest.cc",
     "../browser/content_settings/content_settings_default_provider_unittest.cc",
     "../browser/content_settings/content_settings_mock_observer.cc",
@@ -4113,6 +4112,7 @@
       "../browser/browsing_data/chrome_browsing_data_lifetime_manager_unittest.cc",
       "../browser/component_updater/soda_component_installer_unittest.cc",
       "../browser/component_updater/soda_language_pack_component_installer_unittest.cc",
+      "../browser/component_updater/zxcvbn_data_component_installer_unittest.cc",
       "../browser/content_settings/generated_cookie_prefs_unittest.cc",
       "../browser/content_settings/generated_notification_pref_unittest.cc",
       "../browser/device_identity/device_oauth2_token_service_unittest.cc",
@@ -4357,7 +4357,6 @@
     "//third_party/metrics_proto",
     "//third_party/re2",
     "//third_party/webrtc_overrides:webrtc_component",
-    "//third_party/zxcvbn-cpp",
     "//ui/base:test_support",
     "//ui/display:test_support",
     "//ui/gfx:test_support",
@@ -4924,6 +4923,7 @@
       "//components/sync:test_support",
       "//services/metrics/public/cpp:ukm_builders",
       "//third_party/libaddressinput",
+      "//third_party/zxcvbn-cpp",
       "//ui/base/idle:test_support",
       "//ui/native_theme:test_support",
     ]
diff --git a/chrome/test/data/extensions/api_test/file_system/open_directory/test.js b/chrome/test/data/extensions/api_test/file_system/open_directory/test.js
index 3b60cd0..f3a2681 100644
--- a/chrome/test/data/extensions/api_test/file_system/open_directory/test.js
+++ b/chrome/test/data/extensions/api_test/file_system/open_directory/test.js
@@ -22,11 +22,12 @@
       var reader = directoryEntry.createReader();
       reader.readEntries(chrome.test.callback(function(entries) {
         // On POSIX systems DIR_HOME is overridden for this test and
-        // [.config] directory may be created there, ignore it
-        // See https://codereview.chromium.org/200473002/
+        // dotfiles may be created there, ignore them
+        // See https://codereview.chromium.org/200473002/ and
+        // https://crrev.com/c/2858114.
         var testEntry;
         entries.forEach(function(entry) {
-          if (entry.name != '.config') {
+          if (!entry.name.startsWith('.')) {
             chrome.test.assertEq(entry.name, 'open_existing.txt');
             testEntry = entry;
           }
diff --git a/chrome/test/data/webui/chromeos/diagnostics/fake_observables_test.js b/chrome/test/data/webui/chromeos/diagnostics/fake_observables_test.js
index f3dd54a..41110e5 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/fake_observables_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/fake_observables_test.js
@@ -84,4 +84,85 @@
     observables.trigger('ObserveFoo_OnFooUpdated');
     return resolver.promise;
   });
+
+  test('RegisterSimpleSharedObservable', () => {
+    observables.registerObservableWithArg('ObserveFoo_OnFooUpdated');
+    /** @type !Array<string> */
+    const expected = ['bar'];
+    observables.setObservableDataForArg(
+        'ObserveFoo_OnFooUpdated', 'foo', expected);
+
+    let resolver = new PromiseResolver();
+    observables.observeWithArg('ObserveFoo_OnFooUpdated', 'foo', (foo) => {
+      assertEquals(expected[0], foo);
+      resolver.resolve();
+    });
+
+    observables.triggerWithArg('ObserveFoo_OnFooUpdated', 'foo');
+    return resolver.promise;
+  });
+
+  test('SharedObservableRegisteredTwice', () => {
+    observables.registerObservableWithArg('ObserveFoo_OnFooUpdated');
+    /** @type !Array<string> */
+    const expected = ['bar1', 'bar2'];
+    observables.setObservableDataForArg(
+        'ObserveFoo_OnFooUpdated', 'bar', expected);
+    observables.setObservableDataForArg(
+        'ObserveFoo_OnFooUpdated', 'bar', expected);
+
+    // With 3 calls and 2 observable values the response should cycle
+    // 'bar1', 'bar2', 'bar1'
+    let resolver = new PromiseResolver();
+    const expectedCallCount = 3;
+    let callCount = 0;
+    observables.observeWithArg('ObserveFoo_OnFooUpdated', 'bar', (foo) => {
+      assertEquals(expected[callCount % expected.length], foo);
+      callCount++;
+      if (callCount === expectedCallCount) {
+        resolver.resolve();
+      }
+    });
+
+    // Trigger the observer three times.
+    observables.triggerWithArg('ObserveFoo_OnFooUpdated', 'bar');
+    observables.triggerWithArg('ObserveFoo_OnFooUpdated', 'bar');
+    observables.triggerWithArg('ObserveFoo_OnFooUpdated', 'bar');
+    return resolver.promise;
+  });
+
+  test('SharedObservableRegisteredTwiceWithNewData', () => {
+    observables.registerObservableWithArg('ObserveFoo_OnFooUpdated');
+    /** @type !Array<string> */
+    let expected = ['bar1', 'bar2'];
+    observables.setObservableDataForArg(
+        'ObserveFoo_OnFooUpdated', 'bar', expected);
+
+    // With 4 calls and 4 observable values the response should cycle
+    // 'bar1', 'bar2', 'bar3', 'bar4'
+    let resolver = new PromiseResolver();
+    const expectedCallCount = 4;
+    let callCount = 0;
+    observables.observeWithArg('ObserveFoo_OnFooUpdated', 'bar', (foo) => {
+      assertEquals(expected[callCount % expected.length], foo);
+      callCount++;
+      if (callCount === expectedCallCount) {
+        resolver.resolve();
+      }
+    });
+
+    // Trigger the observer twice.
+    observables.triggerWithArg('ObserveFoo_OnFooUpdated', 'bar');
+    observables.triggerWithArg('ObserveFoo_OnFooUpdated', 'bar');
+
+    // Update observable data.
+    expected = ['bar3', 'bar4'];
+    // Change observable data for 'ObserveFoo_OnFooUpdated_bar'.
+    observables.setObservableDataForArg(
+        'ObserveFoo_OnFooUpdated', 'bar', expected);
+
+    observables.triggerWithArg('ObserveFoo_OnFooUpdated', 'bar');
+    observables.triggerWithArg('ObserveFoo_OnFooUpdated', 'bar');
+    return resolver.promise;
+  });
 }
diff --git a/chrome/test/data/webui/chromeos/scanning/scanner_select_test.js b/chrome/test/data/webui/chromeos/scanning/scanner_select_test.js
index f99d757..0d0b622 100644
--- a/chrome/test/data/webui/chromeos/scanning/scanner_select_test.js
+++ b/chrome/test/data/webui/chromeos/scanning/scanner_select_test.js
@@ -4,11 +4,13 @@
 
 import 'chrome://scanning/scanner_select.js';
 
+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 {ScannerArr} from 'chrome://scanning/scanning_app_types.js';
+import {ScannerArr, ScannerInfo} from 'chrome://scanning/scanning_app_types.js';
 import {getScannerDisplayName, tokenToString} from 'chrome://scanning/scanning_app_util.js';
 
 import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {waitAfterNextRender} from '../../test_util.m.js';
 
 import {assertOrderedAlphabetically, createScanner} from './scanning_app_test_utils.js';
 
@@ -29,6 +31,7 @@
         document.createElement('scanner-select'));
     assertTrue(!!scannerSelect);
     scannerSelect.loaded = false;
+    scannerSelect.scannerInfoMap = new Map();
     document.body.appendChild(scannerSelect);
   });
 
@@ -73,4 +76,52 @@
     assertEquals(
         tokenToString(firstScannerId), scannerSelect.selectedScannerId);
   });
+
+  // Verify the last used scanner is selected if available.
+  test('selectLastUsedScanner', () => {
+    if (!loadTimeData.getBoolean('scanAppStickySettingsEnabled')) {
+      return;
+    }
+
+    const secondScannerIdString = tokenToString(secondScannerId);
+    const secondScannerInfo = /** @type {!ScannerInfo} */ ({
+      token: secondScannerId,
+      displayName: secondScannerName,
+    });
+    const scanners = [
+      createScanner(firstScannerId, firstScannerName),
+      createScanner(secondScannerId, secondScannerName),
+    ];
+
+    scannerSelect.scannerInfoMap.set(secondScannerIdString, secondScannerInfo);
+    scannerSelect.lastUsedScannerId = secondScannerIdString;
+    scannerSelect.scanners = scanners;
+
+    return waitAfterNextRender(scannerSelect).then(() => {
+      assertEquals(secondScannerIdString, scannerSelect.selectedScannerId);
+      assertEquals(secondScannerIdString, scannerSelect.$$('select').value);
+    });
+  });
+
+  // Verify the first scanner in the dropdown is selected when the last used
+  // scanner is not set.
+  test('selectFirtScanner', () => {
+    if (!loadTimeData.getBoolean('scanAppStickySettingsEnabled')) {
+      return;
+    }
+
+    const scanners = [
+      createScanner(secondScannerId, secondScannerName),
+      createScanner(firstScannerId, firstScannerName),
+    ];
+
+    scannerSelect.lastUsedScannerId = '';
+    scannerSelect.scanners = scanners;
+
+    const firstScannerIdString = tokenToString(firstScannerId);
+    return waitAfterNextRender(scannerSelect).then(() => {
+      assertEquals(firstScannerIdString, scannerSelect.selectedScannerId);
+      assertEquals(firstScannerIdString, scannerSelect.$$('select').value);
+    });
+  });
 }
diff --git a/chrome/test/data/webui/chromeos/scanning/scanning_app_test.js b/chrome/test/data/webui/chromeos/scanning/scanning_app_test.js
index 73d904fc..ccabab10 100644
--- a/chrome/test/data/webui/chromeos/scanning/scanning_app_test.js
+++ b/chrome/test/data/webui/chromeos/scanning/scanning_app_test.js
@@ -1203,4 +1203,36 @@
               '300', scanningApp.$$('#resolutionSelect').$$('select').value);
         });
   });
+
+  // Verify the last used scanner is selected from saved settings.
+  test('selectLastUsedScanner', () => {
+    if (!loadTimeData.getBoolean('scanAppStickySettingsEnabled')) {
+      return;
+    }
+
+    const scanSavedSettings = {
+      lastUsedScannerName: secondScannerName,
+      scanToPath: 'scan/to/path',
+      scanners: [{
+        name: secondScannerName,
+        lastScanDate: new Date(),
+        sourceName: ADF_DUPLEX,
+        fileType: ash.scanning.mojom.FileType.kPng,
+        colorMode: ash.scanning.mojom.ColorMode.kBlackAndWhite,
+        pageSize: ash.scanning.mojom.PageSize.kMax,
+        resolutionDpi: 75,
+      }],
+    };
+    testBrowserProxy.setSavedSettings(JSON.stringify(scanSavedSettings));
+
+    return initializeScanningApp(expectedScanners, capabilities)
+        .then(() => {
+          return getScannerCapabilities();
+        })
+        .then(() => {
+          assertEquals(
+              tokenToString(secondScannerId),
+              scanningApp.$$('#scannerSelect').$$('select').value);
+        });
+  });
 }
diff --git a/chrome/test/data/webui/print_preview/destination_settings_test.js b/chrome/test/data/webui/print_preview/destination_settings_test.js
index 93e36c7..d02fbd68a 100644
--- a/chrome/test/data/webui/print_preview/destination_settings_test.js
+++ b/chrome/test/data/webui/print_preview/destination_settings_test.js
@@ -35,7 +35,6 @@
   OpenDialog: 'open dialog',
   TwoAccountsRecentDestinations: 'two accounts recent destinations',
   UpdateRecentDestinations: 'update recent destinations',
-  ResetDestinationOnSignOut: 'reset destination on sign out',
   DisabledSaveAsPdf: 'disabled save as pdf',
   NoDestinations: 'no destinations',
 };
@@ -139,8 +138,7 @@
         destinationSettings.init(
             'FooDevice' /* printerName */, false /* pdfPrinterDisabled */,
             isDriveMounted,
-            '' /* serializedDefaultDestinationSelectionRulesStr */,
-            [] /* userAccounts */, true /* syncAvailable */);
+            '' /* serializedDefaultDestinationSelectionRulesStr */);
         assertFalse(dropdown.loaded);
 
         return eventToPromise(
@@ -213,20 +211,17 @@
     destinationSettings.appKioskMode = false;
     destinationSettings.init(
         '' /* printerName */, pdfPrinterDisabled, isDriveMounted,
-        '' /* serializedDefaultDestinationSelectionRulesStr */, initialAccounts,
-        true /* syncAvailable */);
+        '' /* serializedDefaultDestinationSelectionRulesStr */);
     destinationSettings.state = State.READY;
     destinationSettings.disabled = false;
   }
 
-  /** Simulates a user signing in to Chrome. */
+  // Simulates a user being signed in to cloud print. Call before initialize.
   function signIn() {
-    cloudPrintInterface.resetResolver('printer');
     cloudPrintInterface.setPrinter(getGoogleDriveDestination(defaultUser));
     const whenSearchDone = eventToPromise(
         CloudPrintInterfaceEventType.SEARCH_DONE,
         cloudPrintInterface.getEventTarget());
-    webUIListenerCallback('user-accounts-updated', [defaultUser]);
     return whenSearchDone.then(() => waitBeforeNextRender(destinationSettings));
   }
 
@@ -258,8 +253,15 @@
   test(
       assert(destination_settings_test.TestNames.NoRecentDestinations),
       function() {
+        const whenCloudPrintDone = signIn();
         initialize();
-        return nativeLayer.whenCalled('getPrinterCapabilities')
+        return Promise
+            .all([
+              nativeLayer.whenCalled('getPrinterCapabilities'),
+              // <if expr="not chromeos">
+              whenCloudPrintDone,
+              // </if>
+            ])
             .then(() => {
               // This will result in the destination store setting the Save as
               // PDF destination.
@@ -268,16 +270,6 @@
                   destinationSettings.destination.id);
               assertFalse(
                   destinationSettings.$$('#destinationSelect').disabled);
-              const dropdownItems = ['Save as PDF/local/'];
-              if (isChromeOS) {
-                dropdownItems.push(dropdownDriveDestination);
-              }
-              assertDropdownItems(dropdownItems);
-
-              // If the user is signed in, Save to Drive should be displayed.
-              return signIn();
-            })
-            .then(() => {
               assertDropdownItems([
                 'Save as PDF/local/',
                 dropdownDriveDestination,
@@ -293,8 +285,12 @@
         recentDestinations = destinations.slice(0, 5).map(
             destination => makeRecentDestination(destination));
 
-        const whenCapabilitiesDone =
-            nativeLayer.whenCalled('getPrinterCapabilities');
+        const whenCapabilitiesDone = Promise.all([
+          // <if expr="not chromeos">
+          signIn(),
+          // </if>
+          nativeLayer.whenCalled('getPrinterCapabilities'),
+        ]);
         initialize();
 
         // Wait for the destinations to be inserted into the store.
@@ -308,21 +304,6 @@
               assertEquals('ID1', destinationSettings.destination.id);
               assertFalse(
                   destinationSettings.$$('#destinationSelect').disabled);
-              const dropdownItems = [
-                makeLocalDestinationKey('ID1'),
-                makeLocalDestinationKey('ID2'),
-                makeLocalDestinationKey('ID3'),
-                'Save as PDF/local/',
-              ];
-              if (isChromeOS) {
-                dropdownItems.push(dropdownDriveDestination);
-              }
-              assertDropdownItems(dropdownItems);
-
-              // If the user is signed in, Save to Drive should be displayed.
-              return signIn();
-            })
-            .then(() => {
               assertDropdownItems([
                 makeLocalDestinationKey('ID1'),
                 makeLocalDestinationKey('ID2'),
@@ -367,17 +348,6 @@
                 dropdownItems.push(dropdownDriveDestination);
               }
               assertDropdownItems(dropdownItems);
-
-              // If the user is signed in, Save to Drive should be displayed.
-              return signIn();
-            })
-            .then(() => {
-              assertDropdownItems([
-                makeLocalDestinationKey('ID1'),
-                makeLocalDestinationKey('ID3'),
-                'Save as PDF/local/',
-                dropdownDriveDestination,
-              ]);
             });
       });
 
@@ -411,18 +381,6 @@
             dropdownItems.push(dropdownDriveDestination);
           }
           assertDropdownItems(dropdownItems);
-
-          // If the user is signed in, Save to Drive should be displayed.
-          return signIn();
-        })
-        .then(() => {
-          assertDropdownItems([
-            makeLocalDestinationKey('ID1'),
-            makeLocalDestinationKey('ID3'),
-            makeLocalDestinationKey('ID4'),
-            'Save as PDF/local/',
-            dropdownDriveDestination,
-          ]);
         });
   });
 
@@ -436,8 +394,12 @@
         recentDestinations.splice(
             1, 1,
             makeRecentDestination(getGoogleDriveDestination(defaultUser)));
-        const whenCapabilitiesDone =
-            nativeLayer.whenCalled('getPrinterCapabilities');
+        const whenCapabilitiesDone = Promise.all([
+          // <if expr="not chromeos">
+          signIn(),
+          // </if>
+          nativeLayer.whenCalled('getPrinterCapabilities'),
+        ]);
         initialize();
 
         return whenCapabilitiesDone
@@ -451,23 +413,6 @@
               assertFalse(
                   destinationSettings.$$('#destinationSelect').disabled);
 
-              // Google Drive does not show up even though it is recent, since
-              // the user is not signed in and the destination is not available.
-              const dropdownItems = [
-                makeLocalDestinationKey('ID1'),
-                makeLocalDestinationKey('ID3'),
-                makeLocalDestinationKey('ID4'),
-                'Save as PDF/local/',
-              ];
-              if (isChromeOS) {
-                dropdownItems.push(dropdownDriveDestination);
-              }
-              assertDropdownItems(dropdownItems);
-
-              // If the user is signed in, Save to Drive should be displayed.
-              return signIn();
-            })
-            .then(() => {
               assertDropdownItems([
                 makeLocalDestinationKey('ID1'),
                 makeLocalDestinationKey('ID3'),
@@ -581,8 +526,12 @@
         recentDestinations.splice(
             1, 1,
             makeRecentDestination(getGoogleDriveDestination(defaultUser)));
-        const whenCapabilitiesDone =
-            nativeLayer.whenCalled('getPrinterCapabilities');
+        const whenCapabilitiesDone = Promise.all([
+          // <if expr="not chromeos">
+          signIn(),
+          // </if>
+          nativeLayer.whenCalled('getPrinterCapabilities'),
+        ]);
         initialize();
         const dropdown = destinationSettings.$$('#destinationSelect');
 
@@ -594,22 +543,6 @@
               // This will result in the destination store setting the most
               // recent destination.
               assertEquals('ID1', destinationSettings.destination.id);
-              const dropdownItems = [
-                makeLocalDestinationKey('ID1'),
-                makeLocalDestinationKey('ID3'),
-                makeLocalDestinationKey('ID4'),
-                'Save as PDF/local/',
-              ];
-              if (isChromeOS) {
-                dropdownItems.push(dropdownDriveDestination);
-              }
-              assertDropdownItems(dropdownItems);
-              assertFalse(dropdown.disabled);
-
-              // If the user is signed in, Save to Drive should be displayed.
-              return signIn();
-            })
-            .then(() => {
               assertDropdownItems([
                 makeLocalDestinationKey('ID1'),
                 makeLocalDestinationKey('ID3'),
@@ -903,65 +836,6 @@
             });
       });
 
-  // Tests that the dropdown resets the destination if the user signs out of
-  // the account associated with the curret one.
-  test(
-      assert(destination_settings_test.TestNames.ResetDestinationOnSignOut),
-      function() {
-        recentDestinations = destinations.slice(0, 5).map(
-            destination => makeRecentDestination(destination));
-        const driveDestination = getGoogleDriveDestination(defaultUser);
-        recentDestinations.splice(
-            0, 1, makeRecentDestination(driveDestination));
-        cloudPrintInterface.setPrinter(getGoogleDriveDestination(defaultUser));
-        initialAccounts = [defaultUser];
-        initialize();
-
-        return cloudPrintInterface.whenCalled('printer').then(
-            () => {
-              assertEquals(
-                  Destination.GooglePromotedId.DOCS,
-                  destinationSettings.destination.id);
-              assertFalse(
-                  destinationSettings.$$('#destinationSelect').disabled);
-              assertDropdownItems([
-                makeLocalDestinationKey('ID2'),
-                makeLocalDestinationKey('ID3'),
-                makeLocalDestinationKey('ID4'),
-                'Save as PDF/local/',
-                dropdownDriveDestination,
-              ]);
-
-              // Sign out.
-              webUIListenerCallback('user-accounts-updated', []);
-              flush();
-
-              assertEquals('ID2', destinationSettings.destination.id);
-              assertFalse(
-                  destinationSettings.$$('#destinationSelect').disabled);
-              const dropdownItems = [
-                makeLocalDestinationKey('ID2'),
-                makeLocalDestinationKey('ID3'),
-                makeLocalDestinationKey('ID4'),
-                'Save as PDF/local/',
-              ];
-              if (isChromeOS) {
-                dropdownItems.push(dropdownDriveDestination);
-              }
-              assertDropdownItems(dropdownItems);
-
-              // Now that the selected destination is local, signing in and out
-              // shouldn't impact it.
-              webUIListenerCallback('user-accounts-updated', [defaultUser]);
-              flush();
-              assertEquals('ID2', destinationSettings.destination.id);
-
-              webUIListenerCallback('user-accounts-updated', []);
-              flush();
-              assertEquals('ID2', destinationSettings.destination.id);
-            });
-      });
-
   // Tests that disabling the Save as PDF destination hides the corresponding
   // dropdown item.
   test(
diff --git a/chrome/test/data/webui/print_preview/destination_settings_test_cros.js b/chrome/test/data/webui/print_preview/destination_settings_test_cros.js
index bb777223..fc28a23 100644
--- a/chrome/test/data/webui/print_preview/destination_settings_test_cros.js
+++ b/chrome/test/data/webui/print_preview/destination_settings_test_cros.js
@@ -92,8 +92,7 @@
     destinationSettings.appKioskMode = false;
     destinationSettings.init(
         '' /* printerName */, false, isDriveMounted,
-        '' /* serializedDefaultDestinationSelectionRulesStr */,
-        [] /* accounts */, true /* syncAvailable */);
+        '' /* serializedDefaultDestinationSelectionRulesStr */);
     destinationSettings.state = State.READY;
     destinationSettings.disabled = false;
   }
diff --git a/chrome/test/data/webui/print_preview/destination_store_test.js b/chrome/test/data/webui/print_preview/destination_store_test.js
index 3ca43af..8f1689d 100644
--- a/chrome/test/data/webui/print_preview/destination_store_test.js
+++ b/chrome/test/data/webui/print_preview/destination_store_test.js
@@ -53,6 +53,9 @@
   /** @type {!NativeInitialSettings} */
   let initialSettings;
 
+  /** @type {!Array<string>} */
+  let userAccounts = [];
+
   /** @type {!Array<!LocalDestinationInfo>} */
   let localDestinations = [];
 
@@ -79,7 +82,6 @@
     // </if>
 
     initialSettings = getDefaultInitialSettings();
-    initialSettings.userAccounts = [];
     localDestinations = [];
     destinations = getDestinations(localDestinations);
   });
@@ -109,10 +111,6 @@
         DestinationStore.EventType.DESTINATION_SELECT, function() {
           numPrintersSelected++;
         });
-    destinationStore.setActiveUser(
-        initialSettings.userAccounts.length > 0 ?
-            initialSettings.userAccounts[0] :
-            '');
 
     // Initialize.
     const recentDestinations = initialSettings.serializedAppStateStr ?
@@ -126,6 +124,11 @@
         initialSettings.printerName,
         initialSettings.serializedDefaultDestinationSelectionRulesStr,
         recentDestinations);
+
+    if (userAccounts) {
+      destinationStore.setActiveUser(userAccounts[0]);
+      destinationStore.reloadUserCookieBasedDestinations(userAccounts[0]);
+    }
     return opt_expectPrinterFailure ? Promise.resolve() : Promise.race([
       nativeLayer.whenCalled('getPrinterCapabilities'), whenCapabilitiesReady
     ]);
@@ -346,7 +349,7 @@
           version: 2,
           recentDestinations: [recentDestination],
         });
-        initialSettings.userAccounts = ['foo@chromium.org'];
+        userAccounts = ['foo@chromium.org'];
 
         return setInitialSettings(false).then(function(args) {
           assertEquals('FooDevice', args.destinationId);
@@ -368,7 +371,6 @@
       recentDestinations: [recentDestination],
     });
 
-    DestinationStore.AUTO_SELECT_TIMEOUT = 0;
     return setInitialSettings(false)
         .then(function() {
           assertEquals(
@@ -410,8 +412,7 @@
           version: 2,
           recentDestinations: recentDestinations,
         });
-        initialSettings.userAccounts = [account1, account2];
-        initialSettings.syncAvailable = true;
+        userAccounts = [account1, account2];
 
         const waitForPrinterDone = () => {
           return eventToPromise(
@@ -562,8 +563,7 @@
           version: 2,
           recentDestinations: recentDestinations,
         });
-        initialSettings.userAccounts = [account1, account2];
-        initialSettings.syncAvailable = true;
+        userAccounts = [account1, account2];
 
         const waitForPrinterDone = () => {
           return eventToPromise(
diff --git a/chrome/test/data/webui/print_preview/print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/print_preview_ui_browsertest.js
index b0314989..a688733 100644
--- a/chrome/test/data/webui/print_preview/print_preview_ui_browsertest.js
+++ b/chrome/test/data/webui/print_preview/print_preview_ui_browsertest.js
@@ -1545,13 +1545,6 @@
           destination_settings_test.TestNames.UpdateRecentDestinations);
     });
 
-TEST_F(
-    'PrintPreviewDestinationSettingsTest', 'ResetDestinationOnSignOut',
-    function() {
-      this.runMochaTest(
-          destination_settings_test.TestNames.ResetDestinationOnSignOut);
-    });
-
 TEST_F('PrintPreviewDestinationSettingsTest', 'DisabledSaveAsPdf', function() {
   this.runMochaTest(destination_settings_test.TestNames.DisabledSaveAsPdf);
 });
diff --git a/chrome/test/data/webui/print_preview/user_manager_test.js b/chrome/test/data/webui/print_preview/user_manager_test.js
index 6a52d883..4976650 100644
--- a/chrome/test/data/webui/print_preview/user_manager_test.js
+++ b/chrome/test/data/webui/print_preview/user_manager_test.js
@@ -2,15 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {CloudPrintInterfaceImpl, DestinationStore, InvitationStore, NativeLayer, NativeLayerImpl} from 'chrome://print/print_preview.js';
+import {CloudPrintInterfaceEventType, CloudPrintInterfaceImpl, DestinationStore, InvitationStore, NativeLayer, NativeLayerImpl} from 'chrome://print/print_preview.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
-import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
+import {isChromeOS, webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 import {CloudPrintInterfaceStub} from 'chrome://test/print_preview/cloud_print_interface_stub.js';
 import {NativeLayerStub} from 'chrome://test/print_preview/native_layer_stub.js';
 import {createDestinationStore, getDestinations, getGoogleDriveDestination, setupTestListenerElement} from 'chrome://test/print_preview/print_preview_test_utils.js';
 
+import {eventToPromise} from '../test_util.m.js';
+
 // <if expr="chromeos">
 import {setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
+
 // </if>
 
 suite('UserManagerTest', function() {
@@ -46,6 +49,10 @@
     // </if>
     cloudPrintInterface = new CloudPrintInterfaceStub();
     CloudPrintInterfaceImpl.instance_ = cloudPrintInterface;
+    cloudPrintInterface.setPrinter(getGoogleDriveDestination(account1));
+    const whenSearchDone = eventToPromise(
+        CloudPrintInterfaceEventType.SEARCH_DONE,
+        cloudPrintInterface.getEventTarget());
 
     userManager = document.createElement('print-preview-user-manager');
 
@@ -55,81 +62,40 @@
     const localDestinations = [];
     const destinations = getDestinations(localDestinations);
     nativeLayer.setLocalDestinations(localDestinations);
-    destinationStore.init(
-        false /* pdfPrinterDisabled */, true /* isDriveMounted */,
-        'FooDevice' /* printerName */,
-        '' /* serializedDefaultDestinationSelectionRulesStr */, []);
 
     // Set up user manager
     userManager.appKioskMode = false;
     userManager.destinationStore = destinationStore;
     userManager.invitationStore = new InvitationStore();
     userManager.shouldReloadCookies = false;
+    userManager.cloudPrintDisabled = false;
     document.body.appendChild(userManager);
+
+    destinationStore.init(
+        false /* pdfPrinterDisabled */, true /* isDriveMounted */,
+        'FooDevice' /* printerName */,
+        '' /* serializedDefaultDestinationSelectionRulesStr */, []);
+
+    // <if expr="not chromeos">
+    return whenSearchDone;
+    // </if>
   });
 
   // Checks that initializing and updating user accounts works as expected.
-  test('update users', function() {
-    // Set up a cloud printer for each account.
-    cloudPrintInterface.setPrinter(getGoogleDriveDestination(account1));
-    cloudPrintInterface.setPrinter(getGoogleDriveDestination(account2));
-
-    assertEquals(undefined, userManager.activeUser);
-
-    userManager.cloudPrintDisabled = false;
-    userManager.initUserAccounts([], true /* syncAvailable */);
-    assertEquals('', userManager.activeUser);
-    assertEquals(0, userManager.users.length);
-    assertEquals(0, cloudPrintInterface.getCallCount('search'));
-
-    // Simulate signing in and out of accounts. This should update the list of
-    // users and the active user, and should refresh the list of cloud printers
-    // so that we can confirm Google Drive is available.
-    webUIListenerCallback('user-accounts-updated', [account1]);
-    assertEquals(account1, userManager.activeUser);
-    assertEquals(1, userManager.users.length);
-    assertEquals(1, cloudPrintInterface.getCallCount('search'));
-
-    webUIListenerCallback('user-accounts-updated', [account1, account2]);
-    assertEquals(account1, userManager.activeUser);
-    assertEquals(2, userManager.users.length);
-    // Still 1 search since the active user didn't change.
-    assertEquals(1, cloudPrintInterface.getCallCount('search'));
-
-    webUIListenerCallback('user-accounts-updated', [account2]);
-    assertEquals(account2, userManager.activeUser);
-    assertEquals(1, userManager.users.length);
-    assertEquals(2, cloudPrintInterface.getCallCount('search'));
-
-    webUIListenerCallback('user-accounts-updated', []);
-    assertEquals('', userManager.activeUser);
-    assertEquals(0, userManager.users.length);
-    assertEquals(2, cloudPrintInterface.getCallCount('search'));
-  });
-
-  // Checks that initializing and updating user accounts works as expected
-  // when sync is unavailable.
   test('update users without sync', function() {
-    const whenCalled = cloudPrintInterface.whenCalled('search');
-    assertEquals(undefined, userManager.activeUser);
+    // Destination store will trigger a call to cloud print to determine the
+    // state of the Drive printer, which will in turn set the account state.
+    // This doesn't happen on Chrome OS which uses a different Drive.
+    userManager.initUserAccounts();
+    assertEquals(isChromeOS ? undefined : account1, userManager.activeUser);
+    assertEquals(isChromeOS ? 0 : 1, userManager.users.length);
+    assertEquals(
+        isChromeOS ? 0 : 2, cloudPrintInterface.getCallCount('search'));
 
-    userManager.cloudPrintDisabled = false;
-    userManager.initUserAccounts([], false /* syncAvailable */);
-    return whenCalled
-        .then(() => {
-          assertEquals(undefined, userManager.activeUser);
-          assertEquals(0, userManager.users.length);
-          assertEquals(1, cloudPrintInterface.getCallCount('search'));
-          cloudPrintInterface.resetResolver('search');
-
-          // Simulate signing into an account by setting a cloud printer for
-          // it and firing the 'check-for-account-update' listener.
-          // This should update the list of users and the active user and
-          // trigger a call to search.
-          cloudPrintInterface.setPrinter(getGoogleDriveDestination(account1));
-          webUIListenerCallback('check-for-account-update');
-          return cloudPrintInterface.whenCalled('search');
-        })
+    // Start reloading everything. This will set up the account on Chrome OS,
+    // which doesn't use the Google Cloud Print Drive in the dropdown.
+    destinationStore.startLoadAllDestinations();
+    return cloudPrintInterface.whenCalled('search')
         .then(() => {
           assertEquals(account1, userManager.activeUser);
           assertEquals(1, userManager.users.length);
@@ -147,39 +113,4 @@
           assertEquals(1, cloudPrintInterface.getCallCount('search'));
         });
   });
-
-  test('update active user', function() {
-    // Set up a cloud printer for each account.
-    cloudPrintInterface.setPrinter(getGoogleDriveDestination(account1));
-    cloudPrintInterface.setPrinter(getGoogleDriveDestination(account2));
-    userManager.cloudPrintDisabled = false;
-    userManager.initUserAccounts(
-        [account1, account2], true /* syncAvailable */);
-    assertEquals(account1, userManager.activeUser);
-    assertEquals(2, userManager.users.length);
-
-    // Search is called once at startup.
-    return cloudPrintInterface.whenCalled('search')
-        .then(() => {
-          assertEquals(1, cloudPrintInterface.getCallCount('search'));
-
-          // Changing the active user results in a new search call.
-          cloudPrintInterface.resetResolver('search');
-          const whenSearchCalled = cloudPrintInterface.whenCalled('search');
-          userManager.updateActiveUser(account2);
-          assertEquals(account2, userManager.activeUser);
-          assertEquals(2, userManager.users.length);
-          return whenSearchCalled;
-        })
-        .then(() => {
-          assertEquals(1, cloudPrintInterface.getCallCount('search'));
-
-          // No new search call when switching back.
-          userManager.shouldReloadCookies = true;
-          userManager.updateActiveUser(account1);
-          assertEquals(account1, userManager.activeUser);
-          assertEquals(2, userManager.users.length);
-          assertEquals(1, cloudPrintInterface.getCallCount('search'));
-        });
-  });
 });
diff --git a/chrome/test/data/webui/settings/OWNERS b/chrome/test/data/webui/settings/OWNERS
index 01ccda0..1dafd5d3 100644
--- a/chrome/test/data/webui/settings/OWNERS
+++ b/chrome/test/data/webui/settings/OWNERS
@@ -2,6 +2,8 @@
 
 per-file collapse_radio_button_tests.js=sauski@google.com
 per-file cookies_page_test.js=sauski@google.com
+per-file clear_browsing_data_test.js=sauski@google.com
+per-file do_not_track_toggle_test.js=sauski@google.com
 per-file do_not_track_toggle_test.js=sauski@google.com
 per-file metrics_reporting_tests.js=sauski@google.com
 per-file personalization_options_test.js=sauski@google.com
diff --git a/chrome/test/data/webui/settings/chromeos/internet_page_tests.js b/chrome/test/data/webui/settings/chromeos/internet_page_tests.js
index 6b947cb2..c18b3381 100644
--- a/chrome/test/data/webui/settings/chromeos/internet_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/internet_page_tests.js
@@ -75,8 +75,13 @@
     if (showPSimFlow) {
       params.append('showPsimFlow', 'true');
     }
+
+    // Pretend that we initially started on the INTERNET_NETWORKS route with the
+    // params.
     settings.Router.getInstance().navigateTo(
         settings.routes.INTERNET_NETWORKS, params);
+    internetPage.currentRouteChanged(
+        settings.routes.INTERNET_NETWORKS, undefined);
 
     // Update the device state here to trigger an
     // attemptShowCellularSetupDialog_() call.
@@ -488,8 +493,13 @@
     params.append(
         'type', OncMojo.getNetworkTypeString(mojom.NetworkType.kCellular));
     params.append('showSimLockDialog', true);
+
+    // Pretend that we initially started on the INTERNET_NETWORKS route with the
+    // params.
     settings.Router.getInstance().navigateTo(
         settings.routes.INTERNET_NETWORKS, params);
+    internetPage.currentRouteChanged(
+        settings.routes.INTERNET_NETWORKS, undefined);
 
     await flushAsync();
 
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index a690a86..616650b 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -148,6 +148,13 @@
           .grep(languages_subpage_details_tests.TestNames.AlwaysTranslateDialog)
           .run();
     });
+
+TEST_F(
+    'CrSettingsLanguagesSubpageDetailedV3Test', 'NeverTranslateDialog',
+    function() {
+      mocha.grep(languages_subpage_details_tests.TestNames.NeverTranslateDialog)
+          .run();
+    });
 GEN('#endif');
 
 // eslint-disable-next-line no-var
diff --git a/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js b/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
deleted file mode 100644
index 06809a4..0000000
--- a/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
+++ /dev/null
@@ -1,640 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/** @fileoverview Tests for shared Polymer 3 elements. */
-
-// Polymer BrowserTest fixture.
-GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
-
-GEN('#include "build/branding_buildflags.h"');
-GEN('#include "build/chromeos_buildflags.h"');
-GEN('#include "chrome/common/chrome_features.h"');
-GEN('#include "components/autofill/core/common/autofill_features.h"');
-GEN('#include "components/password_manager/core/common/password_manager_features.h"');
-GEN('#include "content/public/test/browser_test.h"');
-
-/** Test fixture for shared Polymer 3 elements. */
-// eslint-disable-next-line no-var
-var CrSettingsV3BrowserTest = class extends PolymerTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings';
-  }
-
-  /** @override */
-  get featureList() {
-    if (!this.featureListInternal.enabled &&
-        !this.featureListInternal.disabled) {
-      return null;
-    }
-    return this.featureListInternal;
-  }
-
-  /** @return {!{enabled: !Array<string>, disabled: !Array<string>}} */
-  get featureListInternal() {
-    return {};
-  }
-};
-
-// eslint-disable-next-line no-var
-var CrSettingsAboutPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/about_page_tests.js';
-  }
-};
-
-TEST_F('CrSettingsAboutPageV3Test', 'AboutPage', function() {
-  mocha.grep('AboutPageTest_AllBuilds').run();
-});
-
-GEN('#if BUILDFLAG(GOOGLE_CHROME_BRANDING)');
-TEST_F('CrSettingsAboutPageV3Test', 'AboutPage_OfficialBuild', function() {
-  mocha.grep('AboutPageTest_OfficialBuilds').run();
-});
-GEN('#endif');
-
-// eslint-disable-next-line no-var
-var CrSettingsAvatarIconV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/avatar_icon_test.js';
-  }
-};
-
-TEST_F('CrSettingsAvatarIconV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsLanguagesPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/languages_page_tests.js';
-  }
-};
-
-TEST_F('CrSettingsLanguagesPageV3Test', 'Spellcheck', function() {
-  mocha.grep(languages_page_tests.TestNames.Spellcheck).run();
-});
-
-GEN('#if BUILDFLAG(GOOGLE_CHROME_BRANDING)');
-TEST_F('CrSettingsLanguagesPageV3Test', 'SpellcheckOfficialBuild', function() {
-  mocha.grep(languages_page_tests.TestNames.SpellcheckOfficialBuild).run();
-});
-GEN('#endif');
-
-GEN('#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)');
-TEST_F(
-    'CrSettingsLanguagesPageV3Test', 'RestructuredLanguageSettings',
-    function() {
-      mocha.grep(languages_page_tests.TestNames.RestructuredLanguageSettings)
-          .run();
-    });
-GEN('#endif');
-
-GEN('#if BUILDFLAG(IS_CHROMEOS_ASH)');
-TEST_F(
-    'CrSettingsLanguagesPageV3Test', 'ChromeOSLanguagesSettingsUpdate',
-    function() {
-      mocha.grep(languages_page_tests.TestNames.ChromeOSLanguagesSettingsUpdate)
-          .run();
-    });
-GEN('#endif');
-
-// eslint-disable-next-line no-var
-var CrSettingsLanguagesSubpageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/languages_subpage_tests.js';
-  }
-};
-
-TEST_F('CrSettingsLanguagesSubpageV3Test', 'AddLanguagesDialog', function() {
-  mocha.grep(languages_subpage_tests.TestNames.AddLanguagesDialog).run();
-});
-
-TEST_F('CrSettingsLanguagesSubpageV3Test', 'LanguageMenu', function() {
-  mocha.grep(languages_subpage_tests.TestNames.LanguageMenu).run();
-});
-
-GEN('#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)');
-// eslint-disable-next-line no-var
-var CrSettingsLanguagesSubpageDetailedV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/languages_subpage_details_tests.js';
-  }
-};
-
-TEST_F(
-    'CrSettingsLanguagesSubpageDetailedV3Test', 'AlwaysTranslateDialog',
-    function() {
-      mocha
-          .grep(languages_subpage_details_tests.TestNames.AlwaysTranslateDialog)
-          .run();
-    });
-
-TEST_F(
-    'CrSettingsLanguagesSubpageDetailedV3Test', 'NeverTranslateDialog',
-    function() {
-      mocha.grep(languages_subpage_details_tests.TestNames.NeverTranslateDialog)
-          .run();
-    });
-
-GEN('#endif');
-
-// eslint-disable-next-line no-var
-var CrSettingsLanguagesPageMetricsV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/languages_page_metrics_test_browser.js';
-  }
-};
-
-TEST_F(
-    'CrSettingsLanguagesPageMetricsV3Test', 'LanguagesPageMetricsBrowser',
-    function() {
-      runMochaSuite('LanguagesPageMetricsBrowser');
-    });
-
-// eslint-disable-next-line no-var
-var CrSettingsClearBrowsingDataV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/clear_browsing_data_test.js';
-  }
-};
-
-// TODO(crbug.com/1107652): Flaky on Mac.
-GEN('#if defined(OS_MAC)');
-GEN('#define MAYBE_ClearBrowsingDataAllPlatforms DISABLED_ClearBrowsingDataAllPlatforms');
-GEN('#else');
-GEN('#define MAYBE_ClearBrowsingDataAllPlatforms ClearBrowsingDataAllPlatforms');
-GEN('#endif');
-TEST_F(
-    'CrSettingsClearBrowsingDataV3Test', 'MAYBE_ClearBrowsingDataAllPlatforms',
-    function() {
-      runMochaSuite('ClearBrowsingDataAllPlatforms');
-    });
-
-TEST_F('CrSettingsClearBrowsingDataV3Test', 'InstalledApps', () => {
-  runMochaSuite('InstalledApps');
-});
-
-GEN('#if !BUILDFLAG(IS_CHROMEOS_ASH)');
-TEST_F(
-    'CrSettingsClearBrowsingDataV3Test', 'ClearBrowsingDataDesktop',
-    function() {
-      runMochaSuite('ClearBrowsingDataDesktop');
-    });
-GEN('#endif');
-
-
-// eslint-disable-next-line no-var
-var CrSettingsMainPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/settings_main_test.js';
-  }
-};
-
-// Copied from Polymer 2 version of tests:
-// Times out on Windows Tests (dbg). See https://crbug.com/651296.
-// Times out / crashes on chromium.linux/Linux Tests (dbg) crbug.com/667882
-// Flaky everywhere crbug.com/1197768
-TEST_F('CrSettingsMainPageV3Test', 'DISABLED_MainPageV3', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsAutofillPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/autofill_page_test.js';
-  }
-};
-
-TEST_F('CrSettingsAutofillPageV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsAutofillSectionCompanyEnabledV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/autofill_section_test.js';
-  }
-};
-
-TEST_F('CrSettingsAutofillSectionCompanyEnabledV3Test', 'All', function() {
-  // Use 'EnableCompanyName' to inform tests that the feature is enabled.
-  const loadTimeDataOverride = {};
-  loadTimeDataOverride['EnableCompanyName'] = true;
-  loadTimeDataOverride['showHonorific'] = true;
-  loadTimeData.overrideValues(loadTimeDataOverride);
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsPasswordsSectionV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/passwords_section_test.js';
-  }
-};
-
-// Flaky on Debug builds https://crbug.com/1090931
-GEN('#if !defined(NDEBUG)');
-GEN('#define MAYBE_All DISABLED_All');
-GEN('#else');
-GEN('#define MAYBE_All All');
-GEN('#endif');
-TEST_F('CrSettingsPasswordsSectionV3Test', 'MAYBE_All', function() {
-  mocha.run();
-});
-GEN('#undef MAYBE_All');
-
-// eslint-disable-next-line no-var
-var CrSettingsMultiStorePasswordUiEntryV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/multi_store_password_ui_entry_test.js';
-  }
-};
-
-TEST_F('CrSettingsMultiStorePasswordUiEntryV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsPasswordsDeviceSectionV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/passwords_device_section_test.js';
-  }
-
-  /** @override */
-  get featureListInternal() {
-    return {
-      enabled: ['password_manager::features::kEnablePasswordsAccountStorage']
-    };
-  }
-};
-
-TEST_F('CrSettingsPasswordsDeviceSectionV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsMultiStoreExceptionEntryV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/multi_store_exception_entry_test.js';
-  }
-};
-
-TEST_F('CrSettingsMultiStoreExceptionEntryV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsPasswordsCheckV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/password_check_test.js';
-  }
-
-  /** @override */
-  get featureList() {
-    return {enabled: ['features::kSafetyCheckWeakPasswords']};
-  }
-};
-
-// Flaky on Mac builds https://crbug.com/1143801
-GEN('#if defined(OS_MAC)');
-GEN('#define MAYBE_All DISABLED_All');
-GEN('#else');
-GEN('#define MAYBE_All All');
-GEN('#endif');
-TEST_F('CrSettingsPasswordsCheckV3Test', 'MAYBE_All', function() {
-  mocha.run();
-});
-GEN('#undef MAYBE_All');
-
-// eslint-disable-next-line no-var
-var CrSettingsSafetyCheckPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/safety_check_page_test.js';
-  }
-};
-
-TEST_F('CrSettingsSafetyCheckPageV3Test', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsSafetyCheckChromeCleanerV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/safety_check_chrome_cleaner_test.js';
-  }
-
-  /** @override */
-  get featureListInternal() {
-    return {
-      enabled: [
-        'features::kSafetyCheckChromeCleanerChild',
-      ],
-    };
-  }
-};
-
-GEN('#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)');
-TEST_F('CrSettingsSafetyCheckChromeCleanerV3Test', 'All', function() {
-  mocha.run();
-});
-GEN('#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)');
-
-// eslint-disable-next-line no-var
-var CrSettingsSiteListV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/site_list_tests.js';
-  }
-};
-
-// Copied from Polymer 2 test:
-// TODO(crbug.com/929455): flaky, fix.
-TEST_F('CrSettingsSiteListV3Test', 'DISABLED_SiteList', function() {
-  runMochaSuite('SiteList');
-});
-
-// TODO(crbug.com/929455): When the bug is fixed, merge
-// SiteListEmbargoedOrigin into SiteList
-TEST_F('CrSettingsSiteListV3Test', 'SiteListEmbargoedOrigin', function() {
-  runMochaSuite('SiteListEmbargoedOrigin');
-});
-
-TEST_F('CrSettingsSiteListV3Test', 'EditExceptionDialog', function() {
-  runMochaSuite('EditExceptionDialog');
-});
-
-TEST_F('CrSettingsSiteListV3Test', 'AddExceptionDialog', function() {
-  runMochaSuite('AddExceptionDialog');
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsSiteDetailsV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/site_details_tests.js';
-  }
-};
-
-// Disabling on debug due to flaky timeout on Win7 Tests (dbg)(1) bot.
-// https://crbug.com/825304 - later for other platforms in crbug.com/1021219.
-// Disabling on Linux CFI due to flaky timeout (crbug.com/1031960).
-GEN('#if (!defined(NDEBUG)) || ((defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(IS_CFI))');
-GEN('#define MAYBE_SiteDetails DISABLED_SiteDetails');
-GEN('#else');
-GEN('#define MAYBE_SiteDetails SiteDetails');
-GEN('#endif');
-
-TEST_F('CrSettingsSiteDetailsV3Test', 'MAYBE_SiteDetails', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsPersonalizationOptionsV3Test =
-    class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/personalization_options_test.js';
-  }
-};
-
-TEST_F('CrSettingsPersonalizationOptionsV3Test', 'AllBuilds', function() {
-  runMochaSuite('PersonalizationOptionsTests_AllBuilds');
-});
-
-GEN('#if BUILDFLAG(GOOGLE_CHROME_BRANDING)');
-TEST_F('CrSettingsPersonalizationOptionsV3Test', 'OfficialBuild', function() {
-  runMochaSuite('PersonalizationOptionsTests_OfficialBuild');
-});
-GEN('#endif');
-
-// eslint-disable-next-line no-var
-var CrSettingsPrivacyPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/privacy_page_test.js';
-  }
-};
-
-TEST_F('CrSettingsPrivacyPageV3Test', 'PrivacyPageTests', function() {
-  runMochaSuite('PrivacyPage');
-});
-
-TEST_F('CrSettingsPrivacyPageV3Test', 'ContentSettingsRedesign', function() {
-  runMochaSuite('ContentSettingsRedesign');
-});
-
-TEST_F(
-    'CrSettingsPrivacyPageV3Test', 'PrivacySandboxSettingsEnabled', function() {
-      runMochaSuite('PrivacySandboxSettingsEnabled');
-    });
-
-// TODO(crbug.com/1043665): flaky crash on Linux Tests (dbg).
-TEST_F(
-    'CrSettingsPrivacyPageV3Test', 'DISABLED_PrivacyPageSoundTests',
-    function() {
-      runMochaSuite('PrivacyPageSound');
-    });
-
-// TODO(crbug.com/1113912): flaky failure on multiple platforms
-TEST_F(
-    'CrSettingsPrivacyPageV3Test', 'DISABLED_HappinessTrackingSurveysTests',
-    function() {
-      runMochaSuite('HappinessTrackingSurveys');
-    });
-
-GEN('#if defined(OS_MAC) || defined(OS_WIN)');
-// TODO(crbug.com/1043665): disabling due to failures on several builders.
-TEST_F(
-    'CrSettingsPrivacyPageV3Test', 'DISABLED_CertificateManagerTests',
-    function() {
-      runMochaSuite('NativeCertificateManager');
-    });
-GEN('#endif');
-
-// eslint-disable-next-line no-var
-var CrSettingsRouteV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/route_tests.js';
-  }
-};
-
-TEST_F('CrSettingsRouteV3Test', 'Basic', function() {
-  runMochaSuite('route');
-});
-
-TEST_F('CrSettingsRouteV3Test', 'DynamicParameters', function() {
-  runMochaSuite('DynamicParameters');
-});
-
-// Copied from Polymer 2 test:
-// Failing on ChromiumOS dbg. https://crbug.com/709442
-GEN('#if (defined(OS_WIN) || BUILDFLAG(IS_CHROMEOS_ASH)) && !defined(NDEBUG)');
-GEN('#define MAYBE_NonExistentRoute DISABLED_NonExistentRoute');
-GEN('#else');
-GEN('#define MAYBE_NonExistentRoute NonExistentRoute');
-GEN('#endif');
-
-TEST_F('CrSettingsRouteV3Test', 'MAYBE_NonExistentRoute', function() {
-  runMochaSuite('NonExistentRoute');
-});
-
-// eslint-disable-next-line no-var
-var CrSettingsAdvancedPageV3Test = class extends CrSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://settings/test_loader.html?module=settings/advanced_page_test.js';
-  }
-};
-
-// Copied from Polymer 2 test:
-// Times out on debug builders because the Settings page can take several
-// seconds to load in a Release build and several times that in a Debug build.
-// See https://crbug.com/558434.
-GEN('#if !defined(NDEBUG)');
-GEN('#define MAYBE_Load DISABLED_Load');
-GEN('#else');
-GEN('#define MAYBE_Load Load');
-GEN('#endif');
-TEST_F('CrSettingsAdvancedPageV3Test', 'MAYBE_Load', function() {
-  mocha.run();
-});
-
-[['AllSites', 'all_sites_tests.js'],
- ['AppearanceFontsPage', 'appearance_fonts_page_test.js'],
- ['AppearancePage', 'appearance_page_test.js'],
- ['BasicPage', 'basic_page_test.js'],
- [
-   'SettingsCategoryDefaultRadioGroup',
-   'settings_category_default_radio_group_tests.js'
- ],
- ['CategoryDefaultSetting', 'category_default_setting_tests.js'],
- ['CategorySettingExceptions', 'category_setting_exceptions_tests.js'],
- ['Checkbox', 'checkbox_tests.js'],
- ['ChooserExceptionList', 'chooser_exception_list_tests.js'],
- ['ChooserExceptionListEntry', 'chooser_exception_list_entry_tests.js'],
- ['CollapseRadioButton', 'collapse_radio_button_tests.js'],
- ['ControlledButton', 'controlled_button_tests.js'],
- ['ControlledRadioButton', 'controlled_radio_button_tests.js'],
- ['CookiesPage', 'cookies_page_test.js'],
- ['DoNotTrackToggle', 'do_not_track_toggle_test.js'],
- ['DownloadsPage', 'downloads_page_test.js'],
- ['DropdownMenu', 'dropdown_menu_tests.js'],
- ['ExtensionControlledIndicator', 'extension_controlled_indicator_tests.js'],
- ['HelpPage', 'help_page_v3_test.js'],
- ['Languages', 'languages_tests.js'],
- ['Menu', 'settings_menu_test.js'],
- ['OnStartupPage', 'on_startup_page_tests.js'],
- ['PaymentsSection', 'payments_section_test.js'],
- ['PeoplePage', 'people_page_test.js'],
- ['PeoplePageSyncControls', 'people_page_sync_controls_test.js'],
- ['PeoplePageSyncPage', 'people_page_sync_page_test.js'],
- ['Prefs', 'prefs_tests.js'],
- ['PrefUtil', 'pref_util_tests.js'],
- ['ProtocolHandlers', 'protocol_handlers_tests.js'],
- ['RecentSitePermissions', 'recent_site_permissions_test.js'],
- // Flaky on all OSes. TODO(crbug.com/1127733): Enable the test.
- ['ResetPage', 'reset_page_test.js', 'DISABLED_All'],
- ['ResetProfileBanner', 'reset_profile_banner_test.js'],
- ['SearchEngines', 'search_engines_page_test.js'],
- ['SearchPage', 'search_page_test.js'],
- ['Search', 'search_settings_test.js'],
- ['SecurityKeysSubpage', 'security_keys_subpage_test.js'],
- ['SecureDns', 'secure_dns_test.js'],
- ['SiteData', 'site_data_test.js'],
- ['SiteDataDetails', 'site_data_details_subpage_tests.js'],
- ['SiteDetailsPermission', 'site_details_permission_tests.js'],
- ['SiteEntry', 'site_entry_tests.js'],
- ['SiteFavicon', 'site_favicon_test.js'],
- ['SiteListEntry', 'site_list_entry_tests.js'],
- ['SiteSettingsPage', 'site_settings_page_test.js'],
- ['Slider', 'settings_slider_tests.js'],
- ['StartupUrlsPage', 'startup_urls_page_test.js'],
- ['Subpage', 'settings_subpage_test.js'],
- ['SyncAccountControl', 'sync_account_control_test.js'],
- ['Textarea', 'settings_textarea_tests.js'],
- ['ToggleButton', 'settings_toggle_button_tests.js'],
- ['ZoomLevels', 'zoom_levels_tests.js'],
-].forEach(test => registerTest(...test));
-
-// Timeout on MacOS dbg bots
-// https://crbug.com/1133412
-GEN('#if !defined(OS_MAC) || defined(NDEBUG)');
-[['SecurityPage', 'security_page_test.js'],
-].forEach(test => registerTest(...test));
-GEN('#endif  // !defined(OS_MAC) || defined(NDEBUG)');
-
-GEN('#if BUILDFLAG(IS_CHROMEOS_ASH)');
-[['LanguagesPageMetricsChromeOS', 'languages_page_metrics_test_cros.js'],
- ['PasswordsSectionCros', 'passwords_section_test_cros.js'],
- ['PeoplePageChromeOS', 'people_page_test_cros.js'],
- // Copied from Polymer 2 test. TODO(crbug.com/929455): flaky, fix.
- ['SiteListChromeOS', 'site_list_tests_cros.js', 'DISABLED_AndroidSmsInfo'],
-].forEach(test => registerTest(...test));
-GEN('#endif  // BUILDFLAG(IS_CHROMEOS_ASH)');
-
-GEN('#if !defined(OS_MAC)');
-[['EditDictionaryPage', 'edit_dictionary_page_test.js'],
-].forEach(test => registerTest(...test));
-GEN('#endif  //!defined(OS_MAC)');
-
-GEN('#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)');
-[['DefaultBrowser', 'default_browser_browsertest.js'],
- ['SystemPage', 'system_page_tests.js'],
-].forEach(test => registerTest(...test));
-GEN('#endif  // !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)');
-
-GEN('#if !BUILDFLAG(IS_CHROMEOS_ASH)');
-[['ImportDataDialog', 'import_data_dialog_test.js'],
- ['PeoplePageManageProfile', 'people_page_manage_profile_test.js'],
-].forEach(test => registerTest(...test));
-GEN('#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)');
-
-GEN('#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)');
-[['ChromeCleanupPage', 'chrome_cleanup_page_test.js'],
- ['IncompatibleApplicationsPage', 'incompatible_applications_page_test.js'],
-].forEach(test => registerTest(...test));
-GEN('#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)');
-
-GEN('#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && !BUILDFLAG(IS_CHROMEOS_ASH)');
-registerTest('MetricsReporting', 'metrics_reporting_tests.js');
-GEN('#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING) ' +
-    '&& !BUILDFLAG(IS_CHROMEOS_ASH)');
-
-function registerTest(testName, module, caseName) {
-  const className = `CrSettings${testName}V3Test`;
-  this[className] = class extends CrSettingsV3BrowserTest {
-    /** @override */
-    get browsePreload() {
-      return `chrome://settings/test_loader.html?module=settings/${module}`;
-    }
-  };
-
-  TEST_F(className, caseName || 'All', () => mocha.run());
-}
diff --git a/chromecast/base/cast_sys_info_android_things.h b/chromecast/base/cast_sys_info_android_things.h
deleted file mode 100644
index 86744766..0000000
--- a/chromecast/base/cast_sys_info_android_things.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMECAST_BASE_CAST_SYS_INFO_ANDROID_THINGS_H_
-#define CHROMECAST_BASE_CAST_SYS_INFO_ANDROID_THINGS_H_
-
-#include <vector>
-
-#include "chromecast/base/cast_sys_info_android.h"
-
-namespace chromecast {
-
-class CastSysInfoAndroidThings : public CastSysInfoAndroid {
- public:
-  CastSysInfoAndroidThings();
-  ~CastSysInfoAndroidThings() override;
-
-  // CastSysInfo implementation:
-  std::string GetProductName() override;
-  std::string GetDeviceModel() override;
-  std::string GetManufacturer() override;
-  std::string GetSystemReleaseChannel() override;
-  std::vector<std::string> GetFactoryLocaleList() override;
-};
-
-}  // namespace chromecast
-
-#endif  // CHROMECAST_BASE_CAST_SYS_INFO_ANDROID_THINGS_H_
diff --git a/chromeos/components/camera_app_ui/resources/js/main.js b/chromeos/components/camera_app_ui/resources/js/main.js
index 67495440..1101497 100644
--- a/chromeos/components/camera_app_ui/resources/js/main.js
+++ b/chromeos/components/camera_app_ui/resources/js/main.js
@@ -242,7 +242,18 @@
       await filesystem.initialize();
       const cameraDir = filesystem.getCameraDirectory();
       assert(cameraDir !== null);
-      this.galleryButton_.initialize(cameraDir);
+
+      // There are three possible cases:
+      // 1. Regular instance
+      //      (intent === null)
+      // 2. STILL_CAPTURE_CAMREA and VIDEO_CAMERA intents
+      //      (intent !== null && shouldHandleResult === false)
+      // 3. Other intents
+      //      (intent !== null && shouldHandleResult === true)
+      // Only (1) and (2) will show gallery button on the UI.
+      if (this.intent_ === null || !this.intent_.shouldHandleResult) {
+        this.galleryButton_.initialize(cameraDir);
+      }
     } catch (error) {
       console.error(error);
       nav.open(ViewName.WARNING, WarningType.FILESYSTEM_FAILURE);
diff --git a/chromeos/components/diagnostics_ui/resources/fake_observables.js b/chromeos/components/diagnostics_ui/resources/fake_observables.js
index 2f094df..237de216 100644
--- a/chromeos/components/diagnostics_ui/resources/fake_observables.js
+++ b/chromeos/components/diagnostics_ui/resources/fake_observables.js
@@ -107,6 +107,12 @@
   constructor() {
     /** @private {!Map<string, !FakeObservableState>} */
     this.observables_ = new Map();
+
+    /**
+     * Set of observables that are capable of taking an additional argument.
+     * @private {!Set<string>}
+     */
+    this.sharedObservables_ = new Set();
   }
 
   /**
@@ -119,6 +125,16 @@
   }
 
   /**
+   * Register an observable that can take a single argument to its observe
+   * method. The argument can identify a more specific entity within a group to
+   * observe.
+   * @param {string} methodName
+   */
+  registerObservableWithArg(methodName) {
+    this.sharedObservables_.add(methodName);
+  }
+
+  /**
    * Supply the callback for observing methodName.
    * @param {string} methodName
    * @param {!function(!T)} callback
@@ -128,6 +144,18 @@
   }
 
   /**
+   * Supply the callback for observing a methodName that belongs to a shared
+   * observer group.
+   * @param {string} methodName
+   * @param {string} arg
+   * @param {!function(!T)} callback
+   */
+  observeWithArg(methodName, arg, callback) {
+    this.getObservable_(this.lookupMethodWithArgName_(methodName, arg))
+        .addObserver(callback);
+  }
+
+  /**
    * Sets the data that will be produced when the observable is triggered.
    * Each observation produces the next value in the array and wraps around
    * when all observations have been produced.
@@ -137,6 +165,26 @@
   setObservableData(methodName, observations) {
     this.getObservable_(methodName).setObservableData(observations);
   }
+
+  /**
+   * Sets the data that will be produced when an observable that takes
+   * arg as a parameter is triggered.
+   * @param {string} methodName
+   * @param {string} arg
+   * @param {!Array<!T>} observations
+   */
+  setObservableDataForArg(methodName, arg, observations) {
+    assert(
+        this.sharedObservables_.has(methodName),
+        `${methodName} not found in sharedObservables_`);
+    const methodNameToRegister = this.createMethodWithArgName_(methodName, arg);
+    const isMethodRegistered = !!this.observables_.get(methodNameToRegister);
+    if (!isMethodRegistered) {
+      this.register(methodNameToRegister);
+    }
+    this.setObservableData(methodNameToRegister, observations);
+  }
+
   /**
    * Start firing the observer on a fixed interval. setObservableData() must
    * already have been called.
@@ -174,6 +222,17 @@
   }
 
   /**
+   * Causes a shared observable to trigger and notify all observers observing
+   * |arg| of the next observation value.
+   * @param {string} methodName
+   * @param {string} arg
+   */
+  triggerWithArg(methodName, arg) {
+    this.getObservable_(this.lookupMethodWithArgName_(methodName, arg))
+        .trigger();
+  }
+
+  /**
    * Return the Observable for methodName.
    * @param {string} methodName
    * @return {!FakeObservableState}
@@ -184,4 +243,33 @@
     assert(!!observable, `Observable '${methodName}' not found.`);
     return observable;
   }
+
+  /**
+   * Returns a concatenated form of methodName and arg separated by
+   * an underscore.
+   * @param {string} methodName
+   * @param {string} arg
+   * @return {string}
+   * @private
+   */
+  createMethodWithArgName_(methodName, arg) {
+    return `${methodName}_${arg}`;
+  }
+
+  /**
+   * Returns the methodName that was registered for a shared observable.
+   * You must register methodName and set data specifically for arg before
+   * calling this method.
+   * @param {string} methodName
+   * @param {string} arg
+   * @return {string}
+   * @private
+   */
+  lookupMethodWithArgName_(methodName, arg) {
+    const observableName = this.createMethodWithArgName_(methodName, arg);
+    assert(
+        !!this.observables_.get(observableName),
+        `Observable '${observableName}' not found.`);
+    return observableName;
+  }
 }
diff --git a/chromeos/services/assistant/public/cpp/assistant_enums.h b/chromeos/services/assistant/public/cpp/assistant_enums.h
index 6be7d8c3..f134507 100644
--- a/chromeos/services/assistant/public/cpp/assistant_enums.h
+++ b/chromeos/services/assistant/public/cpp/assistant_enums.h
@@ -71,7 +71,8 @@
   kStylus = 7,
   kLauncherSearchResult = 8,
   kLauncherSearchBoxIcon = 9,
-  kProactiveSuggestions = 10,
+  // Deprecated, please do not reuse
+  // kProactiveSuggestions = 10,
   kLauncherChip = 11,
   // Deprecated, please do not reuse
   // kBloom = 12,
diff --git a/chromeos/services/libassistant/conversation_controller.cc b/chromeos/services/libassistant/conversation_controller.cc
index d976d22..f5df4de 100644
--- a/chromeos/services/libassistant/conversation_controller.cc
+++ b/chromeos/services/libassistant/conversation_controller.cc
@@ -33,6 +33,7 @@
 
 // A macro which ensures we are running on the main thread.
 #define ENSURE_MOJOM_THREAD(method, ...)                                    \
+  DVLOG(3) << __func__;                                                     \
   if (!mojom_task_runner_->RunsTasksInCurrentSequence()) {                  \
     mojom_task_runner_->PostTask(                                           \
         FROM_HERE,                                                          \
@@ -273,6 +274,8 @@
 void ConversationController::SendTextQuery(const std::string& query,
                                            AssistantQuerySource source,
                                            bool allow_tts) {
+  DVLOG(1) << __func__;
+
   DCHECK(requests_are_allowed_)
       << "Should not receive requests before Libassistant is running";
   if (!assistant_manager_internal_)
@@ -300,6 +303,8 @@
 }
 
 void ConversationController::StartVoiceInteraction() {
+  DVLOG(1) << __func__;
+
   DCHECK(requests_are_allowed_)
       << "Should not receive requests before Libassistant is running";
   if (!assistant_manager_) {
@@ -564,6 +569,8 @@
 }
 
 void ConversationController::MaybeStopPreviousInteraction() {
+  DVLOG(1) << __func__;
+
   if (!stop_interaction_closure_ || stop_interaction_closure_->IsCancelled()) {
     return;
   }
diff --git a/chromeos/services/libassistant/public/cpp/assistant_interaction_metadata.h b/chromeos/services/libassistant/public/cpp/assistant_interaction_metadata.h
index 4224a850..23719b3 100644
--- a/chromeos/services/libassistant/public/cpp/assistant_interaction_metadata.h
+++ b/chromeos/services/libassistant/public/cpp/assistant_interaction_metadata.h
@@ -23,7 +23,7 @@
   kStylus = 3,
   kSuggestionChip = 4,
   kVoiceInput = 5,
-  kProactiveSuggestions = 6,
+  // kProactiveSuggestionsDeprecated = 6,
   kLibAssistantInitiated = 7,
   // kWarmerWelcomeDeprecated = 8,
   kConversationStarter = 9,
diff --git a/chromeos/services/libassistant/public/mojom/conversation_observer.mojom b/chromeos/services/libassistant/public/mojom/conversation_observer.mojom
index 09c4ec1..2b34684 100644
--- a/chromeos/services/libassistant/public/mojom/conversation_observer.mojom
+++ b/chromeos/services/libassistant/public/mojom/conversation_observer.mojom
@@ -100,7 +100,6 @@
   kStylus,
   kSuggestionChip,
   kVoiceInput,
-  kProactiveSuggestions,
   kLibAssistantInitiated,
   kConversationStarter,
   kWhatsOnMyScreen,
diff --git a/chromeos/services/libassistant/public/mojom/mojom_traits.cc b/chromeos/services/libassistant/public/mojom/mojom_traits.cc
index 8f81340a..25a28ed 100644
--- a/chromeos/services/libassistant/public/mojom/mojom_traits.cc
+++ b/chromeos/services/libassistant/public/mojom/mojom_traits.cc
@@ -438,8 +438,6 @@
       return MojomType::kSuggestionChip;
     case NativeType::kVoiceInput:
       return MojomType::kVoiceInput;
-    case NativeType::kProactiveSuggestions:
-      return MojomType::kProactiveSuggestions;
     case NativeType::kLibAssistantInitiated:
       return MojomType::kLibAssistantInitiated;
     case NativeType::kConversationStarter:
@@ -477,9 +475,6 @@
     case MojomType::kVoiceInput:
       *output = NativeType::kVoiceInput;
       return true;
-    case MojomType::kProactiveSuggestions:
-      *output = NativeType::kProactiveSuggestions;
-      return true;
     case MojomType::kLibAssistantInitiated:
       *output = NativeType::kLibAssistantInitiated;
       return true;
diff --git a/chromeos/services/secure_channel/ble_characteristics_finder.cc b/chromeos/services/secure_channel/ble_characteristics_finder.cc
index f81ba43f..5ec4edb 100644
--- a/chromeos/services/secure_channel/ble_characteristics_finder.cc
+++ b/chromeos/services/secure_channel/ble_characteristics_finder.cc
@@ -165,19 +165,15 @@
 
 void BluetoothLowEnergyCharacteristicsFinder::TryToVerifyEid(
     device::BluetoothRemoteGattCharacteristic* eid_char) {
-  eid_char->ReadRemoteCharacteristic(
-      base::BindOnce(
-          &BluetoothLowEnergyCharacteristicsFinder::OnRemoteCharacteristicRead,
-          weak_ptr_factory_.GetWeakPtr(),
-          eid_char->GetService()->GetIdentifier()),
-      base::BindOnce(&BluetoothLowEnergyCharacteristicsFinder::
-                         OnReadRemoteCharacteristicError,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     eid_char->GetService()->GetIdentifier()));
+  eid_char->ReadRemoteCharacteristic(base::BindOnce(
+      &BluetoothLowEnergyCharacteristicsFinder::OnRemoteCharacteristicRead,
+      weak_ptr_factory_.GetWeakPtr(), eid_char->GetService()->GetIdentifier()));
 }
 
 void BluetoothLowEnergyCharacteristicsFinder::OnRemoteCharacteristicRead(
     const std::string& service_id,
+    base::Optional<device::BluetoothRemoteGattService::GattErrorCode>
+        error_code,
     const std::vector<uint8_t>& value) {
   auto it = service_ids_pending_eid_read_.find(service_id);
   if (it == service_ids_pending_eid_read_.end()) {
@@ -187,6 +183,15 @@
 
   service_ids_pending_eid_read_.erase(it);
 
+  if (error_code.has_value()) {
+    PA_LOG(ERROR) << "OnWriteRemoteCharacteristicError() Error code: "
+                  << error_code.value();
+    service_ids_pending_eid_read_.erase(service_id);
+    if (!has_callback_been_invoked_)
+      NotifyFailureIfNoPendingEidCharReads();
+    return;
+  }
+
   if (has_callback_been_invoked_) {
     PA_LOG(VERBOSE) << "Characteristic read after callback was invoked.";
     return;
@@ -216,23 +221,6 @@
                 rx_chars.front()->GetIdentifier());
 }
 
-void BluetoothLowEnergyCharacteristicsFinder::OnReadRemoteCharacteristicError(
-    const std::string& service_id,
-    device::BluetoothRemoteGattService::GattErrorCode error) {
-  auto it = service_ids_pending_eid_read_.find(service_id);
-  if (it == service_ids_pending_eid_read_.end()) {
-    PA_LOG(WARNING) << "No request entry for " << service_id;
-    return;
-  }
-
-  service_ids_pending_eid_read_.erase(it);
-
-  PA_LOG(ERROR) << "OnWriteRemoteCharacteristicError() Error code: " << error;
-  service_ids_pending_eid_read_.erase(service_id);
-  if (!has_callback_been_invoked_)
-    NotifyFailureIfNoPendingEidCharReads();
-}
-
 bool BluetoothLowEnergyCharacteristicsFinder::DoesEidMatchExpectedDevice(
     const std::vector<uint8_t>& eid_value_read) {
   // Convert the char data from a std::vector<uint8_t> to a std::string.
diff --git a/chromeos/services/secure_channel/ble_characteristics_finder.h b/chromeos/services/secure_channel/ble_characteristics_finder.h
index 521d319..d5d7773 100644
--- a/chromeos/services/secure_channel/ble_characteristics_finder.h
+++ b/chromeos/services/secure_channel/ble_characteristics_finder.h
@@ -96,11 +96,11 @@
   void NotifyFailureIfNoPendingEidCharReads();
 
   void TryToVerifyEid(device::BluetoothRemoteGattCharacteristic* eid_char);
-  void OnRemoteCharacteristicRead(const std::string& service_id,
-                                  const std::vector<uint8_t>& value);
-  void OnReadRemoteCharacteristicError(
+  void OnRemoteCharacteristicRead(
       const std::string& service_id,
-      device::BluetoothRemoteGattService::GattErrorCode error);
+      base::Optional<device::BluetoothRemoteGattService::GattErrorCode>
+          error_code,
+      const std::vector<uint8_t>& value);
   bool DoesEidMatchExpectedDevice(const std::vector<uint8_t>& eid_value_read);
 
   // The Bluetooth adapter where the connection was established.
diff --git a/chromeos/services/secure_channel/ble_characteristics_finder_unittest.cc b/chromeos/services/secure_channel/ble_characteristics_finder_unittest.cc
index 4dc6947..8c40004 100644
--- a/chromeos/services/secure_channel/ble_characteristics_finder_unittest.cc
+++ b/chromeos/services/secure_channel/ble_characteristics_finder_unittest.cc
@@ -172,21 +172,23 @@
     // running only on one thread. Calls to
     // |task_environment_.RunUntilIdle()| in tests will process any
     // pending callbacks.
-    ON_CALL(*characteristic.get(), ReadRemoteCharacteristic_(_, _))
+    ON_CALL(*characteristic.get(), ReadRemoteCharacteristic_(_))
         .WillByDefault(Invoke(
             [read_success, correct_eid](
-                BluetoothRemoteGattCharacteristic::ValueCallback& callback,
-                BluetoothRemoteGattCharacteristic::ErrorCallback&
-                    error_callback) {
+                BluetoothRemoteGattCharacteristic::ValueCallback& callback) {
+              base::Optional<BluetoothRemoteGattService::GattErrorCode>
+                  error_code;
+              std::vector<uint8_t> value;
+              if (read_success) {
+                error_code = base::nullopt;
+                value =
+                    correct_eid ? GetCorrectEidValue() : GetIncorrectEidValue();
+              } else {
+                error_code = device::BluetoothGattService::GATT_ERROR_FAILED;
+              }
               base::ThreadTaskRunnerHandle::Get()->PostTask(
                   FROM_HERE,
-                  read_success
-                      ? base::BindOnce(std::move(callback),
-                                       correct_eid ? GetCorrectEidValue()
-                                                   : GetIncorrectEidValue())
-                      : base::BindOnce(
-                            std::move(error_callback),
-                            BluetoothGattService::GATT_ERROR_FAILED));
+                  base::BindOnce(std::move(callback), error_code, value));
             }));
     return characteristic;
   }
diff --git a/components/autofill/core/common/autofill_util.cc b/components/autofill/core/common/autofill_util.cc
index b741847..9e48205 100644
--- a/components/autofill/core/common/autofill_util.cc
+++ b/components/autofill/core/common/autofill_util.cc
@@ -117,14 +117,6 @@
   return std::u16string::npos;
 }
 
-bool IsDesktopPlatform() {
-#if defined(OS_ANDROID) || defined(OS_IOS)
-  return false;
-#else
-  return true;
-#endif
-}
-
 bool IsCheckable(const FormFieldData::CheckStatus& check_status) {
   return check_status != FormFieldData::CheckStatus::kNotCheckable;
 }
diff --git a/components/autofill/core/common/autofill_util.h b/components/autofill/core/common/autofill_util.h
index ade30dd2..909a18e 100644
--- a/components/autofill/core/common/autofill_util.h
+++ b/components/autofill/core/common/autofill_util.h
@@ -62,10 +62,6 @@
                              const std::u16string& field_contents,
                              bool case_sensitive);
 
-// Returns true if running on a desktop platform. Any platform that is not
-// Android or iOS is considered desktop.
-bool IsDesktopPlatform();
-
 bool IsCheckable(const FormFieldData::CheckStatus& check_status);
 bool IsChecked(const FormFieldData::CheckStatus& check_status);
 void SetCheckStatus(FormFieldData* form_field_data,
diff --git a/components/browser_sync/profile_sync_components_factory_impl.cc b/components/browser_sync/profile_sync_components_factory_impl.cc
index f93914e..e55b188 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -430,8 +430,7 @@
                           base::Unretained(sync_client_)));
 }
 
-void ProfileSyncComponentsFactoryImpl::
-    ClearAllTransportDataExceptEncryptionBootstrapToken() {
+void ProfileSyncComponentsFactoryImpl::ClearAllTransportData() {
   syncer::SyncTransportDataPrefs sync_transport_data_prefs(
       sync_client_->GetPrefService());
 
@@ -448,7 +447,7 @@
             sync_client_->GetModelTypeStoreService()->GetSyncDataPath()));
   }
 
-  sync_transport_data_prefs.ClearAllExceptEncryptionBootstrapToken();
+  sync_transport_data_prefs.ClearAll();
   sync_client_->OnLocalSyncTransportDataCleared();
 }
 
diff --git a/components/browser_sync/profile_sync_components_factory_impl.h b/components/browser_sync/profile_sync_components_factory_impl.h
index e3f28deb..0379c5d6 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.h
+++ b/components/browser_sync/profile_sync_components_factory_impl.h
@@ -82,7 +82,7 @@
       const std::string& name,
       invalidation::InvalidationService* invalidator,
       syncer::SyncInvalidationsService* sync_invalidation_service) override;
-  void ClearAllTransportDataExceptEncryptionBootstrapToken() override;
+  void ClearAllTransportData() override;
 
  private:
   // Factory function for ModelTypeController instances for models living on
diff --git a/components/browser_ui/styles/android/BUILD.gn b/components/browser_ui/styles/android/BUILD.gn
index 9cf5a956..8b4d436 100644
--- a/components/browser_ui/styles/android/BUILD.gn
+++ b/components/browser_ui/styles/android/BUILD.gn
@@ -166,6 +166,7 @@
     "java/res/values-night/values.xml",
     "java/res/values-sw600dp-v27/styles.xml",
     "java/res/values-v27/styles.xml",
+    "java/res/values-v31/styles.xml",
     "java/res/values/colors.xml",
     "java/res/values/dimens.xml",
     "java/res/values/drawables.xml",
diff --git a/components/browser_ui/styles/android/java/res/values-v31/styles.xml b/components/browser_ui/styles/android/java/res/values-v31/styles.xml
new file mode 100644
index 0000000..ba8d47d
--- /dev/null
+++ b/components/browser_ui/styles/android/java/res/values-v31/styles.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<resources>
+    <style name="Theme.BrowserUI" parent="Base.Theme.BrowserUI" >
+        <!-- TODO(https://crbug.com/1201349): Disable overscroll on S due to
+            consistency issues between browser and web platform. -->
+        <item name="android:overScrollMode">never</item>
+    </style>
+</resources>
diff --git a/components/browser_ui/styles/android/java/res/values/styles.xml b/components/browser_ui/styles/android/java/res/values/styles.xml
index 74da5676..1f2c50ab 100644
--- a/components/browser_ui/styles/android/java/res/values/styles.xml
+++ b/components/browser_ui/styles/android/java/res/values/styles.xml
@@ -5,7 +5,7 @@
 
 <resources xmlns:tools="http://schemas.android.com/tools">
     <!-- Theme shared between Chrome and embedders. -->
-    <style name="Theme.BrowserUI" parent="Theme.AppCompat.DayNight.NoActionBar">
+    <style name="Base.Theme.BrowserUI" parent="Theme.AppCompat.DayNight.NoActionBar">
         <!-- Text colors -->
         <item name="android:textColorLink">@color/default_text_color_link</item>
         <item name="android:textColorHighlight">@color/text_highlight_color</item>
@@ -46,6 +46,7 @@
         <!-- Shape -->
         <item name="menuCornerSize">@dimen/popup_bg_corner_radius</item>
     </style>
+    <style name="Theme.BrowserUI" parent="Base.Theme.BrowserUI" />
 
     <!-- Popup and long-press context popup menu style -->
     <style name="PopupMenuStyle" parent="Widget.AppCompat.Light.PopupMenu">
diff --git a/components/live_caption/views/caption_bubble.cc b/components/live_caption/views/caption_bubble.cc
index 39e365f8..bbe98e9 100644
--- a/components/live_caption/views/caption_bubble.cc
+++ b/components/live_caption/views/caption_bubble.cc
@@ -228,6 +228,7 @@
     auto& ax_lines = GetViewAccessibility().virtual_children();
     if (text.empty() && !ax_lines.empty()) {
       GetViewAccessibility().RemoveAllVirtualChildViews();
+      NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged, true);
       return;
     }
 
@@ -248,9 +249,8 @@
     size_t num_ax_lines = ax_lines.size();
     for (size_t i = num_lines; i < num_ax_lines; ++i) {
       GetViewAccessibility().RemoveVirtualChildView(ax_lines.back().get());
+      NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged, true);
     }
-
-    NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged, true);
   }
 
  private:
@@ -265,6 +265,7 @@
       auto ax_line = std::make_unique<views::AXVirtualView>();
       ax_line->GetCustomData().role = ax::mojom::Role::kStaticText;
       GetViewAccessibility().AddVirtualChildView(std::move(ax_line));
+      NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged, true);
     }
 
     // Set the virtual child's name as line text.
@@ -274,6 +275,8 @@
       ax_node_data.SetName(line_text);
       std::vector<gfx::Rect> bounds = GetSubstringBounds(text_range);
       ax_node_data.relative_bounds.bounds = gfx::RectF(bounds[0]);
+      ax_lines[line_index]->NotifyAccessibilityEvent(
+          ax::mojom::Event::kTextChanged);
     }
   }
 };
diff --git a/components/metrics/metrics_service.cc b/components/metrics/metrics_service.cc
index 0f51f80..6492fd2 100644
--- a/components/metrics/metrics_service.cc
+++ b/components/metrics/metrics_service.cc
@@ -917,7 +917,6 @@
   // Redundant setting to assure that we always reset this value at shutdown
   // (and that we don't use some alternate path, and not call LogCleanShutdown).
   clean_shutdown_status_ = CLEANLY_SHUTDOWN;
-  client_->OnLogCleanShutdown();
   state_manager_->clean_exit_beacon()->WriteBeaconValue(true);
   StabilityMetricsProvider(local_state_).MarkSessionEndCompleted(end_completed);
 }
diff --git a/components/metrics/metrics_service_client.h b/components/metrics/metrics_service_client.h
index 9a925993..7f0d3db 100644
--- a/components/metrics/metrics_service_client.h
+++ b/components/metrics/metrics_service_client.h
@@ -78,9 +78,6 @@
   // ownership.
   virtual void OnEnvironmentUpdate(std::string* serialized_environment) {}
 
-  // Called by the metrics service to record a clean shutdown.
-  virtual void OnLogCleanShutdown() {}
-
   // Called prior to a metrics log being closed, allowing the client to collect
   // extra histograms that will go in that log. Asynchronous API - the client
   // implementation should call |done_callback| when complete.
diff --git a/components/offline_pages/core/offline_page_feature.cc b/components/offline_pages/core/offline_page_feature.cc
index 38a3858..f60cabb 100644
--- a/components/offline_pages/core/offline_page_feature.cc
+++ b/components/offline_pages/core/offline_page_feature.cc
@@ -53,9 +53,6 @@
 const base::Feature kOfflineIndicatorFeature{"OfflineIndicator",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kOfflineIndicatorAlwaysHttpProbeFeature{
-    "OfflineIndicatorAlwaysHttpProbe", base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kOfflinePagesNetworkStateLikelyUnknown{
     "OfflinePagesNetworkStateLikelyUnknown", base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -121,10 +118,6 @@
   return base::FeatureList::IsEnabled(kOfflineIndicatorFeature);
 }
 
-bool IsOfflineIndicatorAlwaysHttpProbeEnabled() {
-  return base::FeatureList::IsEnabled(kOfflineIndicatorAlwaysHttpProbeFeature);
-}
-
 bool IsOnTheFlyMhtmlHashComputationEnabled() {
   return false;
 }
diff --git a/components/offline_pages/core/offline_page_feature.h b/components/offline_pages/core/offline_page_feature.h
index 3941c44..23b170c 100644
--- a/components/offline_pages/core/offline_page_feature.h
+++ b/components/offline_pages/core/offline_page_feature.h
@@ -22,7 +22,6 @@
 extern const base::Feature kOfflinePagesCTSuppressNotificationsFeature;
 extern const base::Feature kOfflinePagesShowAlternateDinoPageFeature;
 extern const base::Feature kOfflineIndicatorFeature;
-extern const base::Feature kOfflineIndicatorAlwaysHttpProbeFeature;
 extern const base::Feature kOnTheFlyMhtmlHashComputationFeature;
 extern const base::Feature kOfflinePagesNetworkStateLikelyUnknown;
 
@@ -77,12 +76,6 @@
 // Returns true if offline indicator UI is shown when the user is offline.
 bool IsOfflineIndicatorFeatureEnabled();
 
-// Returns true if we should always do http probes to detect network
-// connectivity instead of retrieving it from the system. This enables the user
-// to test our http probe detection on Android devices with Marshmallow and
-// above.
-bool IsOfflineIndicatorAlwaysHttpProbeEnabled();
-
 // Returns true if we are saving MHTML files to the target location and
 // calculating their content digests in one step.
 bool IsOnTheFlyMhtmlHashComputationEnabled();
diff --git a/components/omnibox/browser/autocomplete_match.h b/components/omnibox/browser/autocomplete_match.h
index 038b400..a19c755 100644
--- a/components/omnibox/browser/autocomplete_match.h
+++ b/components/omnibox/browser/autocomplete_match.h
@@ -482,6 +482,9 @@
   // mucking with the matches stored in the model, lest other omnibox systems
   // get confused about which is which.  See the code that sets
   // |swap_contents_and_description| for conditions they are swapped.
+  //
+  // TODO(crbug.com/1202964): Clean up the handling of contents and description
+  // so that this copy is no longer required.
   AutocompleteMatch GetMatchWithContentsAndDescriptionPossiblySwapped() const;
 
   // Determines whether this match is allowed to be the default match by
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc
index 42429b4..9075e68 100644
--- a/components/omnibox/browser/autocomplete_result.cc
+++ b/components/omnibox/browser/autocomplete_result.cc
@@ -187,35 +187,16 @@
 
 void AutocompleteResult::AppendMatches(const AutocompleteInput& input,
                                        const ACMatches& matches) {
-  for (const auto& i : matches) {
-    DCHECK_EQ(AutocompleteMatch::SanitizeString(i.contents), i.contents);
-    DCHECK_EQ(AutocompleteMatch::SanitizeString(i.description),
-              i.description);
-    matches_.push_back(i);
-    if (!AutocompleteMatch::IsSearchType(i.type) &&
-        i.type != ACMatchType::DOCUMENT_SUGGESTION) {
-      const OmniboxFieldTrial::EmphasizeTitlesCondition condition(
-          OmniboxFieldTrial::GetEmphasizeTitlesConditionForInput(input));
-      bool emphasize = false;
-      switch (condition) {
-        case OmniboxFieldTrial::EMPHASIZE_WHEN_NONEMPTY:
-          emphasize = !i.description.empty();
-          break;
-        case OmniboxFieldTrial::EMPHASIZE_WHEN_TITLE_MATCHES:
-          emphasize = !i.description.empty() &&
-                      AutocompleteMatch::HasMatchStyle(i.description_class);
-          break;
-        case OmniboxFieldTrial::EMPHASIZE_WHEN_ONLY_TITLE_MATCHES:
-          emphasize = !i.description.empty() &&
-                      AutocompleteMatch::HasMatchStyle(i.description_class) &&
-                      !AutocompleteMatch::HasMatchStyle(i.contents_class);
-          break;
-        case OmniboxFieldTrial::EMPHASIZE_NEVER:
-          break;
-        default:
-          NOTREACHED();
-      }
-      matches_.back().swap_contents_and_description = emphasize;
+  for (const auto& match : matches) {
+    DCHECK_EQ(AutocompleteMatch::SanitizeString(match.contents),
+              match.contents);
+    DCHECK_EQ(AutocompleteMatch::SanitizeString(match.description),
+              match.description);
+    matches_.push_back(match);
+    if (!match.description.empty() &&
+        !AutocompleteMatch::IsSearchType(match.type) &&
+        match.type != ACMatchType::DOCUMENT_SUGGESTION) {
+      matches_.back().swap_contents_and_description = true;
     }
   }
 }
diff --git a/components/omnibox/browser/autocomplete_result.h b/components/omnibox/browser/autocomplete_result.h
index f7287b73..f540b4b1 100644
--- a/components/omnibox/browser/autocomplete_result.h
+++ b/components/omnibox/browser/autocomplete_result.h
@@ -87,7 +87,7 @@
                           TemplateURLService* template_url_service);
 
   // Adds a new set of matches to the result set.  Does not re-sort.  Calls
-  // PossiblySwapContentsAndDescriptionForURLSuggestion(input)" on all added
+  // PossiblySwapContentsAndDescriptionForURLSuggestion(input) on all added
   // matches; see comments there for more information.
   void AppendMatches(const AutocompleteInput& input,
                      const ACMatches& matches);
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index 40a336b9..df6b630 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -40,11 +40,13 @@
 #include "components/omnibox/browser/omnibox_pedal_concepts.h"
 #include "components/omnibox/browser/omnibox_popup_model.h"
 #include "components/omnibox/browser/omnibox_popup_view.h"
+#include "components/omnibox/browser/omnibox_prefs.h"
 #include "components/omnibox/browser/omnibox_view.h"
 #include "components/omnibox/browser/search_provider.h"
 #include "components/omnibox/browser/suggestion_answer.h"
 #include "components/omnibox/browser/verbatim_match.h"
 #include "components/omnibox/common/omnibox_features.h"
+#include "components/prefs/pref_service.h"
 #include "components/search_engines/omnibox_focus_type.h"
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_prepopulate_data.h"
@@ -1513,10 +1515,9 @@
   // If entering keyword mode by space is disabled, do not set
   // |allow_exact_keyword_match_|.
   allow_exact_keyword_match_ =
-      (OmniboxFieldTrial::GetKeywordSpaceTrigger() !=
-       OmniboxFieldTrial::SPACE_TRIGGERING_DISABLED) &&
-      state_changes.text_differs && allow_keyword_ui_change &&
-      !state_changes.just_deleted_text && no_selection &&
+      AllowKeywordSpaceTriggering() && state_changes.text_differs &&
+      allow_keyword_ui_change && !state_changes.just_deleted_text &&
+      no_selection &&
       CreatedKeywordSearchByInsertingSpaceInMiddle(
           *state_changes.old_text, user_text_, state_changes.new_sel_start);
   view_->UpdatePopup();
@@ -1698,10 +1699,19 @@
   return controller()->GetLocationBarModel()->ShouldPreventElision();
 }
 
+bool OmniboxEditModel::AllowKeywordSpaceTriggering() const {
+  PrefService* pref_service =
+      autocomplete_controller()->autocomplete_provider_client()->GetPrefs();
+  return (OmniboxFieldTrial::GetKeywordSpaceTrigger() !=
+          OmniboxFieldTrial::SPACE_TRIGGERING_DISABLED) &&
+         (!base::FeatureList::IsEnabled(
+              omnibox::kKeywordSpaceTriggeringSetting) ||
+          pref_service->GetBoolean(omnibox::kKeywordSpaceTriggeringEnabled));
+}
+
 bool OmniboxEditModel::MaybeAcceptKeywordBySpace(
     const std::u16string& new_text) {
-  if (OmniboxFieldTrial::GetKeywordSpaceTrigger() ==
-      OmniboxFieldTrial::SPACE_TRIGGERING_DISABLED)
+  if (!AllowKeywordSpaceTriggering())
     return false;
 
   size_t keyword_length = new_text.length() - 1;
diff --git a/components/omnibox/browser/omnibox_edit_model.h b/components/omnibox/browser/omnibox_edit_model.h
index ba84a7a0..547e94ab 100644
--- a/components/omnibox/browser/omnibox_edit_model.h
+++ b/components/omnibox/browser/omnibox_edit_model.h
@@ -474,6 +474,12 @@
   void GetInfoForCurrentText(AutocompleteMatch* match,
                              GURL* alternate_nav_url) const;
 
+  // Checks whether keyword mode space-triggering has been disabled either by
+  // a pref or relevant feature flags. If the setting isn't available on the
+  // search engines page, the pref should be ignored.  Returns true if space
+  // triggering is enabled, false otherwise.
+  bool AllowKeywordSpaceTriggering() const;
+
   // Accepts current keyword if the user just typed a space at the end of
   // |new_text|.  This handles both of the following cases:
   //   (assume "foo" is a keyword, | is the input caret, [] is selected text)
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index e24487d7..5e4d0fc 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -598,39 +598,6 @@
   return value;
 }
 
-OmniboxFieldTrial::EmphasizeTitlesCondition
-OmniboxFieldTrial::GetEmphasizeTitlesConditionForInput(
-    const AutocompleteInput& input) {
-  if (base::FeatureList::IsEnabled(omnibox::kUIExperimentSwapTitleAndUrl)) {
-    return EMPHASIZE_WHEN_NONEMPTY;
-  }
-
-  // Touch-optimized UI always swaps title and URL.
-  if (ui::TouchUiController::Get()->touch_ui())
-    return EMPHASIZE_WHEN_NONEMPTY;
-
-  // Look up the parameter named kEmphasizeTitlesRule + "_" + input.type(),
-  // find its value, and return that value as an enum.  If the parameter
-  // isn't redefined, fall back to the generic rule kEmphasizeTitlesRule + "_*"
-  std::string value_str(variations::GetVariationParamValue(
-      kBundledExperimentFieldTrialName,
-      std::string(kEmphasizeTitlesRule) + "_" +
-          base::NumberToString(static_cast<int>(input.type()))));
-  if (value_str.empty()) {
-    value_str = variations::GetVariationParamValue(
-        kBundledExperimentFieldTrialName,
-        std::string(kEmphasizeTitlesRule) + "_*");
-  }
-  if (value_str.empty())
-    return EMPHASIZE_NEVER;
-  // This is a best-effort conversion; we trust the hand-crafted parameters
-  // downloaded from the server to be perfect.  There's no need for handle
-  // errors smartly.
-  int value;
-  base::StringToInt(value_str, &value);
-  return static_cast<EmphasizeTitlesCondition>(value);
-}
-
 bool OmniboxFieldTrial::IsShortBookmarkSuggestionsEnabled() {
   return base::FeatureList::IsEnabled(omnibox::kShortBookmarkSuggestions);
 }
@@ -942,7 +909,6 @@
     "KeywordRequiresRegistry";
 const char OmniboxFieldTrial::kKeywordScoreForSufficientlyCompleteMatchRule[] =
     "KeywordScoreForSufficientlyCompleteMatch";
-const char OmniboxFieldTrial::kEmphasizeTitlesRule[] = "EmphasizeTitles";
 
 const char OmniboxFieldTrial::kHUPNewScoringTypedCountRelevanceCapParam[] =
     "TypedCountRelevanceCap";
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index d3a11a2..449aace 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -121,15 +121,6 @@
 // end of the vector are assumed to have scores of 1.0.
 typedef std::vector<std::pair<size_t, double>> NumMatchesScores;
 
-// Do not change these values as they need to be in sync with values
-// specified in experiment configs on the variations server.
-enum EmphasizeTitlesCondition {
-  EMPHASIZE_WHEN_NONEMPTY = 0,
-  EMPHASIZE_WHEN_TITLE_MATCHES = 1,
-  EMPHASIZE_WHEN_ONLY_TITLE_MATCHES = 2,
-  EMPHASIZE_NEVER = 3
-};
-
 // ---------------------------------------------------------
 // For any experiment that's part of the bundled omnibox field trial.
 
@@ -360,17 +351,6 @@
 int KeywordScoreForSufficientlyCompleteMatch();
 
 // ---------------------------------------------------------
-// For the EmphasizeTitles experiment that's part of the bundled omnibox
-// field trial.
-
-// Returns the conditions under which the UI code should display the title
-// of a URL more prominently than the URL for input |input|. Normally the URL
-// is displayed more prominently. Returns NEVER_EMPHASIZE if the experiment
-// isn't active.
-EmphasizeTitlesCondition GetEmphasizeTitlesConditionForInput(
-    const AutocompleteInput& input);
-
-// ---------------------------------------------------------
 // For UI experiments.
 
 // Short bookmarks.
diff --git a/components/omnibox/browser/omnibox_prefs.cc b/components/omnibox/browser/omnibox_prefs.cc
index 4e3a866..3bb4ad2 100644
--- a/components/omnibox/browser/omnibox_prefs.cc
+++ b/components/omnibox/browser/omnibox_prefs.cc
@@ -30,6 +30,10 @@
 // Values are defined in omnibox::IntranetRedirectorBehavior.
 const char kIntranetRedirectBehavior[] = "browser.intranet_redirect_behavior";
 
+// Boolean that controls whether scoped search mode can be triggered by <space>.
+const char kKeywordSpaceTriggeringEnabled[] =
+    "omnibox.keyword_space_triggering_enabled";
+
 // A dictionary of visibility preferences for suggestion groups. The key is the
 // suggestion group ID serialized as a string, and the value is
 // SuggestionGroupVisibility serialized as an integer.
@@ -43,6 +47,7 @@
 
 void RegisterProfilePrefs(PrefRegistrySimple* registry) {
   registry->RegisterDictionaryPref(kSuggestionGroupVisibility);
+  registry->RegisterBooleanPref(kKeywordSpaceTriggeringEnabled, true);
 }
 
 SuggestionGroupVisibility GetUserPreferenceForSuggestionGroupVisibility(
diff --git a/components/omnibox/browser/omnibox_prefs.h b/components/omnibox/browser/omnibox_prefs.h
index 1652f16d..379337b 100644
--- a/components/omnibox/browser/omnibox_prefs.h
+++ b/components/omnibox/browser/omnibox_prefs.h
@@ -33,6 +33,7 @@
 // Keep alphabetized, and document each in the .cc file.
 extern const char kDocumentSuggestEnabled[];
 extern const char kIntranetRedirectBehavior[];
+extern const char kKeywordSpaceTriggeringEnabled[];
 extern const char kSuggestionGroupVisibility[];
 extern const char kPreventUrlElisionsInOmnibox[];
 extern const char kZeroSuggestCachedResults[];
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index 5f56ef02..86e1fa8 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -78,10 +78,6 @@
 const base::Feature kDisplayTitleForCurrentUrl{
     "OmniboxDisplayTitleForCurrentUrl", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Feature used to always swap the title and URL.
-const base::Feature kUIExperimentSwapTitleAndUrl{
-    "OmniboxUIExperimentSwapTitleAndUrl", enabled_by_default_desktop_only};
-
 // Feature used to fetch document suggestions.
 const base::Feature kDocumentProvider{"OmniboxDocumentProvider",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
@@ -308,6 +304,11 @@
 const base::Feature kKeywordSpaceTriggering{"OmniboxKeywordSpaceTriggering",
                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
+// When enabled, a setting is added to chrome://settings/searchEngines to
+// control whether <space> can be used to trigger keyword mode.
+const base::Feature kKeywordSpaceTriggeringSetting{
+    "OmniboxKeywordSpaceTriggeringSetting", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Feature used to reveal the path, query and ref from steady state URLs
 // on hover.
 const base::Feature kRevealSteadyStateUrlPathQueryAndRefOnHover{
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index cb72bd9..a12e3ab 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -17,7 +17,6 @@
 extern const base::Feature kImageSearchSuggestionThumbnail;
 extern const base::Feature kSearchProviderWarmUpOnFocus;
 extern const base::Feature kDisplayTitleForCurrentUrl;
-extern const base::Feature kUIExperimentSwapTitleAndUrl;
 extern const base::Feature kDocumentProvider;
 extern const base::Feature kOmniboxRemoveSuggestionsFromClipboard;
 extern const base::Feature kDebounceDocumentProvider;
@@ -82,6 +81,7 @@
 extern const base::Feature kIntranetRedirectBehaviorPolicyRollout;
 extern const base::Feature kOmniboxAssistantVoiceSearch;
 extern const base::Feature kKeywordSpaceTriggering;
+extern const base::Feature kKeywordSpaceTriggeringSetting;
 
 // Path-hiding experiments - these hide the path and other URL components in
 // some circumstances in the steady-state omnibox.
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index 21c3d25..2a53a640 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -239,8 +239,6 @@
     "ui/post_save_compromised_helper.h",
     "ui/saved_passwords_presenter.cc",
     "ui/saved_passwords_presenter.h",
-    "ui/weak_check_utility.cc",
-    "ui/weak_check_utility.h",
     "votes_uploader.cc",
     "votes_uploader.h",
     "well_known_change_password_state.cc",
@@ -300,7 +298,6 @@
     "//third_party/abseil-cpp:absl",
     "//third_party/protobuf:protobuf_lite",
     "//third_party/re2",
-    "//third_party/zxcvbn-cpp",
     "//ui/base",
     "//ui/gfx",
     "//ui/gfx/range",
@@ -322,6 +319,14 @@
     ]
   }
 
+  if (!is_android && !is_ios) {
+    sources += [
+      "ui/weak_check_utility.cc",
+      "ui/weak_check_utility.h",
+    ]
+    deps += [ "//third_party/zxcvbn-cpp" ]
+  }
+
   if ((is_posix && !is_apple) || is_fuchsia) {
     sources += [ "login_database_posix.cc" ]
   }
@@ -669,7 +674,6 @@
     "ui/insecure_credentials_reader_unittest.cc",
     "ui/post_save_compromised_helper_unittest.cc",
     "ui/saved_passwords_presenter_unittest.cc",
-    "ui/weak_check_utility_unittest.cc",
     "vote_uploads_test_matchers.h",
     "votes_uploader_unittest.cc",
     "well_known_change_password_state_unittest.cc",
@@ -691,6 +695,10 @@
     sources += [ "hash_password_manager_unittest.cc" ]
   }
 
+  if (!is_android && !is_ios) {
+    sources += [ "ui/weak_check_utility_unittest.cc" ]
+  }
+
   if (!is_chromeos_ash && !is_android) {
     sources += [ "password_store_signin_notifier_impl_unittest.cc" ]
   }
diff --git a/components/password_manager/core/browser/ui/insecure_credentials_manager.cc b/components/password_manager/core/browser/ui/insecure_credentials_manager.cc
index 3e5c56e6..8a115007 100644
--- a/components/password_manager/core/browser/ui/insecure_credentials_manager.cc
+++ b/components/password_manager/core/browser/ui/insecure_credentials_manager.cc
@@ -24,9 +24,12 @@
 #include "components/password_manager/core/browser/password_list_sorter.h"
 #include "components/password_manager/core/browser/ui/credential_utils.h"
 #include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
-#include "components/password_manager/core/browser/ui/weak_check_utility.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
+#include "components/password_manager/core/browser/ui/weak_check_utility.h"
+#endif
+
 namespace password_manager {
 
 // Extra information about InsecureCredential which is required by UI.
@@ -164,6 +167,8 @@
   return credentials;
 }
 
+// The function is only used by the weak check.
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
 base::flat_set<std::u16string> ExtractPasswords(
     SavedPasswordsPresenter::SavedPasswordsView password_forms) {
   std::vector<std::u16string> passwords;
@@ -173,6 +178,7 @@
   }
   return base::flat_set<std::u16string>(std::move(passwords));
 }
+#endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
 
 }  // namespace
 
@@ -239,6 +245,7 @@
   insecure_credentials_reader_.Init();
 }
 
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
 void InsecureCredentialsManager::StartWeakCheck(
     base::OnceClosure on_check_done) {
   base::ThreadPool::PostTaskAndReplyWithResult(
@@ -249,6 +256,7 @@
                      weak_ptr_factory_.GetWeakPtr(), base::ElapsedTimer())
           .Then(std::move(on_check_done)));
 }
+#endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
 
 void InsecureCredentialsManager::SaveInsecureCredential(
     const LeakCheckCredential& credential) {
diff --git a/components/password_manager/core/browser/ui/insecure_credentials_manager.h b/components/password_manager/core/browser/ui/insecure_credentials_manager.h
index 0708260..31682b3f1 100644
--- a/components/password_manager/core/browser/ui/insecure_credentials_manager.h
+++ b/components/password_manager/core/browser/ui/insecure_credentials_manager.h
@@ -18,6 +18,7 @@
 #include "base/scoped_observation.h"
 #include "base/timer/elapsed_timer.h"
 #include "base/types/strong_alias.h"
+#include "build/build_config.h"
 #include "components/password_manager/core/browser/insecure_credentials_table.h"
 #include "components/password_manager/core/browser/leak_detection/bulk_leak_check.h"
 #include "components/password_manager/core/browser/password_store.h"
@@ -163,9 +164,11 @@
 
   void Init();
 
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
   // Computes weak credentials in a separate thread and then passes the result
   // to OnWeakCheckDone.
   void StartWeakCheck(base::OnceClosure on_check_done = base::DoNothing());
+#endif
 
   // Marks all saved credentials which have same username & password as
   // insecure.
diff --git a/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc b/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc
index 1584818..ffbe611 100644
--- a/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc
+++ b/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc
@@ -33,18 +33,19 @@
 constexpr char kPassword2[] = "s3cr3t";
 constexpr char kPassword3[] = "484her";
 
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
 constexpr char kWeakPassword1[] = "123456";
 constexpr char kWeakPassword2[] = "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcda";
 constexpr char kStrongPassword1[] = "fnlsr4@cm^mdls@fkspnsg3d";
 constexpr char kStrongPassword2[] = "pmsFlsnoab4nsl#losb@skpfnsbkjb^klsnbs!cns";
+// Delay in milliseconds.
+constexpr int kDelay = 2;
+#endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
 
 using ::testing::ElementsAre;
 using ::testing::ElementsAreArray;
 using ::testing::IsEmpty;
 
-// Delay in milliseconds.
-const int kDelay = 2;
-
 struct MockInsecureCredentialsManagerObserver
     : InsecureCredentialsManager::Observer {
   MOCK_METHOD(void,
@@ -97,6 +98,7 @@
   return credential_with_password;
 }
 
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
 CredentialWithPassword MakeWeakCredential(const PasswordForm& form) {
   CredentialWithPassword weak_credential{CredentialView(form)};
   weak_credential.insecure_type = InsecureCredentialTypeFlags::kWeakCredential;
@@ -112,6 +114,7 @@
       InsecureCredentialTypeFlags::kWeakCredential;
   return credential_with_password;
 }
+#endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
 
 class InsecureCredentialsManagerTest : public ::testing::Test {
  protected:
@@ -482,6 +485,7 @@
               ElementsAreArray(store().stored_passwords().at(kExampleOrg)));
 }
 
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
 TEST_F(InsecureCredentialsManagerTest, StartWeakCheckNotifiesOnCompletion) {
   base::MockOnceClosure closure;
   provider().StartWeakCheck(closure.Get());
@@ -724,6 +728,7 @@
   histogram_tester().ExpectUniqueSample(
       "PasswordManager.WeakCheck.PasswordScore", 0, 1);
 }
+#endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
 
 // Test verifies that saving LeakCheckCredential via provider adds expected
 // compromised credential.
@@ -773,6 +778,7 @@
   EXPECT_THAT(provider().GetInsecureCredentials(), ElementsAre(expected));
 }
 
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
 // Test verifies that editing weak credential via provider has affect on weak
 // credentials and updates password in the store.
 TEST_F(InsecureCredentialsManagerTest, UpdateWeakPassword) {
@@ -794,7 +800,6 @@
             kStrongPassword1);
 }
 
-#if !defined(OS_ANDROID) && !defined(OS_IOS)
 // Test verifies that editing a weak credential to another weak credential
 // continues to be treated weak.
 TEST_F(InsecureCredentialsManagerTest, UpdatedWeakPasswordRemainsWeak) {
@@ -816,7 +821,6 @@
   expected.password = base::ASCIIToUTF16(kWeakPassword2);
   EXPECT_THAT(provider().GetWeakCredentials(), ElementsAre(expected));
 }
-#endif
 
 // Test verifies that editing credential that is weak and compromised via
 // provider change the saved password.
@@ -843,6 +847,7 @@
   EXPECT_EQ(GetSavedPasswordForUsername(kExampleCom, kUsername1),
             kStrongPassword1);
 }
+#endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
 
 TEST_F(InsecureCredentialsManagerTest, RemoveCompromisedCredential) {
   InsecureCredential credential =
@@ -865,6 +870,7 @@
   EXPECT_THAT(provider().GetInsecureCredentials(), IsEmpty());
 }
 
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
 TEST_F(InsecureCredentialsManagerTest, RemoveWeakCredential) {
   PasswordForm password =
       MakeSavedPassword(kExampleCom, kUsername1, kWeakPassword1);
@@ -925,6 +931,7 @@
                           MakeWeakCredential(password_forms[2]),
                           MakeWeakCredential(password_forms[3])));
 }
+#endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
 
 namespace {
 class InsecureCredentialsManagerWithTwoStoresTest : public ::testing::Test {
@@ -1072,6 +1079,7 @@
   EXPECT_TRUE(account_store().stored_passwords().at(kExampleCom).empty());
 }
 
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
 TEST_F(InsecureCredentialsManagerWithTwoStoresTest, RemoveWeakCredential) {
   // Add `kUsername1`,`kPassword1` to both stores.
   profile_store().AddLogin(
@@ -1092,5 +1100,6 @@
   EXPECT_THAT(profile_store().stored_passwords().at(kExampleCom), IsEmpty());
   EXPECT_THAT(account_store().stored_passwords().at(kExampleCom), IsEmpty());
 }
+#endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
 
 }  // namespace password_manager
diff --git a/components/password_manager/ios/shared_password_controller.mm b/components/password_manager/ios/shared_password_controller.mm
index 1d038b7..dd6d95c 100644
--- a/components/password_manager/ios/shared_password_controller.mm
+++ b/components/password_manager/ios/shared_password_controller.mm
@@ -307,7 +307,9 @@
                                     suggestionsAvailable);
                        }];
 
-  if ([formQuery.type isEqual:@"input"] && self.isPasswordGenerated &&
+  if (self.isPasswordGenerated &&
+      ([formQuery.type isEqual:@"input"] ||
+       [formQuery.type isEqual:@"keyup"]) &&
       formQuery.uniqueFieldID == self.passwordGeneratedIdentifier) {
     // On other platforms, when the user clicks on generation field, we show
     // password in clear text. And the user has the possibility to edit it. On
diff --git a/components/policy/core/common/policy_loader_lacros.cc b/components/policy/core/common/policy_loader_lacros.cc
index e763ecfa..eecbc634 100644
--- a/components/policy/core/common/policy_loader_lacros.cc
+++ b/components/policy/core/common/policy_loader_lacros.cc
@@ -89,9 +89,10 @@
 
   PolicyMap policy_map;
   base::WeakPtr<CloudExternalDataManager> external_data_manager;
-  DecodeProtoFields(*(validator.payload()), external_data_manager,
-                    PolicySource::POLICY_SOURCE_CLOUD,
-                    PolicyScope::POLICY_SCOPE_USER, &policy_map);
+  DecodeProtoFieldsPerProfile(*(validator.payload()), external_data_manager,
+                              PolicySource::POLICY_SOURCE_CLOUD,
+                              PolicyScope::POLICY_SCOPE_USER, &policy_map,
+                              PolicyPerProfileFilter::kFalse);
   bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
       .MergeFrom(policy_map);
   return bundle;
diff --git a/components/policy/core/common/policy_loader_lacros.h b/components/policy/core/common/policy_loader_lacros.h
index 3ce80f4..fdc30c6 100644
--- a/components/policy/core/common/policy_loader_lacros.h
+++ b/components/policy/core/common/policy_loader_lacros.h
@@ -19,7 +19,8 @@
 namespace policy {
 
 // A policy loader for Lacros. The data is taken from Ash and the validatity of
-// data is trusted, since they have been validated by Ash.
+// data is trusted, since they have been validated by Ash. This class loads only
+// the user policy that has to apply browser-wide (non per_profile).
 class POLICY_EXPORT PolicyLoaderLacros
     : public AsyncPolicyLoader,
       public chromeos::LacrosChromeServiceImpl::Observer {
diff --git a/components/policy/core/common/policy_loader_lacros_unittest.cc b/components/policy/core/common/policy_loader_lacros_unittest.cc
index a75f7ef..26a636b 100644
--- a/components/policy/core/common/policy_loader_lacros_unittest.cc
+++ b/components/policy/core/common/policy_loader_lacros_unittest.cc
@@ -28,10 +28,9 @@
 
 namespace {
 
-std::vector<uint8_t> GetValidPolicyFetchResponse() {
-  em::CloudPolicySettings policy_proto;
+std::vector<uint8_t> GetValidPolicyFetchResponse(
+    const em::CloudPolicySettings& policy_proto) {
   em::PolicyData policy_data;
-  policy_proto.mutable_homepagelocation()->set_value("http://chromium.org");
   policy_proto.SerializeToString(policy_data.mutable_policy_value());
   policy_data.set_policy_type(dm_protocol::kChromeUserPolicyType);
   em::PolicyFetchResponse policy_response;
@@ -43,6 +42,21 @@
   return data;
 }
 
+std::vector<uint8_t> GetValidPolicyFetchResponseWithPerProfilePolicy() {
+  em::CloudPolicySettings policy_proto;
+  // homepage_location is a per_profile:True policy. See policy_tempates.json
+  // for details.
+  policy_proto.mutable_homepagelocation()->set_value("http://chromium.org");
+  return GetValidPolicyFetchResponse(policy_proto);
+}
+
+std::vector<uint8_t> GetValidPolicyFetchResponseWithSystemWidePolicy() {
+  em::CloudPolicySettings policy_proto;
+  // A policy that is per_profile:False. See policy_tempates.json for details.
+  policy_proto.mutable_taskmanagerendprocessenabled()->set_value(false);
+  return GetValidPolicyFetchResponse(policy_proto);
+}
+
 const PolicyMap* GetChromePolicyMap(PolicyBundle* bundle) {
   PolicyNamespace ns = PolicyNamespace(POLICY_DOMAIN_CHROME, std::string());
   return &(bundle->Get(ns));
@@ -60,7 +74,7 @@
 };
 
 TEST_F(PolicyLoaderLacrosTest, BasicTest) {
-  std::vector<uint8_t> data = GetValidPolicyFetchResponse();
+  std::vector<uint8_t> data = GetValidPolicyFetchResponseWithSystemWidePolicy();
 
   chromeos::ScopedLacrosServiceTestHelper test_helper;
   auto init_params = crosapi::mojom::BrowserInitParams::New();
@@ -69,7 +83,22 @@
 
   PolicyLoaderLacros loader(task_environment_.GetMainThreadTaskRunner());
   base::RunLoop().RunUntilIdle();
-  EXPECT_GT(GetChromePolicyMap(loader.Load().get())->size(), (unsigned int)0);
+  EXPECT_GT(GetChromePolicyMap(loader.Load().get())->size(),
+            static_cast<unsigned int>(0));
+}
+
+TEST_F(PolicyLoaderLacrosTest, BasicTestPerProfile) {
+  std::vector<uint8_t> data = GetValidPolicyFetchResponseWithPerProfilePolicy();
+
+  chromeos::ScopedLacrosServiceTestHelper test_helper;
+  auto init_params = crosapi::mojom::BrowserInitParams::New();
+  init_params->device_account_policy = data;
+  chromeos::LacrosService::Get()->SetInitParamsForTests(std::move(init_params));
+
+  PolicyLoaderLacros loader(task_environment_.GetMainThreadTaskRunner());
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(GetChromePolicyMap(loader.Load().get())->size(),
+            static_cast<unsigned int>(0));
 }
 
 TEST_F(PolicyLoaderLacrosTest, UpdateTest) {
@@ -86,10 +115,11 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetChromePolicyMap(loader->Load().get())->size(), (unsigned int)0);
 
-  std::vector<uint8_t> data = GetValidPolicyFetchResponse();
+  std::vector<uint8_t> data = GetValidPolicyFetchResponseWithSystemWidePolicy();
   loader->NotifyPolicyUpdate(data);
   base::RunLoop().RunUntilIdle();
-  EXPECT_GT(GetChromePolicyMap(loader->Load().get())->size(), (unsigned int)0);
+  EXPECT_GT(GetChromePolicyMap(loader->Load().get())->size(),
+            static_cast<unsigned int>(0));
   provider.Shutdown();
 }
 
diff --git a/components/policy/core/common/policy_proto_decoders.cc b/components/policy/core/common/policy_proto_decoders.cc
index dda0194..27cac6d 100644
--- a/components/policy/core/common/policy_proto_decoders.cc
+++ b/components/policy/core/common/policy_proto_decoders.cc
@@ -105,6 +105,18 @@
   return std::move(value_with_error.value.value());
 }
 
+bool PerProfileMatches(bool policy_per_profile,
+                       PolicyPerProfileFilter per_profile_enum) {
+  switch (per_profile_enum) {
+    case PolicyPerProfileFilter::kTrue:
+      return policy_per_profile;
+    case PolicyPerProfileFilter::kFalse:
+      return !policy_per_profile;
+    case PolicyPerProfileFilter::kAny:
+      return true;
+  }
+}
+
 }  // namespace
 
 void DecodeProtoFields(
@@ -113,12 +125,24 @@
     PolicySource source,
     PolicyScope scope,
     PolicyMap* map) {
+  DecodeProtoFieldsPerProfile(policy, external_data_manager, source, scope, map,
+                              PolicyPerProfileFilter::kAny);
+}
+
+void DecodeProtoFieldsPerProfile(
+    const em::CloudPolicySettings& policy,
+    base::WeakPtr<CloudExternalDataManager> external_data_manager,
+    PolicySource source,
+    PolicyScope scope,
+    PolicyMap* map,
+    PolicyPerProfileFilter per_profile) {
   PolicyLevel level;
 
   // Access arrays are terminated by a struct that contains only nullptrs.
   for (const BooleanPolicyAccess* access = &kBooleanPolicyAccess[0];
        access->policy_key; access++) {
-    if (!(policy.*access->has_proto)())
+    if (!PerProfileMatches(access->per_profile, per_profile) ||
+        !(policy.*access->has_proto)())
       continue;
 
     const em::BooleanPolicyProto& proto = (policy.*access->get_proto)();
@@ -131,7 +155,8 @@
 
   for (const IntegerPolicyAccess* access = &kIntegerPolicyAccess[0];
        access->policy_key; access++) {
-    if (!(policy.*access->has_proto)())
+    if (!PerProfileMatches(access->per_profile, per_profile) ||
+        !(policy.*access->has_proto)())
       continue;
 
     const em::IntegerPolicyProto& proto = (policy.*access->get_proto)();
@@ -149,7 +174,8 @@
 
   for (const StringPolicyAccess* access = &kStringPolicyAccess[0];
        access->policy_key; access++) {
-    if (!(policy.*access->has_proto)())
+    if (!PerProfileMatches(access->per_profile, per_profile) ||
+        !(policy.*access->has_proto)())
       continue;
 
     const em::StringPolicyProto& proto = (policy.*access->get_proto)();
@@ -177,7 +203,8 @@
 
   for (const StringListPolicyAccess* access = &kStringListPolicyAccess[0];
        access->policy_key; access++) {
-    if (!(policy.*access->has_proto)())
+    if (!PerProfileMatches(access->per_profile, per_profile) ||
+        !(policy.*access->has_proto)())
       continue;
 
     const em::StringListPolicyProto& proto = (policy.*access->get_proto)();
diff --git a/components/policy/core/common/policy_proto_decoders.h b/components/policy/core/common/policy_proto_decoders.h
index c0c4be10..7e2a04b 100644
--- a/components/policy/core/common/policy_proto_decoders.h
+++ b/components/policy/core/common/policy_proto_decoders.h
@@ -18,9 +18,18 @@
 class CloudExternalDataManager;
 class PolicyMap;
 
+enum class PolicyPerProfileFilter {
+  // Applies to the browser profile.
+  kTrue,
+  // Applies to all browser instances.
+  kFalse,
+  // Any user policy.
+  kAny
+};
+
 // Decode all of the fields in |policy| which are recognized (see the metadata
 // in policy_constants.cc) and store them in the given |map|, with the given
-// |source| and |scope|.
+// |source| and |scope|. Deprecated: Use DecodeProtoFieldsPerProfile instead.
 POLICY_EXPORT void DecodeProtoFields(
     const enterprise_management::CloudPolicySettings& policy,
     base::WeakPtr<CloudExternalDataManager> external_data_manager,
@@ -28,6 +37,19 @@
     PolicyScope scope,
     PolicyMap* map);
 
+// Decode all the fields in |policy| that match the needed |per_profile| flag
+// which are recognized (see the metadata in policy_constants.cc) and store them
+// in the given |map|, with the given |source| and |scope|. In case
+// |per_profile| is nullopt, the flag is ignored and all the policies are
+// included.
+POLICY_EXPORT void DecodeProtoFieldsPerProfile(
+    const enterprise_management::CloudPolicySettings& policy,
+    base::WeakPtr<CloudExternalDataManager> external_data_manager,
+    PolicySource source,
+    PolicyScope scope,
+    PolicyMap* map,
+    PolicyPerProfileFilter per_profile);
+
 }  // namespace policy
 
 #endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_PROTO_DECODERS_H_
diff --git a/components/policy/resources/webui/policy.html b/components/policy/resources/webui/policy.html
index 8159c176..f3cd08c 100644
--- a/components/policy/resources/webui/policy.html
+++ b/components/policy/resources/webui/policy.html
@@ -48,10 +48,6 @@
         <div class="version"></div>
       </div>
       <div class="status-entry" hidden>
-        <div class="label">$i18n{labelEnterpriseEnrollmentDomain}</div>
-        <div class="enterprise-enrollment-domain"></div>
-      </div>
-      <div class="status-entry" hidden>
         <div class="label">$i18n{labelMachineEnrollmentMachineName}</div>
         <div class="machine-enrollment-name"></div>
       </div>
diff --git a/components/policy/resources/webui/policy_base.js b/components/policy/resources/webui/policy_base.js
index b2446276..af0c8bc 100644
--- a/components/policy/resources/webui/policy_base.js
+++ b/components/policy/resources/webui/policy_base.js
@@ -123,8 +123,6 @@
       // status item with the domain the device is enrolled into.
       this.querySelector('.legend').textContent =
           loadTimeData.getString('statusDevice');
-      this.setLabelAndShow_(
-          '.enterprise-enrollment-domain', status.enterpriseEnrollmentDomain);
 
       // Populate the device naming information.
       // Populate the asset identifier.
@@ -163,7 +161,7 @@
         this.setLabelAndShow_('.version', status.version);
       }
       if (status.domain) {
-        this.setLabelAndShow_('.enterprise-enrollment-domain', status.domain);
+        this.setLabelAndShow_('.machine-enrollment-domain', status.domain);
       }
     } else {
       // For user policy, set the appropriate title and populate the topmost
diff --git a/components/policy/tools/generate_policy_source.py b/components/policy/tools/generate_policy_source.py
index e2c0ebd..b824193c 100755
--- a/components/policy/tools/generate_policy_source.py
+++ b/components/policy/tools/generate_policy_source.py
@@ -92,6 +92,7 @@
     self.is_deprecated = policy.get('deprecated', False)
     self.is_device_only = policy.get('device_only', False)
     self.is_future = policy.get('future', False)
+    self.per_profile = features.get('per_profile', False)
     self.supported_chrome_os_management = policy.get(
         'supported_chrome_os_management', ['active_directory', 'google_cloud'])
     self.schema = policy['schema']
@@ -553,6 +554,7 @@
           % protobuf_type.lower())
   f.write('struct %sPolicyAccess {\n' % protobuf_type)
   f.write('  const char* policy_key;\n'
+          '  bool per_profile;\n'
           '  bool (enterprise_management::CloudPolicySettings::'
           '*has_proto)() const;\n'
           '  const enterprise_management::%sPolicyProto&\n'
@@ -1261,11 +1263,13 @@
       if protobuf_type == 'String':
         extra_args = ',\n   ' + _GetStringPolicyType(policy.policy_type)
       f.write('  {key::k%s,\n'
+              '   %s,\n'
               '   &em::CloudPolicySettings::has_%s,\n'
               '   &em::CloudPolicySettings::%s%s},\n' %
-              (name, name.lower(), name.lower(), extra_args))
+              (name, str(policy.per_profile).lower(), name.lower(),
+               name.lower(), extra_args))
   # The list is nullptr-terminated.
-  f.write('  {nullptr, nullptr, nullptr},\n' '};\n\n')
+  f.write('  {nullptr, false, nullptr, nullptr},\n' '};\n\n')
 
 
 #------------------ policy risk tag header -------------------------#
@@ -1540,6 +1544,7 @@
           '%s user\n// policies.\n' % protobuf_type.lower())
   f.write('struct %sPolicyAccess {\n'
           '  const char* policy_key;\n'
+          '  bool per_profile;\n'
           '  enterprise_management::%sPolicyProto*\n'
           '      (enterprise_management::CloudPolicySettings::'
           '*mutable_proto_ptr)();\n'
diff --git a/components/policy_strings.grdp b/components/policy_strings.grdp
index b4bde37..41fff5f 100644
--- a/components/policy_strings.grdp
+++ b/components/policy_strings.grdp
@@ -320,9 +320,6 @@
     Google Update
   </message>
  </if>
-  <message name="IDS_POLICY_LABEL_ENTERPRISE_ENROLLMENT_DOMAIN" desc="Label for the enrollment domain in the device policy status box.">
-    Enrollment domain:
-  </message>
   <message name="IDS_POLICY_LABEL_MACHINE_ENROLLMENT_DOMAIN" desc="Label for the enrollment domain in the machine policy status box.">
     Enrollment domain:
   </message>
diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
index ca3d6a6..7556af60 100644
--- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
+++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
@@ -354,6 +354,11 @@
     return;
   if (![self _isConsideredOpenForPersistentState])
     return;
+  // By the time state is restored, we don't want to miniaturize. Windows will
+  // be properly restored as miniaturized despite us not saving this.
+  // See https://crbug.com/1204517 for context/details.
+  if ([self isMiniaturized])
+    return;
   base::scoped_nsobject<NSMutableData> restorableStateData(
       [[NSMutableData alloc] init]);
   base::scoped_nsobject<NSKeyedArchiver> encoder([[NSKeyedArchiver alloc]
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index acbdcc7..8207fd98 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -150,7 +150,7 @@
     "driver/sync_service_utils_unittest.cc",
     "driver/sync_session_durations_metrics_recorder_unittest.cc",
     "driver/sync_stopped_reporter_unittest.cc",
-    "driver/sync_user_settings_unittest.cc",
+    "driver/sync_user_settings_impl_unittest.cc",
     "engine/backoff_delay_provider_unittest.cc",
     "engine/bookmark_update_preprocessing_unittest.cc",
     "engine/cancelation_signal_unittest.cc",
diff --git a/components/sync/base/sync_prefs.cc b/components/sync/base/sync_prefs.cc
index 8ccd04f..eccaf67 100644
--- a/components/sync/base/sync_prefs.cc
+++ b/components/sync/base/sync_prefs.cc
@@ -134,7 +134,7 @@
   sync_pref_observers_.RemoveObserver(sync_pref_observer);
 }
 
-void SyncTransportDataPrefs::ClearAllExceptEncryptionBootstrapToken() {
+void SyncTransportDataPrefs::ClearAll() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   pref_service_->ClearPref(prefs::kSyncLastSyncedTime);
@@ -329,18 +329,17 @@
   return pref_service_->GetBoolean(prefs::kSyncManaged);
 }
 
-std::string SyncTransportDataPrefs::GetEncryptionBootstrapToken() const {
+std::string SyncPrefs::GetEncryptionBootstrapToken() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return pref_service_->GetString(prefs::kSyncEncryptionBootstrapToken);
 }
 
-void SyncTransportDataPrefs::SetEncryptionBootstrapToken(
-    const std::string& token) {
+void SyncPrefs::SetEncryptionBootstrapToken(const std::string& token) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   pref_service_->SetString(prefs::kSyncEncryptionBootstrapToken, token);
 }
 
-void SyncTransportDataPrefs::ClearEncryptionBootstrapToken() {
+void SyncPrefs::ClearEncryptionBootstrapToken() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   pref_service_->ClearPref(prefs::kSyncEncryptionBootstrapToken);
 }
diff --git a/components/sync/base/sync_prefs.h b/components/sync/base/sync_prefs.h
index c3e31355..86fdec0 100644
--- a/components/sync/base/sync_prefs.h
+++ b/components/sync/base/sync_prefs.h
@@ -56,9 +56,8 @@
   SyncTransportDataPrefs& operator=(const SyncTransportDataPrefs&) = delete;
   ~SyncTransportDataPrefs();
 
-  // Clears all preferences in this class, which excludes the encryption
-  // bootstrap token (non-keystore counterpart).
-  void ClearAllExceptEncryptionBootstrapToken();
+  // Clears all preferences in this class.
+  void ClearAll();
 
   void SetGaiaId(const std::string& gaia_id);
   std::string GetGaiaId() const;
@@ -78,15 +77,6 @@
   base::TimeDelta GetPollInterval() const;
   void SetPollInterval(base::TimeDelta interval);
 
-  // The encryption bootstrap token is used for explicit passphrase users
-  // (usually custom passphrase) and represents a user-entered passphrase.
-  // Hence, it gets treated as user-controlled similarly to sync datatype
-  // selection settings (i.e. doesn't get cleared in
-  // ClearAllExceptEncryptionBootstrapToken()).
-  std::string GetEncryptionBootstrapToken() const;
-  void SetEncryptionBootstrapToken(const std::string& token);
-  void ClearEncryptionBootstrapToken();
-
   // Use this keystore bootstrap token if we're not using an explicit
   // passphrase.
   std::string GetKeystoreEncryptionBootstrapToken() const;
@@ -189,6 +179,12 @@
   // Gets the local sync backend enabled state.
   bool IsLocalSyncEnabled() const;
 
+  // The encryption bootstrap token is used for explicit passphrase users
+  // (usually custom passphrase) and represents a user-entered passphrase.
+  std::string GetEncryptionBootstrapToken() const;
+  void SetEncryptionBootstrapToken(const std::string& token);
+  void ClearEncryptionBootstrapToken();
+
   // Muting mechanism for passphrase prompts, used on Android.
   int GetPassphrasePromptMutedProductVersion() const;
   void SetPassphrasePromptMutedProductVersion(int major_version);
diff --git a/components/sync/base/sync_prefs_unittest.cc b/components/sync/base/sync_prefs_unittest.cc
index 17e2f8b..ec2b2de 100644
--- a/components/sync/base/sync_prefs_unittest.cc
+++ b/components/sync/base/sync_prefs_unittest.cc
@@ -88,10 +88,12 @@
   EXPECT_EQ(now, sync_prefs_->GetLastSyncedTime());
 }
 
-TEST_F(SyncTransportDataPrefsTest, EncryptionBootstrapToken) {
+TEST_F(SyncPrefsTest, EncryptionBootstrapToken) {
   EXPECT_TRUE(sync_prefs_->GetEncryptionBootstrapToken().empty());
   sync_prefs_->SetEncryptionBootstrapToken("token");
   EXPECT_EQ("token", sync_prefs_->GetEncryptionBootstrapToken());
+  sync_prefs_->ClearEncryptionBootstrapToken();
+  EXPECT_TRUE(sync_prefs_->GetEncryptionBootstrapToken().empty());
 }
 
 class MockSyncPrefObserver : public SyncPrefObserver {
@@ -149,23 +151,18 @@
 }
 #endif
 
-TEST_F(SyncTransportDataPrefsTest, ClearAllExceptEncryptionBootstrapToken) {
+TEST_F(SyncTransportDataPrefsTest, ClearAll) {
   sync_prefs_->SetLastSyncedTime(base::Time::Now());
-  sync_prefs_->SetEncryptionBootstrapToken("explicit_passphrase_token");
   sync_prefs_->SetKeystoreEncryptionBootstrapToken("keystore_token");
 
   ASSERT_NE(base::Time(), sync_prefs_->GetLastSyncedTime());
-  ASSERT_EQ("explicit_passphrase_token",
-            sync_prefs_->GetEncryptionBootstrapToken());
   ASSERT_EQ("keystore_token",
             sync_prefs_->GetKeystoreEncryptionBootstrapToken());
 
-  sync_prefs_->ClearAllExceptEncryptionBootstrapToken();
+  sync_prefs_->ClearAll();
 
   EXPECT_EQ(base::Time(), sync_prefs_->GetLastSyncedTime());
   EXPECT_TRUE(sync_prefs_->GetKeystoreEncryptionBootstrapToken().empty());
-  EXPECT_EQ("explicit_passphrase_token",
-            sync_prefs_->GetEncryptionBootstrapToken());
 }
 
 TEST_F(SyncPrefsTest, Basic) {
diff --git a/components/sync/driver/fake_sync_api_component_factory.cc b/components/sync/driver/fake_sync_api_component_factory.cc
index f91a03c..8ba561f 100644
--- a/components/sync/driver/fake_sync_api_component_factory.cc
+++ b/components/sync/driver/fake_sync_api_component_factory.cc
@@ -59,8 +59,7 @@
   return engine;
 }
 
-void FakeSyncApiComponentFactory::
-    ClearAllTransportDataExceptEncryptionBootstrapToken() {
+void FakeSyncApiComponentFactory::ClearAllTransportData() {
   ++clear_transport_data_call_count_;
 }
 
diff --git a/components/sync/driver/fake_sync_api_component_factory.h b/components/sync/driver/fake_sync_api_component_factory.h
index d5b9d39..5645b9c 100644
--- a/components/sync/driver/fake_sync_api_component_factory.h
+++ b/components/sync/driver/fake_sync_api_component_factory.h
@@ -35,8 +35,8 @@
   FakeSyncEngine* last_created_engine() { return last_created_engine_.get(); }
 
   // Returns the number of times transport data was cleared, which includes
-  // ClearAllTransportDataExceptEncryptionBootstrapToken() being invoked as
-  // well as SyncEngine::Shutdown() being invoked with DISABLE_SYNC.
+  // ClearAllTransportData() being invoked as well as SyncEngine::Shutdown()
+  // being invoked with DISABLE_SYNC.
   int clear_transport_data_call_count() const {
     return clear_transport_data_call_count_;
   }
@@ -59,7 +59,7 @@
       const std::string& name,
       invalidation::InvalidationService* invalidator,
       syncer::SyncInvalidationsService* sync_invalidations_service) override;
-  void ClearAllTransportDataExceptEncryptionBootstrapToken() override;
+  void ClearAllTransportData() override;
 
  private:
   base::WeakPtr<DataTypeManagerImpl> last_created_data_type_manager_;
diff --git a/components/sync/driver/glue/sync_engine_backend.cc b/components/sync/driver/glue/sync_engine_backend.cc
index 1468a2a..b14dd071 100644
--- a/components/sync/driver/glue/sync_engine_backend.cc
+++ b/components/sync/driver/glue/sync_engine_backend.cc
@@ -216,7 +216,7 @@
       std::make_unique<NigoriStorageImpl>(
           sync_data_folder_.Append(kNigoriStorageFilename), &encryptor_),
       &encryptor_, base::BindRepeating(&Nigori::GenerateScryptSalt),
-      restored_local_transport_data.encryption_bootstrap_token,
+      params.encryption_bootstrap_token,
       restored_local_transport_data.keystore_encryption_bootstrap_token);
 
   sync_manager_ = params.sync_manager_factory->CreateSyncManager(name_);
diff --git a/components/sync/driver/glue/sync_engine_backend.h b/components/sync/driver/glue/sync_engine_backend.h
index 6b44ae6..0eee8fa 100644
--- a/components/sync/driver/glue/sync_engine_backend.h
+++ b/components/sync/driver/glue/sync_engine_backend.h
@@ -49,7 +49,6 @@
     RestoredLocalTransportData(const RestoredLocalTransportData&) = delete;
     ~RestoredLocalTransportData();
 
-    std::string encryption_bootstrap_token;
     std::string keystore_encryption_bootstrap_token;
     std::map<ModelType, int64_t> invalidation_versions;
 
diff --git a/components/sync/driver/glue/sync_engine_impl.cc b/components/sync/driver/glue/sync_engine_impl.cc
index 637d2c9..0e05058 100644
--- a/components/sync/driver/glue/sync_engine_impl.cc
+++ b/components/sync/driver/glue/sync_engine_impl.cc
@@ -47,7 +47,6 @@
 SyncEngineBackend::RestoredLocalTransportData
 RestoreLocalTransportDataFromPrefs(const SyncTransportDataPrefs& prefs) {
   SyncEngineBackend::RestoredLocalTransportData result;
-  result.encryption_bootstrap_token = prefs.GetEncryptionBootstrapToken();
   result.keystore_encryption_bootstrap_token =
       prefs.GetKeystoreEncryptionBootstrapToken();
   result.cache_guid = prefs.GetCacheGuid();
@@ -263,11 +262,6 @@
                                 backend_, passphrase));
 }
 
-void SyncEngineImpl::SetEncryptionBootstrapToken(const std::string& token) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  prefs_->SetEncryptionBootstrapToken(token);
-}
-
 void SyncEngineImpl::SetKeystoreEncryptionBootstrapToken(
     const std::string& token) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -657,7 +651,7 @@
 }
 
 void SyncEngineImpl::ClearLocalTransportDataAndNotify() {
-  prefs_->ClearAllExceptEncryptionBootstrapToken();
+  prefs_->ClearAll();
   sync_transport_data_cleared_cb_.Run();
 }
 
diff --git a/components/sync/driver/glue/sync_engine_impl.h b/components/sync/driver/glue/sync_engine_impl.h
index 2d8ee3e6..9127a71 100644
--- a/components/sync/driver/glue/sync_engine_impl.h
+++ b/components/sync/driver/glue/sync_engine_impl.h
@@ -76,7 +76,6 @@
   void StartSyncingWithServer() override;
   void SetEncryptionPassphrase(const std::string& passphrase) override;
   void SetDecryptionPassphrase(const std::string& passphrase) override;
-  void SetEncryptionBootstrapToken(const std::string& token) override;
   void SetKeystoreEncryptionBootstrapToken(const std::string& token) override;
   void AddTrustedVaultDecryptionKeys(
       const std::vector<std::vector<uint8_t>>& keys,
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index b021577..0046757 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -134,7 +134,6 @@
 ProfileSyncService::ProfileSyncService(InitParams init_params)
     : sync_client_(std::move(init_params.sync_client)),
       sync_prefs_(sync_client_->GetPrefService()),
-      sync_transport_data_prefs_(sync_client_->GetPrefService()),
       identity_manager_(init_params.identity_manager),
       auth_manager_(std::make_unique<SyncAuthManager>(
           identity_manager_,
@@ -482,6 +481,7 @@
   params.engine_components_factory =
       std::make_unique<EngineComponentsFactoryImpl>(
           EngineSwitchesFromCommandLine());
+  params.encryption_bootstrap_token = sync_prefs_.GetEncryptionBootstrapToken();
 
   if (!IsLocalSyncEnabled()) {
     auth_manager_->ConnectionOpened();
@@ -519,8 +519,7 @@
     // If the engine hasn't started or is already shut down when a DISABLE_SYNC
     // happens, the Directory needs to be cleaned up here.
     if (reason == ShutdownReason::DISABLE_SYNC) {
-      sync_client_->GetSyncApiComponentFactory()
-          ->ClearAllTransportDataExceptEncryptionBootstrapToken();
+      sync_client_->GetSyncApiComponentFactory()->ClearAllTransportData();
     }
     return;
   }
@@ -588,10 +587,10 @@
       // through first-time setup again and set SyncRequested to false.
       sync_prefs_.ClearFirstSetupComplete();
       sync_prefs_.ClearPassphrasePromptMutedProductVersion();
-      SetSyncRequestedAndIgnoreNotification(false);
       // For explicit passphrase users, clear the encryption key, such that they
       // will need to reenter it if sync gets re-enabled.
-      sync_transport_data_prefs_.ClearEncryptionBootstrapToken();
+      sync_prefs_.ClearEncryptionBootstrapToken();
+      SetSyncRequestedAndIgnoreNotification(false);
       // Also let observers know that Sync-the-feature is now fully disabled
       // (before it possibly starts up again in transport-only mode).
       NotifyObservers();
@@ -1002,6 +1001,12 @@
   NotifyObservers();
 }
 
+void ProfileSyncService::EncryptionBootstrapTokenChanged(
+    const std::string& bootstrap_token) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  sync_prefs_.SetEncryptionBootstrapToken(bootstrap_token);
+}
+
 bool ProfileSyncService::IsSetupInProgress() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return outstanding_setup_in_progress_handles_ > 0;
diff --git a/components/sync/driver/profile_sync_service.h b/components/sync/driver/profile_sync_service.h
index bf74cf0bd..0e0ec0d 100644
--- a/components/sync/driver/profile_sync_service.h
+++ b/components/sync/driver/profile_sync_service.h
@@ -180,6 +180,8 @@
   void CryptoStateChanged() override;
   void CryptoRequiredUserActionChanged() override;
   void ReconfigureDataTypesDueToCrypto() override;
+  void EncryptionBootstrapTokenChanged(
+      const std::string& bootstrap_token) override;
 
   // IdentityManager::Observer implementation.
   void OnAccountsInCookieUpdated(
@@ -368,9 +370,6 @@
 
   // The class that handles getting, setting, and persisting sync preferences.
   SyncPrefs sync_prefs_;
-  // TODO(crbug.com/938894): Remove this member and interact with SyncEngine
-  // instead.
-  SyncTransportDataPrefs sync_transport_data_prefs_;
 
   // Encapsulates user signin - used to set/get the user's authenticated
   // email address and sign-out upon error.
diff --git a/components/sync/driver/profile_sync_service_unittest.cc b/components/sync/driver/profile_sync_service_unittest.cc
index 393e3259..a820385 100644
--- a/components/sync/driver/profile_sync_service_unittest.cc
+++ b/components/sync/driver/profile_sync_service_unittest.cc
@@ -57,7 +57,6 @@
 namespace {
 
 constexpr char kTestUser[] = "test_user@gmail.com";
-constexpr char kTestCacheGuid[] = "test_cache_guid";
 
 class TestSyncServiceObserver : public SyncServiceObserver {
  public:
@@ -165,13 +164,9 @@
   }
 
   void PopulatePrefsForNthSync() {
-    // Set first sync time before initialize to simulate a complete sync setup.
-    SyncTransportDataPrefs transport_data_prefs(prefs());
-    SyncPrefs sync_prefs(prefs());
-    transport_data_prefs.SetCacheGuid(kTestCacheGuid);
-    transport_data_prefs.SetBirthday(FakeSyncEngine::kTestBirthday);
-    transport_data_prefs.SetLastSyncedTime(base::Time::Now());
     component_factory()->set_first_time_sync_configure_done(true);
+    // Set first sync time before initialize to simulate a complete sync setup.
+    SyncPrefs sync_prefs(prefs());
     sync_prefs.SetSyncRequested(true);
     sync_prefs.SetSelectedTypes(
         /*keep_everything_synced=*/true,
@@ -288,20 +283,13 @@
   SignIn();
   CreateService(ProfileSyncService::MANUAL_START);
 
+  // Mimic a sync cycle (transport-only) having completed earlier.
   SyncPrefs sync_prefs(prefs());
   sync_prefs.SetSyncRequested(true);
   sync_prefs.SetSelectedTypes(
       /*keep_everything_synced=*/true,
       /*registered_types=*/UserSelectableTypeSet::All(),
       /*selected_types=*/UserSelectableTypeSet::All());
-
-  // Mimic a sync cycle (transport-only) having completed earlier.
-  const base::Time kLastSyncedTime = base::Time::Now();
-  SyncTransportDataPrefs transport_data_prefs(prefs());
-  transport_data_prefs.SetLastSyncedTime(kLastSyncedTime);
-  transport_data_prefs.SetCacheGuid(kTestCacheGuid);
-  transport_data_prefs.SetBirthday(FakeSyncEngine::kTestBirthday);
-
   service()->Initialize();
 
   EXPECT_EQ(SyncService::DisableReasonSet(), service()->GetDisableReasons());
@@ -311,10 +299,6 @@
             service()->GetTransportState());
   EXPECT_FALSE(service()->IsSyncFeatureActive());
   EXPECT_FALSE(service()->IsSyncFeatureEnabled());
-
-  // The local sync data shouldn't be cleared.
-  EXPECT_EQ(kTestCacheGuid, transport_data_prefs.GetCacheGuid());
-  EXPECT_EQ(kLastSyncedTime, transport_data_prefs.GetLastSyncedTime());
 }
 
 TEST_F(ProfileSyncServiceTest, ModelTypesForTransportMode) {
diff --git a/components/sync/driver/sync_api_component_factory.h b/components/sync/driver/sync_api_component_factory.h
index 041c893..1e36f37e 100644
--- a/components/sync/driver/sync_api_component_factory.h
+++ b/components/sync/driver/sync_api_component_factory.h
@@ -50,10 +50,10 @@
       invalidation::InvalidationService* invalidator,
       syncer::SyncInvalidationsService* sync_invalidation_service) = 0;
 
-  // Clears all local transport data except the encryption bootstrap token.
-  // Upon calling this, the deletion is guaranteed to finish before a new engine
-  // returned by |CreateSyncEngine()| can do any proper work.
-  virtual void ClearAllTransportDataExceptEncryptionBootstrapToken() = 0;
+  // Clears all local transport data. Upon calling this, the deletion is
+  // guaranteed to finish before a new engine returned by |CreateSyncEngine()|
+  // can do any proper work.
+  virtual void ClearAllTransportData() = 0;
 };
 
 }  // namespace syncer
diff --git a/components/sync/driver/sync_service_crypto.cc b/components/sync/driver/sync_service_crypto.cc
index 740e33c..d7758b3 100644
--- a/components/sync/driver/sync_service_crypto.cc
+++ b/components/sync/driver/sync_service_crypto.cc
@@ -511,7 +511,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(state_.engine);
   if (type == PASSPHRASE_BOOTSTRAP_TOKEN) {
-    state_.engine->SetEncryptionBootstrapToken(bootstrap_token);
+    delegate_->EncryptionBootstrapTokenChanged(bootstrap_token);
   } else {
     state_.engine->SetKeystoreEncryptionBootstrapToken(bootstrap_token);
   }
diff --git a/components/sync/driver/sync_service_crypto.h b/components/sync/driver/sync_service_crypto.h
index 10e2aa9..8028fc7 100644
--- a/components/sync/driver/sync_service_crypto.h
+++ b/components/sync/driver/sync_service_crypto.h
@@ -33,6 +33,8 @@
     virtual void CryptoStateChanged() = 0;
     virtual void CryptoRequiredUserActionChanged() = 0;
     virtual void ReconfigureDataTypesDueToCrypto() = 0;
+    virtual void EncryptionBootstrapTokenChanged(
+        const std::string& bootstrap_token) = 0;
   };
 
   // |delegate| must not be null and must outlive this object.
diff --git a/components/sync/driver/sync_service_crypto_unittest.cc b/components/sync/driver/sync_service_crypto_unittest.cc
index 5b75e34d..be52242 100644
--- a/components/sync/driver/sync_service_crypto_unittest.cc
+++ b/components/sync/driver/sync_service_crypto_unittest.cc
@@ -62,6 +62,10 @@
   MOCK_METHOD(void, CryptoStateChanged, (), (override));
   MOCK_METHOD(void, CryptoRequiredUserActionChanged, (), (override));
   MOCK_METHOD(void, ReconfigureDataTypesDueToCrypto, (), (override));
+  MOCK_METHOD(void,
+              EncryptionBootstrapTokenChanged,
+              (const std::string&),
+              (override));
 };
 
 // Object representing a server that contains the authoritative trusted vault
diff --git a/components/sync/driver/sync_user_settings_unittest.cc b/components/sync/driver/sync_user_settings_impl_unittest.cc
similarity index 93%
rename from components/sync/driver/sync_user_settings_unittest.cc
rename to components/sync/driver/sync_user_settings_impl_unittest.cc
index e82b2be..1c9aefa 100644
--- a/components/sync/driver/sync_user_settings_unittest.cc
+++ b/components/sync/driver/sync_user_settings_impl_unittest.cc
@@ -59,11 +59,15 @@
   MOCK_METHOD(void, CryptoStateChanged, (), (override));
   MOCK_METHOD(void, CryptoRequiredUserActionChanged, (), (override));
   MOCK_METHOD(void, ReconfigureDataTypesDueToCrypto, (), (override));
+  MOCK_METHOD(void,
+              EncryptionBootstrapTokenChanged,
+              (const std::string&),
+              (override));
 };
 
-class SyncUserSettingsTest : public testing::Test {
+class SyncUserSettingsImplTest : public testing::Test {
  protected:
-  SyncUserSettingsTest() {
+  SyncUserSettingsImplTest() {
     SyncPrefs::RegisterProfilePrefs(pref_service_.registry());
     sync_prefs_ = std::make_unique<SyncPrefs>(&pref_service_);
 
@@ -88,7 +92,7 @@
   std::unique_ptr<SyncServiceCrypto> sync_service_crypto_;
 };
 
-TEST_F(SyncUserSettingsTest, PreferredTypesSyncEverything) {
+TEST_F(SyncUserSettingsImplTest, PreferredTypesSyncEverything) {
   std::unique_ptr<SyncUserSettingsImpl> sync_user_settings =
       MakeSyncUserSettings(GetUserTypes());
 
@@ -106,7 +110,7 @@
 }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-TEST_F(SyncUserSettingsTest, PreferredTypesSyncAllOsTypes) {
+TEST_F(SyncUserSettingsImplTest, PreferredTypesSyncAllOsTypes) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(chromeos::features::kSplitSettingsSync);
 
@@ -123,7 +127,7 @@
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-TEST_F(SyncUserSettingsTest, PreferredTypesNotKeepEverythingSynced) {
+TEST_F(SyncUserSettingsImplTest, PreferredTypesNotKeepEverythingSynced) {
   std::unique_ptr<SyncUserSettingsImpl> sync_user_settings =
       MakeSyncUserSettings(GetUserTypes());
 
@@ -158,7 +162,7 @@
 }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-TEST_F(SyncUserSettingsTest, PreferredTypesNotAllOsTypesSynced) {
+TEST_F(SyncUserSettingsImplTest, PreferredTypesNotAllOsTypesSynced) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(chromeos::features::kSplitSettingsSync);
 
@@ -189,7 +193,7 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 // Device info should always be enabled.
-TEST_F(SyncUserSettingsTest, DeviceInfo) {
+TEST_F(SyncUserSettingsImplTest, DeviceInfo) {
   std::unique_ptr<SyncUserSettingsImpl> sync_user_settings =
       MakeSyncUserSettings(GetUserTypes());
   EXPECT_TRUE(sync_user_settings->GetPreferredDataTypes().Has(DEVICE_INFO));
@@ -214,7 +218,7 @@
 }
 
 // User Consents should always be enabled.
-TEST_F(SyncUserSettingsTest, UserConsents) {
+TEST_F(SyncUserSettingsImplTest, UserConsents) {
   std::unique_ptr<SyncUserSettingsImpl> sync_user_settings =
       MakeSyncUserSettings(GetUserTypes());
   EXPECT_TRUE(sync_user_settings->GetPreferredDataTypes().Has(USER_CONSENTS));
@@ -239,7 +243,7 @@
 }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-TEST_F(SyncUserSettingsTest, AlwaysPreferredTypes_ChromeOS) {
+TEST_F(SyncUserSettingsImplTest, AlwaysPreferredTypes_ChromeOS) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(chromeos::features::kSplitSettingsSync);
 
@@ -262,7 +266,7 @@
   EXPECT_TRUE(preferred_types.Has(USER_CONSENTS));
 }
 
-TEST_F(SyncUserSettingsTest, AppsAreHandledByOsSettings) {
+TEST_F(SyncUserSettingsImplTest, AppsAreHandledByOsSettings) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(chromeos::features::kSplitSettingsSync);
 
@@ -305,7 +309,7 @@
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-TEST_F(SyncUserSettingsTest, ShouldMutePassphrasePrompt) {
+TEST_F(SyncUserSettingsImplTest, ShouldMutePassphrasePrompt) {
   std::unique_ptr<SyncUserSettingsImpl> sync_user_settings =
       MakeSyncUserSettings(GetUserTypes());
 
@@ -322,7 +326,7 @@
       sync_user_settings->IsPassphrasePromptMutedForCurrentProductVersion());
 }
 
-TEST_F(SyncUserSettingsTest, ShouldClearPassphrasePromptMuteUponUpgrade) {
+TEST_F(SyncUserSettingsImplTest, ShouldClearPassphrasePromptMuteUponUpgrade) {
   // Mimic an old product version being written to prefs.
   sync_prefs_->SetPassphrasePromptMutedProductVersion(/*major_version=*/73);
 
diff --git a/components/sync/engine/sync_engine.h b/components/sync/engine/sync_engine.h
index 866b329..807438c7 100644
--- a/components/sync/engine/sync_engine.h
+++ b/components/sync/engine/sync_engine.h
@@ -64,6 +64,7 @@
     bool enable_local_sync_backend = false;
     base::FilePath local_sync_backend_folder;
     std::unique_ptr<EngineComponentsFactory> engine_components_factory;
+    std::string encryption_bootstrap_token;
 
    private:
     DISALLOW_COPY_AND_ASSIGN(InitParams);
@@ -120,9 +121,8 @@
   // are no pending keys.
   virtual void SetDecryptionPassphrase(const std::string& passphrase) = 0;
 
-  // Legacy bootstrap tokens stored in preferences.
+  // Legacy bootstrap token stored in preferences.
   // TODO(crbug.com/1010397): Delete this API together with the preferences.
-  virtual void SetEncryptionBootstrapToken(const std::string& token) = 0;
   virtual void SetKeystoreEncryptionBootstrapToken(
       const std::string& token) = 0;
 
diff --git a/components/sync/test/engine/fake_sync_engine.cc b/components/sync/test/engine/fake_sync_engine.cc
index 37636265e..b0bbd19 100644
--- a/components/sync/test/engine/fake_sync_engine.cc
+++ b/components/sync/test/engine/fake_sync_engine.cc
@@ -76,8 +76,6 @@
 
 void FakeSyncEngine::SetDecryptionPassphrase(const std::string& passphrase) {}
 
-void FakeSyncEngine::SetEncryptionBootstrapToken(const std::string& token) {}
-
 void FakeSyncEngine::SetKeystoreEncryptionBootstrapToken(
     const std::string& token) {}
 
diff --git a/components/sync/test/engine/fake_sync_engine.h b/components/sync/test/engine/fake_sync_engine.h
index f5ace28..ad2b60a1 100644
--- a/components/sync/test/engine/fake_sync_engine.h
+++ b/components/sync/test/engine/fake_sync_engine.h
@@ -68,8 +68,6 @@
 
   void SetDecryptionPassphrase(const std::string& passphrase) override;
 
-  void SetEncryptionBootstrapToken(const std::string& token) override;
-
   void SetKeystoreEncryptionBootstrapToken(const std::string& token) override;
 
   void AddTrustedVaultDecryptionKeys(
diff --git a/components/sync/test/engine/mock_sync_engine.h b/components/sync/test/engine/mock_sync_engine.h
index a6c6ba7..db54854 100644
--- a/components/sync/test/engine/mock_sync_engine.h
+++ b/components/sync/test/engine/mock_sync_engine.h
@@ -49,10 +49,6 @@
   MOCK_METHOD(void, SetEncryptionPassphrase, (const std::string&), (override));
   MOCK_METHOD(void, SetDecryptionPassphrase, (const std::string&), (override));
   MOCK_METHOD(void,
-              SetEncryptionBootstrapToken,
-              (const std::string&),
-              (override));
-  MOCK_METHOD(void,
               SetKeystoreEncryptionBootstrapToken,
               (const std::string&),
               (override));
diff --git a/components/variations/BUILD.gn b/components/variations/BUILD.gn
index e13a93b..5caffb9 100644
--- a/components/variations/BUILD.gn
+++ b/components/variations/BUILD.gn
@@ -104,6 +104,13 @@
     ]
   }
 
+  if (is_chromeos_ash) {
+    sources += [
+      "variations_crash_keys_chromeos.cc",
+      "variations_crash_keys_chromeos.h",
+    ]
+  }
+
   public_deps = [
     ":variations_features",
     "proto",
@@ -196,6 +203,10 @@
     sources += [ "variations_request_scheduler_mobile_unittest.cc" ]
   }
 
+  if (is_chromeos_ash) {
+    sources += [ "variations_crash_keys_chromeos_unittest.cc" ]
+  }
+
   deps = [
     ":test_support",
     ":variations",
diff --git a/components/variations/child_process_field_trial_syncer_unittest.cc b/components/variations/child_process_field_trial_syncer_unittest.cc
index 4493e7a..b2b7ee9 100644
--- a/components/variations/child_process_field_trial_syncer_unittest.cc
+++ b/components/variations/child_process_field_trial_syncer_unittest.cc
@@ -53,7 +53,7 @@
 }  // namespace
 
 TEST(ChildProcessFieldTrialSyncerTest, FieldTrialState) {
-  base::test::SingleThreadTaskEnvironment task_environment;
+  base::test::TaskEnvironment task_environment;
 
   // We don't use the descriptor here anyways so it's ok to pass -1.
   base::FieldTrialList::CreateTrialsFromCommandLine(
diff --git a/components/variations/synthetic_trial_registry_unittest.cc b/components/variations/synthetic_trial_registry_unittest.cc
index 7bb77445..d2d59a7 100644
--- a/components/variations/synthetic_trial_registry_unittest.cc
+++ b/components/variations/synthetic_trial_registry_unittest.cc
@@ -55,7 +55,7 @@
   }
 
  private:
-  base::test::SingleThreadTaskEnvironment task_environment_;
+  base::test::TaskEnvironment task_environment_;
 
   DISALLOW_COPY_AND_ASSIGN(SyntheticTrialRegistryTest);
 };
diff --git a/components/variations/variations_crash_keys.cc b/components/variations/variations_crash_keys.cc
index 879eef5..e0df470 100644
--- a/components/variations/variations_crash_keys.cc
+++ b/components/variations/variations_crash_keys.cc
@@ -11,11 +11,16 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
+#include "build/buildflag.h"
 #include "components/crash/core/common/crash_key.h"
 #include "components/variations/active_field_trials.h"
 #include "components/variations/buildflags.h"
 #include "components/variations/synthetic_trials.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "components/variations/variations_crash_keys_chromeos.h"
+#endif
+
 namespace variations {
 
 namespace {
@@ -169,6 +174,10 @@
   }
 
   g_variations_crash_key.Set(info.experiment_list);
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  ReportVariationsToChromeOs(info);
+#endif  // IS_CHROMEOS_ASH
 }
 
 void VariationsCrashKeys::OnSyntheticTrialsChanged(
diff --git a/components/variations/variations_crash_keys_chromeos.cc b/components/variations/variations_crash_keys_chromeos.cc
new file mode 100644
index 0000000..0ba1e7d3
--- /dev/null
+++ b/components/variations/variations_crash_keys_chromeos.cc
@@ -0,0 +1,53 @@
+// 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 "components/variations/variations_crash_keys_chromeos.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task/thread_pool.h"
+
+namespace variations {
+
+namespace {
+
+// Path where we put variations in cryptohome.
+constexpr char kCrashVariantFileName[] = ".variant-list.txt";
+
+void WriteVariationsToFile(ExperimentListInfo info) {
+  std::string combined_string =
+      base::StrCat({"upload_var_", kNumExperimentsKey, "=",
+                    base::NumberToString(info.num_experiments),
+                    "\n"
+                    "upload_var_",
+                    kExperimentListKey, "=", info.experiment_list, "\n"});
+
+  base::FilePath path = base::PathService::CheckedGet(base::DIR_HOME);
+  if (path == base::FilePath("/")) {
+    // Fallback to /home/chronos if DIR_HOME is not overridden and
+    // no user has signed in.
+    path = base::FilePath("/home/chronos");
+  }
+
+  path = path.Append(kCrashVariantFileName);
+
+  if (!base::WriteFile(path, combined_string)) {
+    VLOG(1) << "Failed to write " << path.value();
+  }
+}
+
+}  // namespace
+
+void ReportVariationsToChromeOs(ExperimentListInfo info) {
+  // On a thread in the background, write variants to a file.
+  base::ThreadPool::PostTask(
+      FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
+      base::BindOnce(&WriteVariationsToFile, info));
+}
+
+}  // namespace variations
diff --git a/components/variations/variations_crash_keys_chromeos.h b/components/variations/variations_crash_keys_chromeos.h
new file mode 100644
index 0000000..d8f0fbf
--- /dev/null
+++ b/components/variations/variations_crash_keys_chromeos.h
@@ -0,0 +1,19 @@
+// 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 COMPONENTS_VARIATIONS_VARIATIONS_CRASH_KEYS_CHROMEOS_H_
+#define COMPONENTS_VARIATIONS_VARIATIONS_CRASH_KEYS_CHROMEOS_H_
+
+#include "components/variations/variations_crash_keys.h"
+
+namespace variations {
+
+// On a separate thread, report the provided crash keys to Chrome OS using a
+// .variant-list.txt in the user's home directory, or /home/chronos if no user
+// is logged in.
+void ReportVariationsToChromeOs(ExperimentListInfo info);
+
+}  // namespace variations
+
+#endif  // COMPONENTS_VARIATIONS_VARIATIONS_CRASH_KEYS_CHROMEOS_H_
diff --git a/components/variations/variations_crash_keys_chromeos_unittest.cc b/components/variations/variations_crash_keys_chromeos_unittest.cc
new file mode 100644
index 0000000..c70013e
--- /dev/null
+++ b/components/variations/variations_crash_keys_chromeos_unittest.cc
@@ -0,0 +1,86 @@
+// 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 "components/variations/variations_crash_keys_chromeos.h"
+
+#include "base/base_paths.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/metrics/field_trial.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_path_override.h"
+#include "base/test/task_environment.h"
+#include "components/crash/core/common/crash_key.h"
+#include "components/variations/synthetic_trials_active_group_id_provider.h"
+#include "components/variations/variations_crash_keys.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace variations {
+namespace {
+
+class VariationsCrashKeysChromeOsTest : public ::testing::Test {
+ public:
+  VariationsCrashKeysChromeOsTest() {
+    crash_reporter::ResetCrashKeysForTesting();
+    crash_reporter::InitializeCrashKeysForTesting();
+  }
+
+  ~VariationsCrashKeysChromeOsTest() override {
+    SyntheticTrialsActiveGroupIdProvider::GetInstance()->ResetForTesting();
+    ClearCrashKeysInstanceForTesting();
+    crash_reporter::ResetCrashKeysForTesting();
+  }
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(VariationsCrashKeysChromeOsTest);
+};
+
+TEST_F(VariationsCrashKeysChromeOsTest, WritesVariantList) {
+  // Override the homedir so that the class writes to a known location we can
+  // check.
+  base::ScopedTempDir dir;
+  ASSERT_TRUE(dir.CreateUniqueTempDir());
+  const base::FilePath& home_path = dir.GetPath();
+  base::ScopedPathOverride home_override(base::DIR_HOME, home_path);
+  base::FilePath variant_list_path = home_path.Append(".variant-list.txt");
+
+  // Start with 2 trials, one active and one not
+  base::FieldTrialList::CreateFieldTrial("Trial1", "Group1")->group();
+  base::FieldTrialList::CreateFieldTrial("Trial2", "Group2");
+
+  InitCrashKeys();
+
+  base::RunLoop().RunUntilIdle();
+  task_environment_.RunUntilIdle();
+
+  ExperimentListInfo info = GetExperimentListInfo();
+  EXPECT_EQ(1, info.num_experiments);
+  EXPECT_EQ("8e7abfb0-c16397b7,", info.experiment_list);
+
+  std::string contents;
+  ASSERT_TRUE(base::ReadFileToString(variant_list_path, &contents));
+  EXPECT_EQ(contents,
+            "upload_var_num-experiments=1\n"
+            "upload_var_variations=8e7abfb0-c16397b7,\n");
+
+  // Now, activate Trial2.
+  EXPECT_EQ("Group2", base::FieldTrialList::FindFullName("Trial2"));
+  base::RunLoop().RunUntilIdle();
+  task_environment_.RunUntilIdle();
+
+  info = GetExperimentListInfo();
+  EXPECT_EQ(2, info.num_experiments);
+  EXPECT_EQ("8e7abfb0-c16397b7,277f2a3d-d77354d0,", info.experiment_list);
+
+  ASSERT_TRUE(base::ReadFileToString(variant_list_path, &contents));
+  EXPECT_EQ(contents,
+            "upload_var_num-experiments=2\n"
+            "upload_var_variations=8e7abfb0-c16397b7,277f2a3d-d77354d0,\n");
+}
+
+}  // namespace
+}  // namespace variations
diff --git a/components/variations/variations_crash_keys_unittest.cc b/components/variations/variations_crash_keys_unittest.cc
index a0c0074..259aaf6 100644
--- a/components/variations/variations_crash_keys_unittest.cc
+++ b/components/variations/variations_crash_keys_unittest.cc
@@ -42,7 +42,7 @@
   }
 
  private:
-  base::test::SingleThreadTaskEnvironment task_environment_;
+  base::test::TaskEnvironment task_environment_;
 
   DISALLOW_COPY_AND_ASSIGN(VariationsCrashKeysTest);
 };
diff --git a/components/viz/DEPS b/components/viz/DEPS
index fc9c58a7..4f0645c 100644
--- a/components/viz/DEPS
+++ b/components/viz/DEPS
@@ -8,7 +8,7 @@
 ]
 
 specific_include_rules = {
-  ".*_(unittest|perftest|fuzzer)\.cc": [
+  ".*_(unittest|perftest|perf_test|fuzzer)\.cc": [
     "+components/viz",
   ],
 
diff --git a/components/viz/service/display/renderer_perftest.cc b/components/viz/service/display/renderer_perftest.cc
index a7bbc0bf..b813d45e 100644
--- a/components/viz/service/display/renderer_perftest.cc
+++ b/components/viz/service/display/renderer_perftest.cc
@@ -15,21 +15,16 @@
 //    --use_virtualized_gl_contexts=1
 
 #include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/json/json_reader.h"
 #include "base/metrics/histogram.h"
 #include "base/metrics/histogram_base.h"
 #include "base/metrics/histogram_samples.h"
 #include "base/metrics/statistics_recorder.h"
-#include "base/path_service.h"
 #include "base/run_loop.h"
-#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "components/viz/client/client_resource_provider.h"
 #include "components/viz/common/display/renderer_settings.h"
-#include "components/viz/common/quads/render_pass_io.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/service/display/display.h"
@@ -48,7 +43,6 @@
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/gl/gpu_service_impl.h"
 #include "components/viz/test/compositor_frame_helpers.h"
-#include "components/viz/test/paths.h"
 #include "components/viz/test/test_gpu_service_holder.h"
 #include "gpu/command_buffer/client/shared_image_interface.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
@@ -232,35 +226,6 @@
                nearest_neighbor, force_anti_aliasing_off);
 }
 
-bool CompositorRenderPassListFromJSON(
-    const std::string& tag,
-    const std::string& site,
-    uint32_t year,
-    size_t frame_index,
-    CompositorRenderPassList* render_pass_list) {
-  base::FilePath json_path;
-  if (!base::PathService::Get(Paths::DIR_TEST_DATA, &json_path))
-    return false;
-  std::string site_year = site + "_" + base::NumberToString(year);
-  std::string filename = base::NumberToString(frame_index);
-  while (filename.length() < 4)
-    filename = "0" + filename;
-  filename += ".json";
-  json_path = json_path.Append(FILE_PATH_LITERAL("render_pass_data"))
-                  .AppendASCII(tag)
-                  .AppendASCII(site_year)
-                  .AppendASCII(filename);
-  if (!base::PathExists(json_path))
-    return false;
-  std::string json_text;
-  if (!base::ReadFileToString(json_path, &json_text))
-    return false;
-  base::Optional<base::Value> dict = base::JSONReader::Read(json_text);
-  if (!dict.has_value())
-    return false;
-  return CompositorRenderPassListFromDict(dict.value(), render_pass_list);
-}
-
 }  // namespace
 
 template <typename RendererType>
diff --git a/components/viz/service/display/surface_aggregator_perftest.cc b/components/viz/service/display/surface_aggregator_perftest.cc
index 4e8dc55..b819cb7 100644
--- a/components/viz/service/display/surface_aggregator_perftest.cc
+++ b/components/viz/service/display/surface_aggregator_perftest.cc
@@ -7,6 +7,7 @@
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/quads/surface_draw_quad.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
+#include "components/viz/common/surfaces/local_surface_id.h"
 #include "components/viz/service/display/aggregated_frame.h"
 #include "components/viz/service/display/display_resource_provider_software.h"
 #include "components/viz/service/display/surface_aggregator.h"
@@ -16,6 +17,7 @@
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/surfaces/surface_manager.h"
 #include "components/viz/test/compositor_frame_helpers.h"
+#include "components/viz/test/test_surface_id_allocator.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/perf/perf_result_reporter.h"
 
@@ -190,11 +192,78 @@
     reporter.AddResult(kMetricSpeedRunsPerS, timer_.LapsPerSecond());
   }
 
+  void SetUpRenderPassListResources(
+      CompositorRenderPassList* render_pass_list) {
+    std::set<ResourceId> created_resources;
+    for (auto& render_pass : *render_pass_list) {
+      for (auto* quad : render_pass->quad_list) {
+        for (ResourceId resource_id : quad->resources) {
+          // Don't create multiple resources for the same ResourceId.
+          if (created_resources.find(resource_id) != created_resources.end()) {
+            continue;
+          }
+          resource_list_.push_back(TransferableResource::MakeSoftware(
+              SharedBitmap::GenerateId(), quad->rect.size(), RGBA_8888));
+          resource_list_.back().id = resource_id;
+          created_resources.insert(resource_id);
+        }
+      }
+    }
+  }
+
+  void RunSingleSurfaceRenderPassListFromJson(const std::string& tag,
+                                              const std::string& site,
+                                              uint32_t year,
+                                              size_t index) {
+    CompositorRenderPassList render_pass_list;
+    ASSERT_TRUE(CompositorRenderPassListFromJSON(tag, site, year, index,
+                                                 &render_pass_list));
+    ASSERT_FALSE(render_pass_list.empty());
+    this->SetUpRenderPassListResources(&render_pass_list);
+
+    aggregator_ = std::make_unique<SurfaceAggregator>(
+        manager_.surface_manager(), resource_provider_.get(), true, true);
+
+    constexpr FrameSinkId root_frame_sink_id(1, 1);
+    TestSurfaceIdAllocator root_surface_id(root_frame_sink_id);
+    auto root_support = std::make_unique<CompositorFrameSinkSupport>(
+        nullptr, &manager_, root_frame_sink_id, /*is_root=*/true);
+
+    base::TimeTicks next_fake_display_time =
+        base::TimeTicks() + base::TimeDelta::FromSeconds(1);
+
+    timer_.Reset();
+    do {
+      CompositorRenderPassList local_list;
+      CompositorRenderPass::CopyAllForTest(render_pass_list, &local_list);
+      // Ensure damage encompasses the entire output_rect so everything is
+      // aggregated.
+      auto& last_render_pass = *local_list.back();
+      last_render_pass.damage_rect = last_render_pass.output_rect;
+
+      CompositorFrame frame = CompositorFrameBuilder()
+                                  .SetRenderPassList(std::move(local_list))
+                                  .SetTransferableResources(resource_list_)
+                                  .Build();
+      root_support->SubmitCompositorFrame(root_surface_id.local_surface_id(),
+                                          std::move(frame));
+      auto aggregated = aggregator_->Aggregate(
+          root_surface_id, next_fake_display_time, gfx::OVERLAY_TRANSFORM_NONE);
+
+      next_fake_display_time += BeginFrameArgs::DefaultInterval();
+      timer_.NextLap();
+    } while (!timer_.HasTimeLimitExpired());
+
+    auto reporter = SetUpSurfaceAggregatorReporter(site + "_json");
+    reporter.AddResult(kMetricSpeedRunsPerS, timer_.LapsPerSecond());
+  }
+
  protected:
   ServerSharedBitmapManager shared_bitmap_manager_;
   FrameSinkManagerImpl manager_;
   std::unique_ptr<DisplayResourceProvider> resource_provider_;
   std::unique_ptr<SurfaceAggregator> aggregator_;
+  std::vector<TransferableResource> resource_list_;
 };
 
 TEST_F(SurfaceAggregatorPerfTest, ManySurfacesOpaque) {
@@ -251,5 +320,35 @@
           ExpectedOutput(1, 1500));
 }
 
+#define TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(SITE, FRAME)           \
+  TEST_F(SurfaceAggregatorPerfTest, SITE##_JsonTest) {                   \
+    this->RunSingleSurfaceRenderPassListFromJson(                        \
+        /*tag=*/"top_real_world_desktop", /*site=*/#SITE, /*year=*/2018, \
+        /*frame_index=*/FRAME);                                          \
+  }
+
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(accu_weather, 298)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(amazon, 30)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(blogspot, 56)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(ebay, 44)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(espn, 463)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(facebook, 327)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(gmail, 66)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(google_calendar, 53)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(google_docs, 369)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(google_image_search, 44)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(google_plus, 45)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(google_web_search, 89)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(linkedin, 284)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(pinterest, 120)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(techcrunch, 190)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(twitch, 396)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(wikipedia, 48)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(wordpress, 75)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(yahoo_answers, 74)
+TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(yahoo_sports, 269)
+
+#undef TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST
+
 }  // namespace
 }  // namespace viz
diff --git a/components/viz/service/display/viz_perf_test.cc b/components/viz/service/display/viz_perf_test.cc
index 27c137d..bb1e80e 100644
--- a/components/viz/service/display/viz_perf_test.cc
+++ b/components/viz/service/display/viz_perf_test.cc
@@ -5,9 +5,44 @@
 #include "components/viz/service/display/viz_perf_test.h"
 
 #include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
+#include "components/viz/common/quads/render_pass_io.h"
+#include "components/viz/test/paths.h"
 
 namespace viz {
+
+bool CompositorRenderPassListFromJSON(
+    const std::string& tag,
+    const std::string& site,
+    uint32_t year,
+    size_t frame_index,
+    CompositorRenderPassList* render_pass_list) {
+  base::FilePath json_path;
+  if (!base::PathService::Get(Paths::DIR_TEST_DATA, &json_path))
+    return false;
+  std::string site_year = site + "_" + base::NumberToString(year);
+  std::string filename = base::NumberToString(frame_index);
+  while (filename.length() < 4)
+    filename = "0" + filename;
+  filename += ".json";
+  json_path = json_path.Append(FILE_PATH_LITERAL("render_pass_data"))
+                  .AppendASCII(tag)
+                  .AppendASCII(site_year)
+                  .AppendASCII(filename);
+  if (!base::PathExists(json_path))
+    return false;
+  std::string json_text;
+  if (!base::ReadFileToString(json_path, &json_text))
+    return false;
+  base::Optional<base::Value> dict = base::JSONReader::Read(json_text);
+  if (!dict.has_value())
+    return false;
+  return CompositorRenderPassListFromDict(dict.value(), render_pass_list);
+}
+
 namespace {
 
 constexpr char kPerfTestTimeMillis[] = "perf-test-time-ms";
diff --git a/components/viz/service/display/viz_perf_test.h b/components/viz/service/display/viz_perf_test.h
index 5e42536fc..8ef6093e 100644
--- a/components/viz/service/display/viz_perf_test.h
+++ b/components/viz/service/display/viz_perf_test.h
@@ -5,11 +5,23 @@
 #ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_VIZ_PERF_TEST_H_
 #define COMPONENTS_VIZ_SERVICE_DISPLAY_VIZ_PERF_TEST_H_
 
+#include <string>
+
 #include "base/timer/lap_timer.h"
+#include "components/viz/common/quads/compositor_render_pass.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace viz {
 
+// Reads the specified JSON file and parses a CompositorRenderPassList from it,
+// storing the result in |render_pass_list|.
+bool CompositorRenderPassListFromJSON(
+    const std::string& tag,
+    const std::string& site,
+    uint32_t year,
+    size_t frame_index,
+    CompositorRenderPassList* render_pass_list);
+
 // Viz perf test base class that sets up a lap timer with a specified duration.
 class VizPerfTest : public testing::Test {
  public:
diff --git a/components/viz/service/display_embedder/image_context_impl.cc b/components/viz/service/display_embedder/image_context_impl.cc
index 5002a214..bcaa036 100644
--- a/components/viz/service/display_embedder/image_context_impl.cc
+++ b/components/viz/service/display_embedder/image_context_impl.cc
@@ -139,7 +139,8 @@
   if (BindOrCopyTextureIfNecessary(texture_base, &texture_size) &&
       texture_size != size()) {
     DLOG(ERROR) << "Failed to fulfill the promise texture - texture "
-                   "size does not match TransferableResource size.";
+                   "size does not match TransferableResource size: "
+                << texture_size.ToString() << " vs " << size().ToString();
     CreateFallbackImage(context_state);
     return;
   }
@@ -202,7 +203,9 @@
 
     if (representation->size() != size()) {
       DLOG(ERROR) << "Failed to fulfill the promise texture - SharedImage "
-                     "size does not match TransferableResource size.";
+                     "size does not match TransferableResource size: "
+                  << representation->size().ToString() << " vs "
+                  << size().ToString();
       return false;
     }
 
diff --git a/components/webxr/android/java/src/org/chromium/components/webxr/ArImmersiveOverlay.java b/components/webxr/android/java/src/org/chromium/components/webxr/ArImmersiveOverlay.java
index 68660c6d..05a84a8f 100644
--- a/components/webxr/android/java/src/org/chromium/components/webxr/ArImmersiveOverlay.java
+++ b/components/webxr/android/java/src/org/chromium/components/webxr/ArImmersiveOverlay.java
@@ -208,14 +208,15 @@
 
             mWebContentsObserver = new WebContentsObserver() {
                 @Override
-                public void hasEffectivelyFullscreenVideoChange(boolean isFullscreen) {
+                public void didToggleFullscreenModeForTab(
+                        boolean enteredFullscreen, boolean willCauseResize) {
                     if (DEBUG_LOGS) {
                         Log.i(TAG,
-                                "hasEffectivelyFullscreenVideoChange(), isFullscreen="
-                                        + isFullscreen);
+                                "didToggleFullscreenModeForTab(), enteredFullscreen="
+                                        + enteredFullscreen);
                     }
 
-                    if (!isFullscreen) {
+                    if (!enteredFullscreen) {
                         cleanupAndExit();
                     }
                 }
diff --git a/content/browser/SITE_ISOLATION_OWNERS b/content/browser/SITE_ISOLATION_OWNERS
index 30845c1..c0d9435 100644
--- a/content/browser/SITE_ISOLATION_OWNERS
+++ b/content/browser/SITE_ISOLATION_OWNERS
@@ -1,4 +1,3 @@
-acolwell@chromium.org
 alexmos@chromium.org
 creis@chromium.org
 lukasza@chromium.org
diff --git a/content/browser/accessibility/accessibility_event_recorder_mac.mm b/content/browser/accessibility/accessibility_event_recorder_mac.mm
index 285d27903..72254f6 100644
--- a/content/browser/accessibility/accessibility_event_recorder_mac.mm
+++ b/content/browser/accessibility/accessibility_event_recorder_mac.mm
@@ -70,6 +70,8 @@
   AddNotification(@"AXMenuItemSelected");
   AddNotification(@"AXRowCollapsed");
   AddNotification(@"AXRowExpanded");
+  AddNotification((NSString*)kAXMenuClosedNotification);
+  AddNotification((NSString*)kAXMenuOpenedNotification);
   AddNotification(NSAccessibilityFocusedUIElementChangedNotification);
   AddNotification(NSAccessibilityRowCollapsedNotification);
   AddNotification(NSAccessibilityRowCountChangedNotification);
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index efecbd7..51838ae 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -1384,6 +1384,11 @@
   if (BrowserAccessibility* wrapper = GetFromAXNode(node)) {
     if (wrapper == GetLastFocusedNode())
       SetLastFocusedNode(nullptr);
+
+    // We fire these here, immediately, to ensure we can send platform
+    // notifications prior to the actual destruction of the object.
+    if (node->data().role == ax::mojom::Role::kMenu)
+      FireGeneratedEvent(ui::AXEventGenerator::Event::MENU_POPUP_END, wrapper);
   }
 }
 
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc
index afb61ad7..6fb5bd7b 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -302,6 +302,8 @@
     case ui::AXEventGenerator::Event::LIVE_RELEVANT_CHANGED:
     case ui::AXEventGenerator::Event::LIVE_STATUS_CHANGED:
     case ui::AXEventGenerator::Event::LOAD_START:
+    case ui::AXEventGenerator::Event::MENU_POPUP_END:
+    case ui::AXEventGenerator::Event::MENU_POPUP_START:
     case ui::AXEventGenerator::Event::MENU_ITEM_SELECTED:
     case ui::AXEventGenerator::Event::MULTILINE_STATE_CHANGED:
     case ui::AXEventGenerator::Event::MULTISELECTABLE_STATE_CHANGED:
diff --git a/content/browser/accessibility/browser_accessibility_manager_auralinux.cc b/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
index 003b07f..146a147 100644
--- a/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
@@ -97,6 +97,13 @@
       is_expanded);
 }
 
+void BrowserAccessibilityManagerAuraLinux::FireShowingEvent(
+    BrowserAccessibility* node,
+    bool is_showing) {
+  ToBrowserAccessibilityAuraLinux(node)->GetNode()->OnShowingStateChanged(
+      is_showing);
+}
+
 void BrowserAccessibilityManagerAuraLinux::FireInvalidStatusChangedEvent(
     BrowserAccessibility* node) {
   ToBrowserAccessibilityAuraLinux(node)->GetNode()->OnInvalidStatusChanged();
@@ -221,6 +228,12 @@
     case ui::AXEventGenerator::Event::MENU_ITEM_SELECTED:
       FireSelectedEvent(node);
       break;
+    case ui::AXEventGenerator::Event::MENU_POPUP_END:
+      FireShowingEvent(node, false);
+      break;
+    case ui::AXEventGenerator::Event::MENU_POPUP_START:
+      FireShowingEvent(node, true);
+      break;
     case ui::AXEventGenerator::Event::NAME_CHANGED:
       FireNameChangedEvent(node);
       break;
diff --git a/content/browser/accessibility/browser_accessibility_manager_auralinux.h b/content/browser/accessibility/browser_accessibility_manager_auralinux.h
index 0b419d6..75452e5 100644
--- a/content/browser/accessibility/browser_accessibility_manager_auralinux.h
+++ b/content/browser/accessibility/browser_accessibility_manager_auralinux.h
@@ -37,6 +37,7 @@
   void FireSelectedEvent(BrowserAccessibility* node);
   void FireEnabledChangedEvent(BrowserAccessibility* node);
   void FireExpandedEvent(BrowserAccessibility* node, bool is_expanded);
+  void FireShowingEvent(BrowserAccessibility* node, bool is_showing);
   void FireInvalidStatusChangedEvent(BrowserAccessibility* node);
   void FireAriaCurrentChangedEvent(BrowserAccessibility* node);
   void FireLoadingEvent(BrowserAccessibility* node, bool is_loading);
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm
index 96cceee..840b193 100644
--- a/content/browser/accessibility/browser_accessibility_manager_mac.mm
+++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -265,6 +265,22 @@
         return;
       }
       break;
+    case ui::AXEventGenerator::Event::MENU_POPUP_END:
+      // Calling NSAccessibilityPostNotification on a menu which is about to be
+      // closed/destroyed is possible, but the event does not appear to be
+      // emitted reliably by the NSAccessibility stack. If VoiceOver is not
+      // notified that a given menu has been closed, it might fail to present
+      // subsequent changes to the user. WebKit seems to address this by firing
+      // AXMenuClosed on the document itself when an accessible menu is being
+      // detached. See WebKit's AccessibilityObject::detachRemoteParts
+      if (BrowserAccessibilityManager* root_manager = GetRootManager()) {
+        if (BrowserAccessibility* root = root_manager->GetRoot())
+          FireNativeMacNotification((NSString*)kAXMenuClosedNotification, root);
+      }
+      return;
+    case ui::AXEventGenerator::Event::MENU_POPUP_START:
+      mac_notification = (NSString*)kAXMenuOpenedNotification;
+      break;
     case ui::AXEventGenerator::Event::MENU_ITEM_SELECTED:
       mac_notification = ui::NSAccessibilityMenuItemSelectedNotification;
       break;
diff --git a/content/browser/accessibility/browser_accessibility_manager_win.cc b/content/browser/accessibility/browser_accessibility_manager_win.cc
index d06da10..a2e98893 100644
--- a/content/browser/accessibility/browser_accessibility_manager_win.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_win.cc
@@ -306,10 +306,6 @@
       if (node->IsIgnored()) {
         FireWinAccessibilityEvent(EVENT_OBJECT_HIDE, node);
         FireUiaStructureChangedEvent(StructureChangeType_ChildRemoved, node);
-        if (node->GetRole() == ax::mojom::Role::kMenu) {
-          FireWinAccessibilityEvent(EVENT_SYSTEM_MENUPOPUPEND, node);
-          FireUiaAccessibilityEvent(UIA_MenuClosedEventId, node);
-        }
       }
       HandleAriaPropertiesChangedEvent(*node);
       break;
@@ -365,6 +361,14 @@
     case ui::AXEventGenerator::Event::LAYOUT_INVALIDATED:
       FireUiaAccessibilityEvent(UIA_LayoutInvalidatedEventId, node);
       break;
+    case ui::AXEventGenerator::Event::MENU_POPUP_END:
+      FireWinAccessibilityEvent(EVENT_SYSTEM_MENUPOPUPEND, node);
+      FireUiaAccessibilityEvent(UIA_MenuClosedEventId, node);
+      break;
+    case ui::AXEventGenerator::Event::MENU_POPUP_START:
+      FireWinAccessibilityEvent(EVENT_SYSTEM_MENUPOPUPSTART, node);
+      FireUiaAccessibilityEvent(UIA_MenuOpenedEventId, node);
+      break;
     case ui::AXEventGenerator::Event::MULTILINE_STATE_CHANGED:
       HandleAriaPropertiesChangedEvent(*node);
       break;
@@ -470,10 +474,6 @@
     case ui::AXEventGenerator::Event::SUBTREE_CREATED:
       FireWinAccessibilityEvent(EVENT_OBJECT_SHOW, node);
       FireUiaStructureChangedEvent(StructureChangeType_ChildAdded, node);
-      if (node->GetRole() == ax::mojom::Role::kMenu) {
-        FireWinAccessibilityEvent(EVENT_SYSTEM_MENUPOPUPSTART, node);
-        FireUiaAccessibilityEvent(UIA_MenuOpenedEventId, node);
-      }
       break;
     case ui::AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED:
       FireWinAccessibilityEvent(IA2_EVENT_TEXT_ATTRIBUTE_CHANGED, node);
@@ -737,16 +737,6 @@
   }
 }
 
-void BrowserAccessibilityManagerWin::OnNodeWillBeDeleted(ui::AXTree* tree,
-                                                         ui::AXNode* node) {
-  if (node->data().role == ax::mojom::Role::kMenu) {
-    BrowserAccessibility* obj = GetFromAXNode(node);
-    DCHECK(obj);
-    FireWinAccessibilityEvent(EVENT_SYSTEM_MENUPOPUPEND, obj);
-    FireUiaAccessibilityEvent(UIA_MenuClosedEventId, obj);
-  }
-}
-
 void BrowserAccessibilityManagerWin::OnAtomicUpdateFinished(
     ui::AXTree* tree,
     bool root_changed,
diff --git a/content/browser/accessibility/browser_accessibility_manager_win.h b/content/browser/accessibility/browser_accessibility_manager_win.h
index eac24636..d2a417b 100644
--- a/content/browser/accessibility/browser_accessibility_manager_win.h
+++ b/content/browser/accessibility/browser_accessibility_manager_win.h
@@ -75,7 +75,6 @@
  protected:
   // AXTreeObserver methods.
   void OnSubtreeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override;
-  void OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override;
   void OnAtomicUpdateFinished(
       ui::AXTree* tree,
       bool root_changed,
diff --git a/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
index 7547ff4..b008be3 100644
--- a/content/browser/accessibility/dump_accessibility_events_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
@@ -1068,6 +1068,19 @@
   RunEventTest(FILE_PATH_LITERAL("menu-opened-closed.html"));
 }
 
+#if defined(OS_WIN)
+// TODO(crbug.com/1198056#c3): Test is flaky on Windows.
+#define MAYBE_AccessibilityEventsMenubarShowHideMenus \
+  DISABLED_AccessibilityEventsMenubarShowHideMenus
+#else
+#define MAYBE_AccessibilityEventsMenubarShowHideMenus \
+  AccessibilityEventsMenubarShowHideMenus
+#endif
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest,
+                       MAYBE_AccessibilityEventsMenubarShowHideMenus) {
+  RunEventTest(FILE_PATH_LITERAL("menubar-show-hide-menus.html"));
+}
+
 // crbug.com/1047282: disabled due to flakiness.
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest,
                        DISABLED_AccessibilityEventsAriaFlowToChange) {
diff --git a/content/browser/android/content_ui_event_handler.cc b/content/browser/android/content_ui_event_handler.cc
index 0a7042d..265c5ef 100644
--- a/content/browser/android/content_ui_event_handler.cc
+++ b/content/browser/android/content_ui_event_handler.cc
@@ -16,6 +16,7 @@
 #include "ui/events/android/key_event_android.h"
 #include "ui/events/android/motion_event_android.h"
 #include "ui/events/base_event_utils.h"
+#include "ui/events/event_utils.h"
 
 using base::android::AttachCurrentThread;
 using base::android::JavaParamRef;
@@ -99,9 +100,7 @@
   base::TimeTicks current_time = ui::EventTimeForNow();
   base::TimeTicks event_time =
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(time_ms);
-  base::TimeDelta delta = current_time - event_time;
-  UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.OS.MOUSE_WHEEL",
-                              delta.InMicroseconds(), 1, 1000000, 50);
+  ComputeEventLatencyOS(ui::ET_MOUSEWHEEL, event_time, current_time);
   ui::MotionEventAndroid::Pointer pointer(
       0, x, y, 0.0f /* touch_major */, 0.0f /* touch_minor */, 0.0f, 0.0f, 0);
 
diff --git a/content/browser/android/web_contents_observer_proxy.cc b/content/browser/android/web_contents_observer_proxy.cc
index 31a85755..8ffb8f1 100644
--- a/content/browser/android/web_contents_observer_proxy.cc
+++ b/content/browser/android/web_contents_observer_proxy.cc
@@ -229,6 +229,14 @@
       env, java_observer_, is_fullscreen);
 }
 
+void WebContentsObserverProxy::DidToggleFullscreenModeForTab(
+    bool entered_fullscreen,
+    bool will_cause_resize) {
+  JNIEnv* env = AttachCurrentThread();
+  Java_WebContentsObserverProxy_didToggleFullscreenModeForTab(
+      env, java_observer_, entered_fullscreen, will_cause_resize);
+}
+
 void WebContentsObserverProxy::DidFirstVisuallyNonEmptyPaint() {
   JNIEnv* env = AttachCurrentThread();
   Java_WebContentsObserverProxy_didFirstVisuallyNonEmptyPaint(env,
diff --git a/content/browser/android/web_contents_observer_proxy.h b/content/browser/android/web_contents_observer_proxy.h
index 04e4910b..9fab1f7 100644
--- a/content/browser/android/web_contents_observer_proxy.h
+++ b/content/browser/android/web_contents_observer_proxy.h
@@ -62,6 +62,8 @@
   void WebContentsDestroyed() override;
   void DidChangeThemeColor() override;
   void MediaEffectivelyFullscreenChanged(bool is_fullscreen) override;
+  void DidToggleFullscreenModeForTab(bool entered_fullscreen,
+                                     bool will_cause_resize) override;
   bool SetToBaseURLForDataURLIfNeeded(GURL* url);
   void ViewportFitChanged(blink::mojom::ViewportFit value) override;
   void OnWebContentsFocused(RenderWidgetHost*) override;
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.cc b/content/browser/bluetooth/web_bluetooth_service_impl.cc
index 953aefe8..ef657ad3 100644
--- a/content/browser/bluetooth/web_bluetooth_service_impl.cc
+++ b/content/browser/bluetooth/web_bluetooth_service_impl.cc
@@ -1136,14 +1136,9 @@
     return;
   }
 
-  // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating
-  // the callee interface.
-  auto copyable_callback = AdaptCallbackForRepeating(std::move(callback));
   query_result.characteristic->ReadRemoteCharacteristic(
-      base::BindOnce(&WebBluetoothServiceImpl::OnCharacteristicReadValueSuccess,
-                     weak_ptr_factory_.GetWeakPtr(), copyable_callback),
-      base::BindOnce(&WebBluetoothServiceImpl::OnCharacteristicReadValueFailed,
-                     weak_ptr_factory_.GetWeakPtr(), copyable_callback));
+      base::BindOnce(&WebBluetoothServiceImpl::OnCharacteristicReadValue,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void WebBluetoothServiceImpl::RemoteCharacteristicWriteValue(
@@ -1313,14 +1308,9 @@
     return;
   }
 
-  // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating
-  // the callee interface.
-  auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
   query_result.descriptor->ReadRemoteDescriptor(
-      base::BindOnce(&WebBluetoothServiceImpl::OnDescriptorReadValueSuccess,
-                     weak_ptr_factory_.GetWeakPtr(), copyable_callback),
-      base::BindOnce(&WebBluetoothServiceImpl::OnDescriptorReadValueFailed,
-                     weak_ptr_factory_.GetWeakPtr(), copyable_callback));
+      base::BindOnce(&WebBluetoothServiceImpl::OnDescriptorReadValue,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void WebBluetoothServiceImpl::RemoteDescriptorWriteValue(
@@ -1887,21 +1877,19 @@
   std::move(callback).Run(TranslateConnectErrorAndRecord(error_code));
 }
 
-void WebBluetoothServiceImpl::OnCharacteristicReadValueSuccess(
+void WebBluetoothServiceImpl::OnCharacteristicReadValue(
     RemoteCharacteristicReadValueCallback callback,
+    base::Optional<BluetoothRemoteGattService::GattErrorCode> error_code,
     const std::vector<uint8_t>& value) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (error_code.has_value()) {
+    std::move(callback).Run(TranslateGATTError(error_code.value()),
+                            /*value=*/base::nullopt);
+    return;
+  }
   std::move(callback).Run(blink::mojom::WebBluetoothResult::SUCCESS, value);
 }
 
-void WebBluetoothServiceImpl::OnCharacteristicReadValueFailed(
-    RemoteCharacteristicReadValueCallback callback,
-    BluetoothRemoteGattService::GattErrorCode error_code) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  std::move(callback).Run(TranslateGATTError(error_code),
-                          /*value=*/base::nullopt);
-}
-
 void WebBluetoothServiceImpl::OnCharacteristicWriteValueSuccess(
     RemoteCharacteristicWriteValueCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -1949,21 +1937,19 @@
   std::move(callback).Run();
 }
 
-void WebBluetoothServiceImpl::OnDescriptorReadValueSuccess(
+void WebBluetoothServiceImpl::OnDescriptorReadValue(
     RemoteDescriptorReadValueCallback callback,
+    base::Optional<BluetoothRemoteGattService::GattErrorCode> error_code,
     const std::vector<uint8_t>& value) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (error_code.has_value()) {
+    std::move(callback).Run(TranslateGATTError(error_code.value()),
+                            /*value=*/base::nullopt);
+    return;
+  }
   std::move(callback).Run(blink::mojom::WebBluetoothResult::SUCCESS, value);
 }
 
-void WebBluetoothServiceImpl::OnDescriptorReadValueFailed(
-    RemoteDescriptorReadValueCallback callback,
-    BluetoothRemoteGattService::GattErrorCode error_code) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  std::move(callback).Run(TranslateGATTError(error_code),
-                          /*value=*/base::nullopt);
-}
-
 void WebBluetoothServiceImpl::OnDescriptorWriteValueSuccess(
     RemoteDescriptorWriteValueCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.h b/content/browser/bluetooth/web_bluetooth_service_impl.h
index 436183d..c353346 100644
--- a/content/browser/bluetooth/web_bluetooth_service_impl.h
+++ b/content/browser/bluetooth/web_bluetooth_service_impl.h
@@ -115,6 +115,8 @@
                            BluetoothScanningPermissionRevokedWhenBlocked);
   FRIEND_TEST_ALL_PREFIXES(WebBluetoothServiceImplTest,
                            BluetoothScanningPermissionRevokedWhenFocusIsLost);
+  FRIEND_TEST_ALL_PREFIXES(WebBluetoothServiceImplTest,
+                           ReadCharacteristicValueErrorWithValueIgnored);
   friend class FrameConnectedBluetoothDevicesTest;
   friend class WebBluetoothServiceImplTest;
   using PrimaryServicesRequestCallback =
@@ -294,12 +296,11 @@
       device::BluetoothDevice::ConnectErrorCode error_code);
 
   // Callbacks for BluetoothRemoteGattCharacteristic::ReadRemoteCharacteristic.
-  void OnCharacteristicReadValueSuccess(
+  void OnCharacteristicReadValue(
       RemoteCharacteristicReadValueCallback callback,
+      base::Optional<device::BluetoothRemoteGattService::GattErrorCode>
+          error_code,
       const std::vector<uint8_t>& value);
-  void OnCharacteristicReadValueFailed(
-      RemoteCharacteristicReadValueCallback callback,
-      device::BluetoothRemoteGattService::GattErrorCode error_code);
 
   // Callbacks for BluetoothRemoteGattCharacteristic::WriteRemoteCharacteristic.
   void OnCharacteristicWriteValueSuccess(
@@ -324,11 +325,11 @@
       RemoteCharacteristicStopNotificationsCallback callback);
 
   // Callbacks for BluetoothRemoteGattDescriptor::ReadRemoteDescriptor.
-  void OnDescriptorReadValueSuccess(RemoteDescriptorReadValueCallback callback,
-                                    const std::vector<uint8_t>& value);
-  void OnDescriptorReadValueFailed(
+  void OnDescriptorReadValue(
       RemoteDescriptorReadValueCallback callback,
-      device::BluetoothRemoteGattService::GattErrorCode error_code);
+      base::Optional<device::BluetoothRemoteGattService::GattErrorCode>
+          error_code,
+      const std::vector<uint8_t>& value);
 
   // Callbacks for BluetoothRemoteGattDescriptor::WriteRemoteDescriptor.
   void OnDescriptorWriteValueSuccess(
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc b/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
index b69e5161..f002398a 100644
--- a/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
+++ b/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
@@ -5,6 +5,7 @@
 #include "content/browser/bluetooth/web_bluetooth_service_impl.h"
 
 #include <utility>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/test/bind.h"
@@ -20,6 +21,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom.h"
 
+using testing::Mock;
 using testing::Return;
 
 namespace content {
@@ -228,10 +230,10 @@
     RenderViewHostImplTestHarness::SetUp();
 
     // Set up an adapter.
-    scoped_refptr<FakeBluetoothAdapter> adapter(new FakeBluetoothAdapter());
-    EXPECT_CALL(*adapter, IsPresent()).WillRepeatedly(Return(true));
+    adapter_ = new FakeBluetoothAdapter();
+    EXPECT_CALL(*adapter_, IsPresent()).WillRepeatedly(Return(true));
     BluetoothAdapterFactoryWrapper::Get().SetBluetoothAdapterForTesting(
-        adapter);
+        adapter_);
 
     // Hook up the test bluetooth delegate.
     old_browser_client_ = SetBrowserClientForTesting(&browser_client_);
@@ -244,6 +246,7 @@
   }
 
   void TearDown() override {
+    adapter_.reset();
     service_ = nullptr;
     SetBrowserClientForTesting(old_browser_client_);
     RenderViewHostImplTestHarness::TearDown();
@@ -301,6 +304,7 @@
     return result;
   }
 
+  scoped_refptr<FakeBluetoothAdapter> adapter_;
   WebBluetoothServiceImpl* service_;
   TestContentBrowserClient browser_client_;
   ContentBrowserClient* old_browser_client_ = nullptr;
@@ -501,4 +505,34 @@
   EXPECT_TRUE(client_impl_3.on_connection_error_called());
 }
 
+TEST_F(WebBluetoothServiceImplTest,
+       ReadCharacteristicValueErrorWithValueIgnored) {
+  // The contract for calls accepting a
+  // BluetoothRemoteGattCharacteristic::ValueCallback callback argument is that
+  // when an error occurs, value must be ignored. This test verifies that
+  // WebBluetoothServiceImpl::OnCharacteristicReadValue honors that contract
+  // and will not pass a value to it's callback
+  // (a RemoteCharacteristicReadValueCallback instance) when an error occurs
+  // with a non-empty value array.
+  const std::vector<uint8_t> read_error_value = {1, 2, 3};
+  bool callback_called = false;
+  service_->OnCharacteristicReadValue(
+      base::BindLambdaForTesting(
+          [&callback_called](
+              blink::mojom::WebBluetoothResult result,
+              const base::Optional<std::vector<uint8_t>>& value) {
+            callback_called = true;
+            EXPECT_EQ(
+                blink::mojom::WebBluetoothResult::GATT_OPERATION_IN_PROGRESS,
+                result);
+            EXPECT_FALSE(value.has_value());
+          }),
+      device::BluetoothGattService::GATT_ERROR_IN_PROGRESS, read_error_value);
+  EXPECT_TRUE(callback_called);
+
+  // This test doesn't invoke any methods of the mock adapter. Allow it to be
+  // leaked without producing errors.
+  Mock::AllowLeak(adapter_.get());
+}
+
 }  // namespace content
diff --git a/content/browser/cross_origin_opener_policy_browsertest.cc b/content/browser/cross_origin_opener_policy_browsertest.cc
index 215711db..1c2ceefb 100644
--- a/content/browser/cross_origin_opener_policy_browsertest.cc
+++ b/content/browser/cross_origin_opener_policy_browsertest.cc
@@ -3138,6 +3138,57 @@
 #endif  // defined(OS_ANDROID)
 }
 
+// Check setting the OriginTrial works, even in popups where the javascript
+// context of the initial empty document is reused.
+IN_PROC_BROWSER_TEST_F(UnrestrictedSharedArrayBufferOriginTrialBrowserTest,
+                       HasSharedArrayBufferReuseContext) {
+  // Create a document without the origin trial in a renderer process.
+  {
+    URLLoaderInterceptor interceptor(base::BindLambdaForTesting(
+        [&](URLLoaderInterceptor::RequestParams* params) {
+          DCHECK_EQ(params->url_request.url, OriginTrialURL());
+          URLLoaderInterceptor::WriteResponse(
+              "HTTP/1.1 200 OK\n"
+              "Content-type: text/html\n\n",
+              "", params->client.get());
+          return true;
+        }));
+    EXPECT_TRUE(NavigateToURL(shell(), OriginTrialURL()));
+    EXPECT_EQ(false, EvalJs(current_frame_host(),
+                            "'SharedArrayBuffer' in globalThis"));
+  }
+
+  // In the same process, open a popup. The document loaded defines an
+  // OriginTrial. It will reuse the javascript context created for the initial
+  // empty document.
+  {
+    URLLoaderInterceptor interceptor(base::BindLambdaForTesting(
+        [&](URLLoaderInterceptor::RequestParams* params) {
+          DCHECK_EQ(params->url_request.url, OriginTrialURL());
+          URLLoaderInterceptor::WriteResponse(
+              "HTTP/1.1 200 OK\n"
+              "Content-type: text/html\n"
+              "Origin-Trial: " +
+                  OriginTrialToken() + "\n\n",
+              "", params->client.get());
+          return true;
+        }));
+    ShellAddedObserver shell_observer;
+    EXPECT_TRUE(ExecJs(current_frame_host(), "window.open(location.href)"));
+
+    auto* popup = static_cast<WebContentsImpl*>(
+        shell_observer.GetShell()->web_contents());
+    WaitForLoadStop(popup);
+
+#if defined(OS_ANDROID)
+    EXPECT_EQ(false, EvalJs(popup, "'SharedArrayBuffer' in globalThis"));
+#else
+    // TODO(https://crbug.com/1204271). This should be true instead.
+    EXPECT_EQ(false, EvalJs(popup, "'SharedArrayBuffer' in globalThis"));
+#endif
+  }
+}
+
 IN_PROC_BROWSER_TEST_F(UnrestrictedSharedArrayBufferOriginTrialBrowserTest,
                        SupportForMeta) {
   URLLoaderInterceptor interceptor(base::BindLambdaForTesting(
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 02ae2a1..fe20b4a 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -948,7 +948,10 @@
   // URLLoaderClient has already been transferred to the renderer process and
   // OnComplete is not expected to be called here.
   if (status.error_code == net::OK) {
-    DEBUG_ALIAS_FOR_GURL(navigate_url, url_);
+    base::debug::SetCrashKeyString(
+        base::debug::AllocateCrashKeyString("navigate_url",
+                                            base::debug::CrashKeySize::Size256),
+        url_.spec());
     base::debug::DumpWithoutCrashing();
     return;
   }
diff --git a/content/browser/renderer_host/OWNERS b/content/browser/renderer_host/OWNERS
index 8e94ea5..7c731b07 100644
--- a/content/browser/renderer_host/OWNERS
+++ b/content/browser/renderer_host/OWNERS
@@ -11,7 +11,6 @@
 jonross@chromium.org
 
 # RenderFrameHost and navigation-related code.
-acolwell@chromium.org
 alexmos@chromium.org
 altimin@chromium.org
 arthursonzogni@chromium.org
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 9dc89454..6bf39d05 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -92,6 +92,7 @@
 #include "ui/events/blink/blink_features.h"
 #include "ui/events/blink/did_overscroll_params.h"
 #include "ui/events/blink/web_input_event_traits.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/gesture_detection/gesture_provider_config_helper.h"
 #include "ui/gfx/android/view_configuration.h"
 #include "ui/gfx/codec/jpeg_codec.h"
@@ -2304,25 +2305,24 @@
 void RenderWidgetHostViewAndroid::ComputeEventLatencyOSTouchHistograms(
       const ui::MotionEvent& event) {
   base::TimeTicks event_time = event.GetEventTime();
-  base::TimeDelta delta = base::TimeTicks::Now() - event_time;
+  base::TimeTicks current_time = base::TimeTicks::Now();
+  ui::EventType event_type;
   switch (event.GetAction()) {
     case ui::MotionEvent::Action::DOWN:
     case ui::MotionEvent::Action::POINTER_DOWN:
-      UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.OS.TOUCH_PRESSED",
-                                  delta.InMicroseconds(), 1, 1000000, 50);
-      return;
+      event_type = ui::ET_TOUCH_PRESSED;
+      break;
     case ui::MotionEvent::Action::MOVE:
-      UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.OS.TOUCH_MOVED",
-                                  delta.InMicroseconds(), 1, 1000000, 50);
-      return;
+      event_type = ui::ET_TOUCH_MOVED;
+      break;
     case ui::MotionEvent::Action::UP:
     case ui::MotionEvent::Action::POINTER_UP:
-      UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.OS.TOUCH_RELEASED",
-                                  delta.InMicroseconds(), 1, 1000000, 50);
-      return;
+      event_type = ui::ET_TOUCH_RELEASED;
+      break;
     default:
       return;
   }
+  ui::ComputeEventLatencyOS(event_type, event_time, current_time);
 }
 
 void RenderWidgetHostViewAndroid::CreateOverscrollControllerIfPossible() {
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
index d3f8bd7..ea91436 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -1697,7 +1697,7 @@
   base::HistogramTester histogram_tester;
 
   // Send an initial wheel event for scrolling by 3 lines.
-  // Verify that Event.Latency.OS.MOUSE_WHEEL histogram is computed properly.
+  // Verify that Event.Latency.OS2.MOUSE_WHEEL histogram is computed properly.
   NSEvent* wheelEvent = MockScrollWheelEventWithPhase(@selector(phaseBegan),3);
   [rwhv_mac_->GetInProcessNSView() scrollWheel:wheelEvent];
 
@@ -1708,6 +1708,7 @@
       blink::mojom::InputEventResultState::kConsumed);
 
   histogram_tester.ExpectTotalCount("Event.Latency.OS.MOUSE_WHEEL", 1);
+  histogram_tester.ExpectTotalCount("Event.Latency.OS2.MOUSE_WHEEL", 1);
 }
 
 // This test verifies that |selected_text_| is updated accordingly with
diff --git a/content/common/font_list_mac.mm b/content/common/font_list_mac.mm
index 6b605e97..1171fe6f 100644
--- a/content/common/font_list_mac.mm
+++ b/content/common/font_list_mac.mm
@@ -5,40 +5,231 @@
 #include "content/common/font_list.h"
 
 #import <Cocoa/Cocoa.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreText/CoreText.h>
 
 #include <utility>
 
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_cftyperef.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/values.h"
 
+// The code here is unusually skeptical about the macOS APIs returning non-null
+// values. An earlier version was reverted due to crashing tests on bots running
+// older macOS versions. The DCHECKs are there to expedite debugging similar
+// problems.
+
 namespace content {
 
+namespace {
+
+// Core Text-based localized family name lookup.
+//
+// This class is not thread-safe.
+//
+// This class caches some state for an efficient implementation of
+// [NSFontManager localizedNameForFamily:face:] using the Core Text API.
+class FontFamilyResolver {
+ public:
+  FontFamilyResolver() {
+    DCHECK(mandatory_attributes_ != nullptr);
+    DCHECK(font_descriptor_attributes_ != nullptr);
+  }
+  ~FontFamilyResolver() = default;
+
+  FontFamilyResolver(const FontFamilyResolver&) = delete;
+  FontFamilyResolver& operator=(const FontFamilyResolver&) = delete;
+
+  // Returns a localized font family name for the given family name.
+  base::ScopedCFTypeRef<CFStringRef> CopyLocalizedFamilyName(
+      CFStringRef family_name) {
+    DCHECK(family_name != nullptr);
+
+    CFDictionarySetValue(font_descriptor_attributes_.get(),
+                         kCTFontFamilyNameAttribute, family_name);
+    base::ScopedCFTypeRef<CTFontDescriptorRef> raw_descriptor(
+        CTFontDescriptorCreateWithAttributes(
+            font_descriptor_attributes_.get()));
+    DCHECK(raw_descriptor != nullptr)
+        << "CTFontDescriptorCreateWithAttributes returned null";
+
+    base::ScopedCFTypeRef<CFArrayRef> normalized_descriptors(
+        CTFontDescriptorCreateMatchingFontDescriptors(
+            raw_descriptor, mandatory_attributes_.get()));
+    DCHECK(normalized_descriptors != nullptr)
+        << "CTFontDescriptorCreateMatchingFontDescriptors returned null";
+    return CopyLocalizedFamilyNameFrom(family_name,
+                                       normalized_descriptors.get());
+  }
+
+  // True if the font should be hidden from Chrome.
+  //
+  // On macOS 10.15, CTFontManagerCopyAvailableFontFamilyNames() filters hidden
+  // fonts. This is not true on older version of macOS that Chrome still
+  // supports. The unittest FontTest.GetFontListDoesNotIncludeHiddenFonts can be
+  // used to determine when it's safe to slim down / remove this function.
+  static bool IsHiddenFontFamily(CFStringRef family_name) {
+    DCHECK(family_name != nullptr);
+    DCHECK_GT(CFStringGetLength(family_name), 0);
+
+    // macOS 10.13 includes names that start with . (period). These fonts should
+    // not be shown to users.
+    return CFStringGetCharacterAtIndex(family_name, 0) == '.';
+  }
+
+ private:
+  // Returns the first font descriptor matching the given family name.
+  //
+  // |descriptors| must be an array of CTFontDescriptors whose font family name
+  // attribute is populated.
+  //
+  // Returns null if none of the descriptors match.
+  static base::ScopedCFTypeRef<CTFontDescriptorRef> FindFirstWithFamilyName(
+      CFStringRef family_name,
+      CFArrayRef descriptors) {
+    DCHECK(family_name != nullptr);
+    DCHECK(descriptors != nullptr);
+
+    CFIndex normalized_descriptor_count = CFArrayGetCount(descriptors);
+    for (CFIndex i = 0; i < normalized_descriptor_count; ++i) {
+      CTFontDescriptorRef descriptor =
+          base::mac::CFCastStrict<CTFontDescriptorRef>(
+              CFArrayGetValueAtIndex(descriptors, i));
+      DCHECK(descriptor != nullptr)
+          << "The descriptors array has a null element.";
+
+      base::ScopedCFTypeRef<CFStringRef> descriptor_family_name(
+          base::mac::CFCastStrict<CFStringRef>(CTFontDescriptorCopyAttribute(
+              descriptor, kCTFontFamilyNameAttribute)));
+      if (CFStringCompare(family_name, descriptor_family_name,
+                          /*compareOptions=*/0) == kCFCompareEqualTo) {
+        return base::ScopedCFTypeRef<CTFontDescriptorRef>(
+            descriptor, base::scoped_policy::RETAIN);
+      }
+    }
+    return base::ScopedCFTypeRef<CTFontDescriptorRef>(nullptr);
+  }
+
+  // Returns a localized font family name for the given family name.
+  //
+  // |descriptors| must be an array of CTFontDescriptors representing all
+  // descriptors on the system matching the given family name.
+  //
+  // The given family name is returned as a fallback, if none of the descriptors
+  // match the desired font family name.
+  static base::ScopedCFTypeRef<CFStringRef> CopyLocalizedFamilyNameFrom(
+      CFStringRef family_name,
+      CFArrayRef descriptors) {
+    DCHECK(family_name != nullptr);
+    DCHECK(descriptors != nullptr);
+
+    base::ScopedCFTypeRef<CTFontDescriptorRef> descriptor =
+        FindFirstWithFamilyName(family_name, descriptors);
+    if (descriptor == nullptr) {
+      DLOG(WARNING) << "Will use non-localized family name for font family: "
+                    << family_name;
+      return base::ScopedCFTypeRef<CFStringRef>(family_name,
+                                                base::scoped_policy::RETAIN);
+    }
+
+    base::ScopedCFTypeRef<CFStringRef> localized_family_name(
+        base::mac::CFCastStrict<CFStringRef>(
+            CTFontDescriptorCopyLocalizedAttribute(descriptor,
+                                                   kCTFontFamilyNameAttribute,
+                                                   /*language=*/nullptr)));
+    DCHECK(localized_family_name != nullptr)
+        << "CTFontDescriptorCopyLocalizedAttribute returned null";
+    return localized_family_name;
+  }
+
+  // Creates the set stored in |mandatory_attributes_|.
+  static base::ScopedCFTypeRef<CFSetRef> CreateMandatoryAttributes() {
+    CFStringRef set_values[] = {kCTFontFamilyNameAttribute};
+    return base::ScopedCFTypeRef<CFSetRef>(CFSetCreate(
+        kCFAllocatorDefault, reinterpret_cast<const void**>(set_values),
+        base::size(set_values), &kCFTypeSetCallBacks));
+  }
+
+  // Creates the mutable dictionary stored in |font_descriptor_attributes_|.
+  static base::ScopedCFTypeRef<CFMutableDictionaryRef>
+  CreateFontDescriptorAttributes() {
+    return base::ScopedCFTypeRef<CFMutableDictionaryRef>(
+        CFDictionaryCreateMutable(kCFAllocatorDefault, /*capacity=*/1,
+                                  &kCFTypeDictionaryKeyCallBacks,
+                                  &kCFTypeDictionaryValueCallBacks));
+  }
+
+  // Used for all CTFontDescriptorCreateMatchingFontDescriptors() calls.
+  //
+  // Caching this dictionary saves one dictionary creation per lookup.
+  const base::ScopedCFTypeRef<CFSetRef> mandatory_attributes_ =
+      CreateMandatoryAttributes();
+
+  // Used for all CTFontDescriptorCreateMatchingFontDescriptors() calls.
+  //
+  // This dictionary has exactly one key, kCTFontFamilyNameAttribute. The value
+  // associated with the key is overwritten as needed.
+  //
+  // Caching this dictionary saves one dictionary creation per lookup.
+  const base::ScopedCFTypeRef<CFMutableDictionaryRef>
+      font_descriptor_attributes_ = CreateFontDescriptorAttributes();
+};
+
+}  // namespace
+
 std::unique_ptr<base::ListValue> GetFontList_SlowBlocking() {
   @autoreleasepool {
-    std::unique_ptr<base::ListValue> font_list(new base::ListValue);
-    NSFontManager* fontManager = [[[NSFontManager alloc] init] autorelease];
-    NSMutableDictionary* fonts_dict = [NSMutableDictionary dictionary];
-    NSArray* fonts = [fontManager availableFontFamilies];
+    FontFamilyResolver resolver;
 
-    for (NSString* family_name in fonts) {
-      NSString* localized_family_name =
-          [fontManager localizedNameForFamily:family_name face:nil];
-      fonts_dict[family_name] = localized_family_name;
+    base::ScopedCFTypeRef<CFArrayRef> cf_family_names(
+        CTFontManagerCopyAvailableFontFamilyNames());
+    DCHECK(cf_family_names != nullptr)
+        << "CTFontManagerCopyAvailableFontFamilyNames returned null";
+    NSArray* family_names = base::mac::CFToNSCast(cf_family_names.get());
+
+    // Maps localized font family names to non-localized names.
+    NSMutableDictionary* family_name_map = [NSMutableDictionary
+        dictionaryWithCapacity:CFArrayGetCount(cf_family_names)];
+    for (NSString* family_name in family_names) {
+      DCHECK(family_name != nullptr)
+          << "CTFontManagerCopyAvailableFontFamilyNames returned an array with "
+          << "a null element";
+
+      CFStringRef family_name_cf = base::mac::NSToCFCast(family_name);
+      if (FontFamilyResolver::IsHiddenFontFamily(family_name_cf))
+        continue;
+
+      base::ScopedCFTypeRef<CFStringRef> cf_normalized_family_name =
+          resolver.CopyLocalizedFamilyName(base::mac::NSToCFCast(family_name));
+      DCHECK(cf_normalized_family_name != nullptr)
+          << "FontFamilyResolver::CopyLocalizedFamilyName returned null";
+      family_name_map[base::mac::CFToNSCast(cf_normalized_family_name)] =
+          family_name;
     }
 
-    // Sort family names based on localized names.
-    NSArray* sortedFonts = [fonts_dict
+    // The Apple documentation for CTFontManagerCopyAvailableFontFamilyNames
+    // states that it returns family names sorted for user interface display.
+    // https://developer.apple.com/documentation/coretext/1499494-ctfontmanagercopyavailablefontfa
+    //
+    // This doesn't seem to be the case, at least on macOS 10.15.3.
+    NSArray* sorted_localized_family_names = [family_name_map
         keysSortedByValueUsingSelector:@selector(localizedStandardCompare:)];
 
-    for (NSString* family_name in sortedFonts) {
-      NSString* localized_family_name = fonts_dict[family_name];
-      auto font_item = std::make_unique<base::ListValue>();
-      font_item->AppendString(base::SysNSStringToUTF16(family_name));
-      font_item->AppendString(base::SysNSStringToUTF16(localized_family_name));
-      font_list->Append(std::move(font_item));
+    std::vector<base::Value> font_list;
+    for (NSString* localized_family_name in sorted_localized_family_names) {
+      NSString* family_name = family_name_map[localized_family_name];
+
+      std::vector<base::Value> font_list_item;
+      font_list_item.reserve(2);
+      font_list_item.emplace_back(base::SysNSStringToUTF8(family_name));
+      font_list_item.emplace_back(
+          base::SysNSStringToUTF8(localized_family_name));
+      font_list.emplace_back(std::move(font_list_item));
     }
 
-    return font_list;
+    return std::make_unique<base::ListValue>(base::Value(font_list).TakeList());
   }
 }
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java
index 0fc7796..19ddae8 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java
@@ -268,6 +268,15 @@
 
     @Override
     @CalledByNative
+    public void didToggleFullscreenModeForTab(boolean enteredFullscreen, boolean willCauseResize) {
+        for (mObserversIterator.rewind(); mObserversIterator.hasNext();) {
+            mObserversIterator.next().didToggleFullscreenModeForTab(
+                    enteredFullscreen, willCauseResize);
+        }
+    }
+
+    @Override
+    @CalledByNative
     public void viewportFitChanged(@WebContentsObserver.ViewportFitType int value) {
         for (mObserversIterator.rewind(); mObserversIterator.hasNext();) {
             mObserversIterator.next().viewportFitChanged(value);
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java b/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java
index c2131350..d5b63d9 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java
@@ -178,12 +178,19 @@
     public void didChangeThemeColor() {}
 
     /**
-     * Called when the Web Contents leaves or enters fullscreen mode.
+     * Called when Media in the Web Contents leaves or enters fullscreen mode.
      * @param isFullscreen whether fullscreen is being entered or left.
      */
     public void hasEffectivelyFullscreenVideoChange(boolean isFullscreen) {}
 
     /**
+     * Called when the Web Contents is toggled into or out of fullscreen mode by the renderer.
+     * @param enteredFullscreen whether fullscreen is being entered or left.
+     * @param willCauseResize whether the change to fullscreen will cause the contents to resize.
+     */
+    public void didToggleFullscreenModeForTab(boolean enteredFullscreen, boolean willCauseResize) {}
+
+    /**
      * The Viewport Fit Type passed to viewportFitChanged. This is mirrored
      * in an enum in display_cutout.mojom.
      */
diff --git a/content/test/data/accessibility/event/menu-opened-closed-expected-auralinux.txt b/content/test/data/accessibility/event/menu-opened-closed-expected-auralinux.txt
new file mode 100644
index 0000000..65f44a5
--- /dev/null
+++ b/content/test/data/accessibility/event/menu-opened-closed-expected-auralinux.txt
@@ -0,0 +1,11 @@
+CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_MENU) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:SHOWING:TRUE role=ROLE_MENU name='menu' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE
+=== Start Continuation ===
+CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_MENU) role=ROLE_MENU_ITEM ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:SHOWING:TRUE role=ROLE_MENU name='(null)' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE
+=== Start Continuation ===
+CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_MENU) role=ROLE_MENU_ITEM ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='(null)' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE
+=== Start Continuation ===
+CHILDREN-CHANGED:REMOVE index:0 CHILD:(role=ROLE_MENU) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='menu' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE
diff --git a/content/test/data/accessibility/event/menu-opened-closed-expected-mac.txt b/content/test/data/accessibility/event/menu-opened-closed-expected-mac.txt
new file mode 100644
index 0000000..abfe510
--- /dev/null
+++ b/content/test/data/accessibility/event/menu-opened-closed-expected-mac.txt
@@ -0,0 +1,7 @@
+AXMenuOpened on AXMenu AXDescription="menu"
+=== Start Continuation ===
+AXMenuOpened on AXMenu
+=== Start Continuation ===
+AXMenuClosed on AXWebArea
+=== Start Continuation ===
+AXMenuClosed on AXWebArea
diff --git a/content/test/data/accessibility/event/menu-opened-closed.html b/content/test/data/accessibility/event/menu-opened-closed.html
index 8cbb43a..97d6e526 100644
--- a/content/test/data/accessibility/event/menu-opened-closed.html
+++ b/content/test/data/accessibility/event/menu-opened-closed.html
@@ -1,4 +1,5 @@
 <!--
+@AURALINUX-DENY:STATE-CHANGE:DEFUNCT*
 @WIN-DENY:EVENT_OBJECT_REORDER*
 @WIN-DENY:EVENT_OBJECT_SHOW*
 @WIN-DENY:EVENT_OBJECT_HIDE*
diff --git a/content/test/data/accessibility/event/menubar-show-hide-menus-expected-auralinux.txt b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-auralinux.txt
new file mode 100644
index 0000000..18339a0
--- /dev/null
+++ b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-auralinux.txt
@@ -0,0 +1,18 @@
+STATE-CHANGE:EXPANDED:TRUE role=ROLE_MENU_ITEM name='File' ENABLED,EXPANDABLE,EXPANDED,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:EXPANDED:TRUE role=ROLE_MENU_ITEM name='New' ENABLED,EXPANDABLE,EXPANDED,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:SHOWING:TRUE role=ROLE_MENU name='File' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE
+STATE-CHANGE:SHOWING:TRUE role=ROLE_MENU name='New' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE
+=== Start Continuation ===
+STATE-CHANGE:EXPANDED:FALSE role=ROLE_MENU_ITEM name='File' ENABLED,EXPANDABLE,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='File' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE
+STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='New' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE
+=== Start Continuation ===
+=== Start Continuation ===
+STATE-CHANGE:EXPANDED:TRUE role=ROLE_MENU_ITEM name='File' ENABLED,EXPANDABLE,EXPANDED,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:EXPANDED:TRUE role=ROLE_MENU_ITEM name='New' ENABLED,EXPANDABLE,EXPANDED,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:SHOWING:TRUE role=ROLE_MENU name='File' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE
+STATE-CHANGE:SHOWING:TRUE role=ROLE_MENU name='New' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE
+=== Start Continuation ===
+STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='File' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE
+STATE-CHANGE:SHOWING:FALSE role=ROLE_MENU name='New' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE
+=== Start Continuation ===
diff --git a/content/test/data/accessibility/event/menubar-show-hide-menus-expected-mac.txt b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-mac.txt
new file mode 100644
index 0000000..112ab9b1
--- /dev/null
+++ b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-mac.txt
@@ -0,0 +1,18 @@
+AXExpandedChanged on AXMenuItem AXDescription="File"
+AXExpandedChanged on AXMenuItem AXDescription="New"
+AXMenuOpened on AXMenu AXDescription="File"
+AXMenuOpened on AXMenu AXDescription="New"
+=== Start Continuation ===
+AXExpandedChanged on AXMenuItem AXDescription="File"
+AXMenuClosed on AXWebArea
+AXMenuClosed on AXWebArea
+=== Start Continuation ===
+=== Start Continuation ===
+AXExpandedChanged on AXMenuItem AXDescription="File"
+AXExpandedChanged on AXMenuItem AXDescription="New"
+AXMenuOpened on AXMenu AXDescription="File"
+AXMenuOpened on AXMenu AXDescription="New"
+=== Start Continuation ===
+AXMenuClosed on AXWebArea
+AXMenuClosed on AXWebArea
+=== Start Continuation ===
diff --git a/content/test/data/accessibility/event/menubar-show-hide-menus-expected-uia-win.txt b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-uia-win.txt
new file mode 100644
index 0000000..a7efa69b
--- /dev/null
+++ b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-uia-win.txt
@@ -0,0 +1,31 @@
+AriaProperties changed on role=menu, name=File
+AriaProperties changed on role=menuitem, name=File
+AriaProperties changed on role=menuitem, name=New
+ExpandCollapseExpandCollapseState changed on role=menuitem, name=File
+ExpandCollapseExpandCollapseState changed on role=menuitem, name=New
+MenuOpened on role=menu, name=File
+MenuOpened on role=menu, name=New
+=== Start Continuation ===
+AriaProperties changed on role=menuitem, name=File
+ExpandCollapseExpandCollapseState changed on role=menuitem, name=File
+MenuClosed 
+MenuClosed 
+MenuClosed 
+MenuClosed 
+=== Start Continuation ===
+=== Start Continuation ===
+AriaProperties changed on role=menu, name=File
+AriaProperties changed on role=menuitem, name=File
+AriaProperties changed on role=menuitem, name=New
+ExpandCollapseExpandCollapseState changed on role=menuitem, name=File
+ExpandCollapseExpandCollapseState changed on role=menuitem, name=New
+MenuOpened on role=menu, name=File
+MenuOpened on role=menu, name=New
+=== Start Continuation ===
+MenuClosed 
+MenuClosed 
+MenuClosed 
+MenuClosed 
+=== Start Continuation ===
+AriaProperties changed on role=menubar
+AriaProperties changed on role=menuitem, name=File
diff --git a/content/test/data/accessibility/event/menubar-show-hide-menus-expected-win.txt b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-win.txt
new file mode 100644
index 0000000..d797b55
--- /dev/null
+++ b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-win.txt
@@ -0,0 +1,16 @@
+EVENT_OBJECT_STATECHANGE on <li#file-menuitem> role=ROLE_SYSTEM_MENUITEM name="File" EXPANDED,HASPOPUP PosInSet=1 SetSize=2
+EVENT_SYSTEM_MENUPOPUPSTART on <ul#file-menu> role=ROLE_SYSTEM_MENUPOPUP name="File" IA2_STATE_VERTICAL SetSize=3
+EVENT_SYSTEM_MENUPOPUPSTART on <ul#new-menu> role=ROLE_SYSTEM_MENUPOPUP name="New" IA2_STATE_VERTICAL SetSize=3
+=== Start Continuation ===
+EVENT_OBJECT_STATECHANGE on <li#file-menuitem> role=ROLE_SYSTEM_MENUITEM name="File" COLLAPSED,HASPOPUP PosInSet=1 SetSize=2
+EVENT_SYSTEM_MENUPOPUPEND on <ul#file-menu> role=ROLE_SYSTEM_MENUPOPUP name="File" IA2_STATE_VERTICAL SetSize=3
+EVENT_SYSTEM_MENUPOPUPEND on <ul#new-menu> role=ROLE_SYSTEM_MENUPOPUP name="New" IA2_STATE_VERTICAL SetSize=3
+=== Start Continuation ===
+=== Start Continuation ===
+EVENT_OBJECT_STATECHANGE on <li#file-menuitem> role=ROLE_SYSTEM_MENUITEM name="File" EXPANDED,HASPOPUP PosInSet=1 SetSize=2
+EVENT_SYSTEM_MENUPOPUPSTART on <ul#file-menu> role=ROLE_SYSTEM_MENUPOPUP name="File" IA2_STATE_VERTICAL SetSize=3
+EVENT_SYSTEM_MENUPOPUPSTART on <ul#new-menu> role=ROLE_SYSTEM_MENUPOPUP name="New" IA2_STATE_VERTICAL SetSize=3
+=== Start Continuation ===
+EVENT_SYSTEM_MENUPOPUPEND on <ul#file-menu> role=ROLE_SYSTEM_MENUPOPUP name="File" IA2_STATE_VERTICAL SetSize=3
+EVENT_SYSTEM_MENUPOPUPEND on <ul#new-menu> role=ROLE_SYSTEM_MENUPOPUP name="New" IA2_STATE_VERTICAL SetSize=3
+=== Start Continuation ===
diff --git a/content/test/data/accessibility/event/menubar-show-hide-menus.html b/content/test/data/accessibility/event/menubar-show-hide-menus.html
new file mode 100644
index 0000000..049aea1
--- /dev/null
+++ b/content/test/data/accessibility/event/menubar-show-hide-menus.html
@@ -0,0 +1,113 @@
+<!--
+@AURALINUX-DENY:CHILDREN-CHANGED*
+@AURALINUX-DENY:PARENT-CHANGED*
+@AURALINUX-DENY:STATE-CHANGE:DEFUNCT*
+@WIN-DENY:EVENT_OBJECT_REORDER*
+@WIN-DENY:EVENT_OBJECT_SHOW*
+@WIN-DENY:EVENT_OBJECT_HIDE*
+@WIN-DENY:EVENT_OBJECT_LOCATIONCHANGE*
+@WIN-DENY:IA2_EVENT_TEXT_INSERTED*
+@WIN-DENY:IA2_EVENT_TEXT_REMOVED*
+@UIA-WIN-DENY:*StructureChanged*
+@UIA-WIN_DENY:AutomationFocusChanged*
+-->
+<!DOCTYPE html>
+<style>
+ul { list-style-type: none;}
+</style>
+<ul role="menubar" id="menubar">
+  <li role="menuitem" id="file-menuitem" aria-haspopup="true" aria-label="File">File
+    <ul role="menu" id="file-menu" aria-label="File">
+      <li role="menuitem" id="new-menuitem" aria-haspopup="true" aria-label="New">New
+	<ul role="menu" id="new-menu" aria-label="New">
+	  <li role="menuitem">Document</li>
+	  <li role="menuitem">Spreadsheet</li>
+	  <li role="menuitem">Presentation</li>
+	</ul>
+      </li>
+      <li role="menuitem">Open...</li>
+      <li role="menuitem">Quit</li>
+    </ul>
+  </li>
+  <li role="menuitem">About</li>
+</ul>
+<script>
+  fileMenu = document.getElementById("file-menu");
+  fileMenuItem = document.getElementById("file-menuitem");
+  newMenu = document.getElementById("new-menu");
+  newMenuItem = document.getElementById("new-menuitem");
+  menubar = document.getElementById("menubar")
+  setInitialState();
+
+  function setInitialState() {
+    newMenu.style.display = "none";
+    newMenuItem.setAttribute("aria-expanded", "false");
+    fileMenu.style.display = "none";
+    fileMenuItem.setAttribute("aria-expanded", "false");
+  }
+
+  function openFileMenu() {
+    fileMenu.style.display = "block";
+    fileMenuItem.setAttribute("aria-expanded", "true");
+  }
+
+  function closeFileMenu() {
+    fileMenu.style.display = "none";
+    fileMenuItem.setAttribute("aria-expanded", "false");
+  }
+
+  function openNewMenu() {
+    newMenu.style.display = "block";
+    newMenuItem.setAttribute("aria-expanded", "true");
+  }
+
+  function closeNewMenu() {
+    newMenu.style.display = "none";
+    newMenuItem.setAttribute("aria-expanded", "false");
+  }
+
+  function openFileAndNewMenus() {
+    openFileMenu();
+    openNewMenu();
+  }
+
+  function closeFileAndNewMenus() {
+    closeNewMenu();
+    closeFileMenu();
+  }
+
+  function hideMenubar() {
+    menubar.style.display = "none";
+  }
+
+  function showMenubar() {
+    setInitialState();
+    menubar.style.display = "block";
+  }
+
+  const go_passes = [
+    () => openFileAndNewMenus(),
+    () => closeFileAndNewMenus(),
+
+    // This should not generate any events because the parent container has
+    // display:none.
+    () => openNewMenu(),
+
+    // This should generate events for both menus, because functionally they
+    // are now both being displayed.
+    () => openFileMenu(),
+
+    // When the menubar is hidden, we want to be sure menus which fired events
+    // to notify they were showing also fire events notifying they were closed.
+    () => hideMenubar(),
+
+    // There should be no menu-related events here.
+    () => showMenubar(),
+  ];
+
+  var current_pass = 0;
+  function go() {
+    go_passes[current_pass++].call();
+    return current_pass < go_passes.length;
+  }
+</script>
diff --git a/content/web_test/browser/web_test_bluetooth_adapter_provider.cc b/content/web_test/browser/web_test_bluetooth_adapter_provider.cc
index 82c4d8ff..126e70c 100644
--- a/content/web_test/browser/web_test_bluetooth_adapter_provider.cc
+++ b/content/web_test/browser/web_test_bluetooth_adapter_provider.cc
@@ -707,9 +707,9 @@
   NiceMockBluetoothGattCharacteristic* measurement_ptr =
       measurement_interval.get();
 
-  ON_CALL(*measurement_interval, ReadRemoteCharacteristic_(_, _))
-      .WillByDefault(
-          RunCallback<0 /* success_callback */>(std::vector<uint8_t>({1})));
+  ON_CALL(*measurement_interval, ReadRemoteCharacteristic_(_))
+      .WillByDefault(RunCallbackWithResult<0>(/*error_code=*/base::nullopt,
+                                              std::vector<uint8_t>({1})));
 
   ON_CALL(*measurement_interval, WriteRemoteCharacteristic_(_, _, _, _))
       .WillByDefault(RunCallback<2 /* success_callback */>());
@@ -730,14 +730,13 @@
         BluetoothUUID(kUserDescriptionUUID),
         device::BluetoothRemoteGattCharacteristic::PROPERTY_READ);
 
-    ON_CALL(*user_description, ReadRemoteDescriptor_(_, _))
+    ON_CALL(*user_description, ReadRemoteDescriptor_(_))
         .WillByDefault(
             Invoke([descriptorName](
-                       BluetoothRemoteGattDescriptor::ValueCallback& callback,
-                       BluetoothRemoteGattDescriptor::ErrorCallback&) {
+                       BluetoothRemoteGattDescriptor::ValueCallback& callback) {
               std::vector<uint8_t> value(descriptorName.begin(),
                                          descriptorName.end());
-              std::move(callback).Run(value);
+              std::move(callback).Run(/*error_code=*/base::nullopt, value);
             }));
 
     ON_CALL(*user_description, WriteRemoteDescriptor_(_, _, _))
@@ -769,10 +768,9 @@
     // because this is used in web tests that may not report a mock
     // expectation
     // error correctly as a web test failure.
-    ON_CALL(*no_read_descriptor, ReadRemoteDescriptor_(_, _))
+    ON_CALL(*no_read_descriptor, ReadRemoteDescriptor_(_))
         .WillByDefault(
-            Invoke([](BluetoothRemoteGattDescriptor::ValueCallback&,
-                      BluetoothRemoteGattDescriptor::ErrorCallback&) {
+            Invoke([](BluetoothRemoteGattDescriptor::ValueCallback&) {
               NOTREACHED();
             }));
 
@@ -1016,20 +1014,20 @@
   NiceMockBluetoothGattCharacteristic* measurement_ptr =
       measurement_interval.get();
 
-  ON_CALL(*measurement_interval, ReadRemoteCharacteristic_(_, _))
-      .WillByDefault(
-          Invoke([adapter_ptr, device_ptr, disconnect, succeeds](
-                     BluetoothRemoteGattCharacteristic::ValueCallback& callback,
-                     BluetoothRemoteGattCharacteristic::ErrorCallback&
-                         error_callback) {
+  ON_CALL(*measurement_interval, ReadRemoteCharacteristic_(_))
+      .WillByDefault(Invoke(
+          [adapter_ptr, device_ptr, disconnect, succeeds](
+              BluetoothRemoteGattCharacteristic::ValueCallback& callback) {
             base::OnceClosure pending;
             if (succeeds) {
               pending = base::BindOnce(std::move(callback),
+                                       /*error_code=*/base::nullopt,
                                        std::vector<uint8_t>({1}));
             } else {
               pending =
-                  base::BindOnce(std::move(error_callback),
-                                 BluetoothRemoteGattService::GATT_ERROR_FAILED);
+                  base::BindOnce(std::move(callback),
+                                 BluetoothRemoteGattService::GATT_ERROR_FAILED,
+                                 /*value=*/std::vector<uint8_t>());
             }
             device_ptr->PushPendingCallback(std::move(pending));
             if (disconnect) {
@@ -1123,19 +1121,20 @@
       BluetoothUUID(kUserDescriptionUUID),
       device::BluetoothRemoteGattCharacteristic::PROPERTY_READ);
 
-  ON_CALL(*user_descriptor, ReadRemoteDescriptor_(_, _))
-      .WillByDefault(Invoke(
-          [adapter_ptr, device_ptr, disconnect, succeeds](
-              BluetoothRemoteGattDescriptor::ValueCallback& callback,
-              BluetoothRemoteGattDescriptor::ErrorCallback& error_callback) {
+  ON_CALL(*user_descriptor, ReadRemoteDescriptor_(_))
+      .WillByDefault(
+          Invoke([adapter_ptr, device_ptr, disconnect, succeeds](
+                     BluetoothRemoteGattDescriptor::ValueCallback& callback) {
             base::OnceClosure pending;
             if (succeeds) {
               pending = base::BindOnce(std::move(callback),
+                                       /*error_code=*/base::nullopt,
                                        std::vector<uint8_t>({1}));
             } else {
               pending =
-                  base::BindOnce(std::move(error_callback),
-                                 BluetoothRemoteGattService::GATT_ERROR_FAILED);
+                  base::BindOnce(std::move(callback),
+                                 BluetoothRemoteGattService::GATT_ERROR_FAILED,
+                                 /*value=*/std::vector<uint8_t>());
             }
             device_ptr->PushPendingCallback(std::move(pending));
             if (disconnect) {
@@ -1463,11 +1462,9 @@
   // Crash if ReadRemoteCharacteristic called. Not using GoogleMock's Expect
   // because this is used in web tests that may not report a mock expectation
   // error correctly as a web test failure.
-  ON_CALL(*blocklist_exclude_reads_characteristic,
-          ReadRemoteCharacteristic_(_, _))
+  ON_CALL(*blocklist_exclude_reads_characteristic, ReadRemoteCharacteristic_(_))
       .WillByDefault(
-          Invoke([](BluetoothRemoteGattCharacteristic::ValueCallback&,
-                    BluetoothRemoteGattCharacteristic::ErrorCallback&) {
+          Invoke([](BluetoothRemoteGattCharacteristic::ValueCallback&) {
             NOTREACHED();
           }));
 
@@ -1501,10 +1498,9 @@
   // Crash if ReadRemoteCharacteristic called. Not using GoogleMock's Expect
   // because this is used in web tests that may not report a mock expectation
   // error correctly as a web test failure.
-  ON_CALL(*serial_number_string, ReadRemoteCharacteristic_(_, _))
+  ON_CALL(*serial_number_string, ReadRemoteCharacteristic_(_))
       .WillByDefault(
-          Invoke([](BluetoothRemoteGattCharacteristic::ValueCallback&,
-                    BluetoothRemoteGattCharacteristic::ErrorCallback&) {
+          Invoke([](BluetoothRemoteGattCharacteristic::ValueCallback&) {
             NOTREACHED();
           }));
 
@@ -1530,8 +1526,9 @@
     std::vector<uint8_t> device_name_value;
     if (base::Optional<std::string> name = device->GetName())
       device_name_value.assign(name.value().begin(), name.value().end());
-    ON_CALL(*device_name, ReadRemoteCharacteristic_(_, _))
-        .WillByDefault(RunCallback<0>(device_name_value));
+    ON_CALL(*device_name, ReadRemoteCharacteristic_(_))
+        .WillByDefault(RunCallbackWithResult<0>(/*error_code=*/base::nullopt,
+                                                device_name_value));
 
     // Write response.
     ON_CALL(*device_name, WriteRemoteCharacteristic_(_, _, _, _))
@@ -1556,8 +1553,9 @@
     std::vector<uint8_t> value(1);
     value[0] = false;
 
-    ON_CALL(*peripheral_privacy_flag, ReadRemoteCharacteristic_(_, _))
-        .WillByDefault(RunCallback<0>(value));
+    ON_CALL(*peripheral_privacy_flag, ReadRemoteCharacteristic_(_))
+        .WillByDefault(
+            RunCallbackWithResult<0>(/*error_code=*/base::nullopt, value));
 
     // Crash if WriteRemoteCharacteristic called. Not using GoogleMock's Expect
     // because this is used in web tests that may not report a mock
@@ -1623,9 +1621,9 @@
           "Body Sensor Location Chest", heart_rate.get(), kBodySensorLocation,
           BluetoothRemoteGattCharacteristic::PROPERTY_READ));
 
-  ON_CALL(*body_sensor_location_chest, ReadRemoteCharacteristic_(_, _))
-      .WillByDefault(RunCallback<0 /* success_callback */>(
-          std::vector<uint8_t>({1} /* Chest */)));
+  ON_CALL(*body_sensor_location_chest, ReadRemoteCharacteristic_(_))
+      .WillByDefault(RunCallbackWithResult<0>(
+          /*error_code=*/base::nullopt, std::vector<uint8_t>({1} /* Chest */)));
 
   // Body Sensor Location Characteristic (Wrist)
   std::unique_ptr<NiceMockBluetoothGattCharacteristic>
@@ -1633,9 +1631,9 @@
           "Body Sensor Location Wrist", heart_rate.get(), kBodySensorLocation,
           BluetoothRemoteGattCharacteristic::PROPERTY_READ));
 
-  ON_CALL(*body_sensor_location_wrist, ReadRemoteCharacteristic_(_, _))
-      .WillByDefault(RunCallback<0 /* success_callback */>(
-          std::vector<uint8_t>({2} /* Wrist */)));
+  ON_CALL(*body_sensor_location_wrist, ReadRemoteCharacteristic_(_))
+      .WillByDefault(RunCallbackWithResult<0>(
+          /*error_code=*/base::nullopt, std::vector<uint8_t>({2} /* Wrist */)));
 
   heart_rate->AddMockCharacteristic(std::move(heart_rate_measurement));
   heart_rate->AddMockCharacteristic(std::move(body_sensor_location_chest));
@@ -1701,9 +1699,10 @@
       service, identifier, BluetoothUUID(uuid), properties,
       BluetoothGattCharacteristic::Permission::PERMISSION_NONE);
 
-  ON_CALL(*characteristic, ReadRemoteCharacteristic_(_, _))
-      .WillByDefault(
-          RunCallback<1>(BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED));
+  ON_CALL(*characteristic, ReadRemoteCharacteristic_(_))
+      .WillByDefault(RunCallbackWithResult<0>(
+          BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED,
+          /*value=*/std::vector<uint8_t>()));
 
   ON_CALL(*characteristic, WriteRemoteCharacteristic_(_, _, _, _))
       .WillByDefault(
@@ -1735,8 +1734,10 @@
           BluetoothRemoteGattCharacteristic::PROPERTY_INDICATE));
 
   // Read response.
-  ON_CALL(*characteristic, ReadRemoteCharacteristic_(_, _))
-      .WillByDefault(RunCallback<1 /* error_callback */>(error_code));
+  ON_CALL(*characteristic, ReadRemoteCharacteristic_(_))
+      .WillByDefault(
+          RunCallbackWithResult<0>(error_code,
+                                   /*value=*/std::vector<uint8_t>()));
 
   // Write response.
   ON_CALL(*characteristic, WriteRemoteCharacteristic_(_, _, _, _))
@@ -1756,8 +1757,10 @@
       BluetoothUUID(kUserDescriptionUUID),
       device::BluetoothRemoteGattCharacteristic::PROPERTY_READ);
 
-  ON_CALL(*error_descriptor, ReadRemoteDescriptor_(_, _))
-      .WillByDefault(RunCallback<1 /* error_callback */>(error_code));
+  ON_CALL(*error_descriptor, ReadRemoteDescriptor_(_))
+      .WillByDefault(
+          RunCallbackWithResult<0>(error_code,
+                                   /*value=*/std::vector<uint8_t>()));
 
   ON_CALL(*error_descriptor, WriteRemoteDescriptor_(_, _, _))
       .WillByDefault(RunCallback<2 /* error_callback */>(error_code));
diff --git a/device/BUILD.gn b/device/BUILD.gn
index 1255fe6..1197c3eb 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -252,6 +252,7 @@
       "bluetooth/bluez/bluetooth_service_record_bluez_unittest.cc",
       "bluetooth/bluez/bluetooth_socket_bluez_unittest.cc",
       "bluetooth/dbus/bluetooth_gatt_application_service_provider_unittest.cc",
+      "bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_unittest.cc",
       "bluetooth/test/bluetooth_test_bluez.cc",
       "bluetooth/test/bluetooth_test_bluez.h",
     ]
diff --git a/device/bluetooth/bluetooth_adapter_mac_metrics_unittest.mm b/device/bluetooth/bluetooth_adapter_mac_metrics_unittest.mm
index 974e312..1bb09dda 100644
--- a/device/bluetooth/bluetooth_adapter_mac_metrics_unittest.mm
+++ b/device/bluetooth/bluetooth_adapter_mac_metrics_unittest.mm
@@ -141,8 +141,8 @@
   EXPECT_EQ(1u, characteristic_->GetDescriptors().size());
   BluetoothRemoteGattDescriptor* descriptor =
       characteristic_->GetDescriptors()[0];
-  descriptor->ReadRemoteDescriptor(GetReadValueCallback(Call::NOT_EXPECTED),
-                                   GetGattErrorCallback(Call::EXPECTED));
+  descriptor->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
   SimulateGattDescriptorUpdateError(
       descriptor, BluetoothRemoteGattService::GATT_ERROR_FAILED);
   base::RunLoop().RunUntilIdle();
diff --git a/device/bluetooth/bluetooth_local_gatt_characteristic_unittest.cc b/device/bluetooth/bluetooth_local_gatt_characteristic_unittest.cc
index 78deac8..f9916bd01 100644
--- a/device/bluetooth/bluetooth_local_gatt_characteristic_unittest.cc
+++ b/device/bluetooth/bluetooth_local_gatt_characteristic_unittest.cc
@@ -63,8 +63,8 @@
        MAYBE_ReadLocalCharacteristicValue) {
   delegate_->value_to_write_ = 0x1337;
   SimulateLocalGattCharacteristicValueReadRequest(
-      device_, read_characteristic_.get(), GetReadValueCallback(Call::EXPECTED),
-      GetCallback(Call::NOT_EXPECTED));
+      device_, read_characteristic_.get(),
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
 
   EXPECT_EQ(delegate_->value_to_write_, GetInteger(last_read_value_));
   EXPECT_EQ(device_->GetIdentifier(), delegate_->last_seen_device_);
@@ -129,7 +129,7 @@
   delegate_->should_fail_ = true;
   SimulateLocalGattCharacteristicValueReadRequest(
       device_, read_characteristic_.get(),
-      GetReadValueCallback(Call::NOT_EXPECTED), GetCallback(Call::EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
 
   EXPECT_NE(delegate_->value_to_write_, GetInteger(last_read_value_));
   EXPECT_NE(device_->GetIdentifier(), delegate_->last_seen_device_);
@@ -147,7 +147,7 @@
   delegate_->value_to_write_ = 0x1337;
   SimulateLocalGattCharacteristicValueReadRequest(
       device_, write_characteristic_.get(),
-      GetReadValueCallback(Call::NOT_EXPECTED), GetCallback(Call::EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
 
   EXPECT_NE(delegate_->value_to_write_, GetInteger(last_read_value_));
   EXPECT_NE(device_->GetIdentifier(), delegate_->last_seen_device_);
diff --git a/device/bluetooth/bluetooth_local_gatt_descriptor_unittest.cc b/device/bluetooth/bluetooth_local_gatt_descriptor_unittest.cc
index 2d1c02f..8273229e 100644
--- a/device/bluetooth/bluetooth_local_gatt_descriptor_unittest.cc
+++ b/device/bluetooth/bluetooth_local_gatt_descriptor_unittest.cc
@@ -54,8 +54,8 @@
 TEST_F(BluetoothLocalGattDescriptorTest, MAYBE_ReadLocalDescriptorValue) {
   delegate_->value_to_write_ = 0x1337;
   SimulateLocalGattDescriptorValueReadRequest(
-      device_, read_descriptor_.get(), GetReadValueCallback(Call::EXPECTED),
-      GetCallback(Call::NOT_EXPECTED));
+      device_, read_descriptor_.get(),
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
 
   EXPECT_EQ(delegate_->value_to_write_, GetInteger(last_read_value_));
   EXPECT_EQ(device_->GetIdentifier(), delegate_->last_seen_device_);
@@ -85,8 +85,8 @@
   delegate_->value_to_write_ = 0x1337;
   delegate_->should_fail_ = true;
   SimulateLocalGattDescriptorValueReadRequest(
-      device_, read_descriptor_.get(), GetReadValueCallback(Call::NOT_EXPECTED),
-      GetCallback(Call::EXPECTED));
+      device_, read_descriptor_.get(),
+      GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
 
   EXPECT_NE(delegate_->value_to_write_, GetInteger(last_read_value_));
   EXPECT_NE(device_->GetIdentifier(), delegate_->last_seen_device_);
@@ -121,7 +121,7 @@
   delegate_->value_to_write_ = 0x1337;
   SimulateLocalGattDescriptorValueReadRequest(
       device_, write_descriptor_.get(),
-      GetReadValueCallback(Call::NOT_EXPECTED), GetCallback(Call::EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
 
   EXPECT_NE(delegate_->value_to_write_, GetInteger(last_read_value_));
   EXPECT_NE(device_->GetIdentifier(), delegate_->last_seen_device_);
diff --git a/device/bluetooth/bluetooth_local_gatt_service.h b/device/bluetooth/bluetooth_local_gatt_service.h
index 24eb3b1..251c92c3 100644
--- a/device/bluetooth/bluetooth_local_gatt_service.h
+++ b/device/bluetooth/bluetooth_local_gatt_service.h
@@ -12,6 +12,7 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_export.h"
 #include "device/bluetooth/bluetooth_gatt_characteristic.h"
@@ -45,28 +46,28 @@
   class Delegate {
    public:
     // Callbacks used for communicating GATT request responses.
-    using ValueCallback = base::OnceCallback<void(const std::vector<uint8_t>&)>;
+    using ValueCallback = base::OnceCallback<void(
+        base::Optional<BluetoothGattService::GattErrorCode> error_code,
+        const std::vector<uint8_t>&)>;
     using ErrorCallback = base::OnceClosure;
 
     // Called when a remote device |device| requests to read the value of the
-    // characteristic |characteristic| starting at offset |offset|.
-    // This method is only called if the characteristic was specified as
-    // readable and any authentication and authorization challenges were
-    // satisfied by the remote device.
+    // characteristic |characteristic| starting at offset |offset|. To respond
+    // to the request with failure (e.g. if an invalid offset was given),
+    // delegates must invoke |callback| with the appropriate error code. If
+    // |callback| is not invoked, the request will time out resulting in an
+    // error. Therefore, delegates MUST invoke |callback| regardless of success
+    // or failure.
     //
     // To respond to the request with success and return the requested value,
-    // the delegate must invoke |callback| with the value. Doing so will
-    // automatically update the value property of |characteristic|. To respond
-    // to the request with failure (e.g. if an invalid offset was given),
-    // delegates must invoke |error_callback|. If neither callback parameter is
-    // invoked, the request will time out and result in an error. Therefore,
-    // delegates MUST invoke either |callback| or |error_callback|.
+    // the delegate must invoke |callback| with the value (and without an error
+    // code). Doing so will automatically update the value property of
+    // |characteristic|.
     virtual void OnCharacteristicReadRequest(
         const BluetoothDevice* device,
         const BluetoothLocalGattCharacteristic* characteristic,
         int offset,
-        ValueCallback callback,
-        ErrorCallback error_callback) = 0;
+        ValueCallback callback) = 0;
 
     // Called when a remote device |device| requests to write the value of the
     // characteristic |characteristic| starting at offset |offset|.
@@ -113,24 +114,22 @@
         ErrorCallback error_callback) = 0;
 
     // Called when a remote device |device| requests to read the value of the
-    // descriptor |descriptor| starting at offset |offset|.
-    // This method is only called if the descriptor was specified as
-    // readable and any authentication and authorization challenges were
-    // satisfied by the remote device.
+    // descriptor |descriptor| starting at offset |offset|. To respond
+    // to the request with failure (e.g. if an invalid offset was given),
+    // delegates must invoke |callback| with the appropriate error code. If
+    // |callback| is not invoked, the request will time out resulting in an
+    // error. Therefore, delegates MUST invoke |callback| regardless of success
+    // or failure.
     //
     // To respond to the request with success and return the requested value,
-    // the delegate must invoke |callback| with the value. Doing so will
-    // automatically update the value property of |descriptor|. To respond
-    // to the request with failure (e.g. if an invalid offset was given),
-    // delegates must invoke |error_callback|. If neither callback parameter is
-    // invoked, the request will time out and result in an error. Therefore,
-    // delegates MUST invoke either |callback| or |error_callback|.
+    // the delegate must invoke |callback| with the value (and without an error
+    // code). Doing so will automatically update the value property of
+    // |descriptor|.
     virtual void OnDescriptorReadRequest(
         const BluetoothDevice* device,
         const BluetoothLocalGattDescriptor* descriptor,
         int offset,
-        ValueCallback callback,
-        ErrorCallback error_callback) = 0;
+        ValueCallback callback) = 0;
 
     // Called when a remote device |devie| requests to write the value of the
     // descriptor |descriptor| starting at offset |offset|.
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic.h b/device/bluetooth/bluetooth_remote_gatt_characteristic.h
index 873f950..64a3aed 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic.h
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic.h
@@ -51,8 +51,12 @@
   };
 
   // The ValueCallback is used to return the value of a remote characteristic
-  // upon a read request.
-  using ValueCallback = base::OnceCallback<void(const std::vector<uint8_t>&)>;
+  // upon a read request. Upon successful completion |error_code| will not
+  // have a value and |value| may be used. When unsuccessful |error_code| will
+  // have a value and |value| must be ignored.
+  using ValueCallback = base::OnceCallback<void(
+      base::Optional<BluetoothGattService::GattErrorCode> error_code,
+      const std::vector<uint8_t>& value)>;
 
   // The NotifySessionCallback is used to return sessions after they have
   // been successfully started.
@@ -139,10 +143,8 @@
 #endif
 
   // Sends a read request to a remote characteristic to read its value.
-  // |callback| is called to return the read value on success and
-  // |error_callback| is called for failures.
-  virtual void ReadRemoteCharacteristic(ValueCallback callback,
-                                        ErrorCallback error_callback) = 0;
+  // |callback| is called to return the read value or error.
+  virtual void ReadRemoteCharacteristic(ValueCallback callback) = 0;
 
   // Sends a write request to a remote characteristic with the value |value|
   // using the specified |write_type|. |callback| is called to signal success
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc
index 4fd63137..50ca90d9 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc
@@ -55,10 +55,10 @@
     ~BluetoothRemoteGattCharacteristicAndroid() {
   Java_ChromeBluetoothRemoteGattCharacteristic_onBluetoothRemoteGattCharacteristicAndroidDestruction(
       AttachCurrentThread(), j_characteristic_);
-  if (!read_callback_.is_null()) {
-    DCHECK(!read_error_callback_.is_null());
-    std::move(read_error_callback_)
-        .Run(BluetoothGattService::GATT_ERROR_FAILED);
+  if (read_callback_) {
+    std::move(read_callback_)
+        .Run(BluetoothGattService::GATT_ERROR_FAILED,
+             /*value=*/std::vector<uint8_t>());
   }
 
   if (!write_callback_.is_null()) {
@@ -126,28 +126,27 @@
 }
 
 void BluetoothRemoteGattCharacteristicAndroid::ReadRemoteCharacteristic(
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   if (read_pending_ || write_pending_) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::BindOnce(std::move(error_callback),
-                       BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS));
+        base::BindOnce(std::move(callback),
+                       BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS,
+                       /*value=*/std::vector<uint8_t>()));
     return;
   }
 
   if (!Java_ChromeBluetoothRemoteGattCharacteristic_readRemoteCharacteristic(
           AttachCurrentThread(), j_characteristic_)) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(error_callback),
-                       BluetoothRemoteGattService::GATT_ERROR_FAILED));
+        FROM_HERE, base::BindOnce(std::move(callback),
+                                  BluetoothRemoteGattService::GATT_ERROR_FAILED,
+                                  /*value=*/std::vector<uint8_t>()));
     return;
   }
 
   read_pending_ = true;
   read_callback_ = std::move(callback);
-  read_error_callback_ = std::move(error_callback);
 }
 
 void BluetoothRemoteGattCharacteristicAndroid::WriteRemoteCharacteristic(
@@ -234,15 +233,16 @@
 
   // Clear callbacks before calling to avoid reentrancy issues.
   ValueCallback read_callback = std::move(read_callback_);
-  ErrorCallback read_error_callback = std::move(read_error_callback_);
+  if (!read_callback)
+    return;
 
-  if (status == 0  // android.bluetooth.BluetoothGatt.GATT_SUCCESS
-      && !read_callback.is_null()) {
+  if (status == 0) {  // android.bluetooth.BluetoothGatt.GATT_SUCCESS
     base::android::JavaByteArrayToByteVector(env, value, &value_);
-    std::move(read_callback).Run(value_);
-  } else if (!read_error_callback.is_null()) {
-    std::move(read_error_callback)
-        .Run(BluetoothRemoteGattServiceAndroid::GetGattErrorCode(status));
+    std::move(read_callback).Run(/*error_code=*/base::nullopt, value_);
+  } else {
+    std::move(read_callback)
+        .Run(BluetoothRemoteGattServiceAndroid::GetGattErrorCode(status),
+             /*value=*/std::vector<uint8_t>());
   }
 }
 
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h b/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h
index 7710e77..89f5514f 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h
@@ -63,8 +63,7 @@
       const std::string& identifier) const override;
   std::vector<BluetoothRemoteGattDescriptor*> GetDescriptorsByUUID(
       const BluetoothUUID& uuid) const override;
-  void ReadRemoteCharacteristic(ValueCallback callback,
-                                ErrorCallback error_callback) override;
+  void ReadRemoteCharacteristic(ValueCallback callback) override;
   void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
                                  WriteType write_type,
                                  base::OnceClosure callback,
@@ -142,10 +141,9 @@
   // Adapter unique instance ID.
   std::string instance_id_;
 
-  // ReadRemoteCharacteristic callbacks and pending state.
+  // ReadRemoteCharacteristic callback and pending state.
   bool read_pending_ = false;
   ValueCallback read_callback_;
-  ErrorCallback read_error_callback_;
 
   // WriteRemoteCharacteristic callbacks and pending state.
   bool write_pending_ = false;
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h b/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h
index 71510e2..60164dd 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h
@@ -41,8 +41,7 @@
   const std::vector<uint8_t>& GetValue() const override;
   BluetoothRemoteGattService* GetService() const override;
   bool IsNotifying() const override;
-  void ReadRemoteCharacteristic(ValueCallback callback,
-                                ErrorCallback error_callback) override;
+  void ReadRemoteCharacteristic(ValueCallback callback) override;
   void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
                                  WriteType write_type,
                                  base::OnceClosure callback,
@@ -102,7 +101,7 @@
   BluetoothRemoteGattDescriptorMac* GetBluetoothRemoteGattDescriptorMac(
       CBDescriptor* cb_descriptor) const;
   bool HasPendingRead() const {
-    return !read_characteristic_value_callbacks_.first.is_null();
+    return !read_characteristic_value_callback_.is_null();
   }
   bool HasPendingWrite() const {
     return !write_characteristic_value_callbacks_.first.is_null();
@@ -123,8 +122,11 @@
   BluetoothUUID uuid_;
   // Characteristic value.
   std::vector<uint8_t> value_;
-  // ReadRemoteCharacteristic request callbacks.
-  std::pair<ValueCallback, ErrorCallback> read_characteristic_value_callbacks_;
+  // The destructor runs callbacks. Methods can use |destructor_called_| to
+  // protect against reentrant calls to a partially deleted instance.
+  bool destructor_called_ = false;
+  // ReadRemoteCharacteristic request callback.
+  ValueCallback read_characteristic_value_callback_;
   // WriteRemoteCharacteristic request callbacks.
   std::pair<base::OnceClosure, ErrorCallback>
       write_characteristic_value_callbacks_;
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm b/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm
index 8cf91ed..276afb6 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm
@@ -84,9 +84,11 @@
 }
 
 BluetoothRemoteGattCharacteristicMac::~BluetoothRemoteGattCharacteristicMac() {
+  destructor_called_ = true;
   if (HasPendingRead()) {
-    std::move(read_characteristic_value_callbacks_.second)
-        .Run(BluetoothGattService::GATT_ERROR_FAILED);
+    std::move(read_characteristic_value_callback_)
+        .Run(BluetoothGattService::GATT_ERROR_FAILED,
+             /*value=*/std::vector<uint8_t>());
   }
   if (HasPendingWrite()) {
     std::move(write_characteristic_value_callbacks_.second)
@@ -129,27 +131,27 @@
 }
 
 void BluetoothRemoteGattCharacteristicMac::ReadRemoteCharacteristic(
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   if (!IsReadable()) {
     DVLOG(1) << *this << ": Characteristic not readable.";
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::BindOnce(std::move(error_callback),
-                       BluetoothRemoteGattService::GATT_ERROR_NOT_PERMITTED));
+        base::BindOnce(std::move(callback),
+                       BluetoothRemoteGattService::GATT_ERROR_NOT_PERMITTED,
+                       /*value=*/std::vector<uint8_t>()));
     return;
   }
-  if (HasPendingRead() || HasPendingWrite()) {
+  if (destructor_called_ || HasPendingRead() || HasPendingWrite()) {
     DVLOG(1) << *this << ": Characteristic read already in progress.";
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::BindOnce(std::move(error_callback),
-                       BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS));
+        base::BindOnce(std::move(callback),
+                       BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS,
+                       /*value=*/std::vector<uint8_t>()));
     return;
   }
   DVLOG(1) << *this << ": Read characteristic.";
-  read_characteristic_value_callbacks_ =
-      std::make_pair(std::move(callback), std::move(error_callback));
+  read_characteristic_value_callback_ = std::move(callback);
   [GetCBPeripheral() readValueForCharacteristic:cb_characteristic_];
 }
 
@@ -158,7 +160,7 @@
     WriteType write_type,
     base::OnceClosure callback,
     ErrorCallback error_callback) {
-  if (HasPendingRead() || HasPendingWrite()) {
+  if (destructor_called_ || HasPendingRead() || HasPendingWrite()) {
     DVLOG(1) << *this << ": Characteristic write already in progress.";
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
@@ -205,7 +207,7 @@
                        BluetoothRemoteGattService::GATT_ERROR_NOT_PERMITTED));
     return;
   }
-  if (HasPendingRead() || HasPendingWrite()) {
+  if (destructor_called_ || HasPendingRead() || HasPendingWrite()) {
     DVLOG(1) << *this << ": Characteristic write already in progress.";
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
@@ -271,8 +273,8 @@
   // notification is received.
   RecordDidUpdateValueResult(error);
   if (HasPendingRead()) {
-    std::pair<ValueCallback, ErrorCallback> callbacks;
-    callbacks.swap(read_characteristic_value_callbacks_);
+    ValueCallback read_callback =
+        std::move(read_characteristic_value_callback_);
     if (error) {
       BluetoothGattService::GattErrorCode error_code =
           BluetoothDeviceMac::GetGattErrorCodeFromNSError(error);
@@ -280,12 +282,14 @@
                << ": Bluetooth error while reading for characteristic, domain: "
                << BluetoothAdapterMac::String(error)
                << ", error code: " << error_code;
-      std::move(callbacks.second).Run(error_code);
+      std::move(read_callback)
+          .Run(error_code,
+               /*value=*/std::vector<uint8_t>());
       return;
     }
     DVLOG(1) << *this << ": Read request arrived.";
     UpdateValue();
-    std::move(callbacks.first).Run(value_);
+    std::move(read_callback).Run(/*error_code=*/base::nullopt, value_);
   } else if (IsNotifying()) {
     DVLOG(1) << *this << ": Notification arrived.";
     UpdateValue();
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
index f0cc2fb..8c7e4b91 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
@@ -384,8 +384,7 @@
       BluetoothRemoteGattCharacteristic::PROPERTY_READ));
 
   characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::EXPECTED),
-      GetGattErrorCallback(Call::NOT_EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
   std::vector<uint8_t> empty_vector;
   SimulateGattCharacteristicRead(characteristic1_, empty_vector);
   base::RunLoop().RunUntilIdle();
@@ -499,23 +498,20 @@
   ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate(
       BluetoothRemoteGattCharacteristic::PROPERTY_READ));
 
-  bool read_error_callback_called = false;
-  characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::NOT_EXPECTED),
-      base::BindLambdaForTesting(
-          [&](BluetoothRemoteGattService::GattErrorCode error_code) {
-            EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED,
-                      error_code);
-            read_error_callback_called = true;
-            // Retrying Read should fail:
-            characteristic1_->ReadRemoteCharacteristic(
-                GetReadValueCallback(Call::NOT_EXPECTED),
-                GetGattErrorCallback(Call::EXPECTED));
-          }));
+  bool read_characteristic_failed = false;
+  characteristic1_->ReadRemoteCharacteristic(base::BindLambdaForTesting(
+      [&](base::Optional<BluetoothRemoteGattService::GattErrorCode> error_code,
+          const std::vector<uint8_t>&) {
+        EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED, error_code);
+        read_characteristic_failed = true;
+        // Retrying Read should fail:
+        characteristic1_->ReadRemoteCharacteristic(
+            GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
+      }));
 
   DeleteDevice(device_);  // TODO(576906) delete only the characteristic.
   base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(read_error_callback_called);
+  EXPECT_TRUE(read_characteristic_failed);
   EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS,
             last_gatt_error_code_);
 }
@@ -641,8 +637,7 @@
       BluetoothRemoteGattCharacteristic::PROPERTY_READ));
 
   characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::NOT_EXPECTED),
-      GetGattErrorCallback(Call::EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
 
   RememberCharacteristicForSubsequentAction(characteristic1_);
   DeleteDevice(device_);  // TODO(576906) delete only the characteristic.
@@ -678,8 +673,7 @@
       BluetoothRemoteGattCharacteristic::PROPERTY_READ));
 
   characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::NOT_EXPECTED),
-      GetGattErrorCallback(Call::EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
 
 // Set up for receiving a read response after disconnection.
 // On macOS or WinRT no events arrive after disconnection so there is no point
@@ -919,8 +913,7 @@
       BluetoothRemoteGattCharacteristic::PROPERTY_READ));
 
   characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::EXPECTED),
-      GetGattErrorCallback(Call::NOT_EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
 
   std::vector<uint8_t> test_vector = {0, 1, 2, 3, 4, 0xf, 0xf0, 0xff};
   SimulateGattCharacteristicRead(characteristic1_, test_vector);
@@ -941,9 +934,10 @@
 static void TestCallback(
     BluetoothRemoteGattCharacteristic::ValueCallback callback,
     const TestBluetoothAdapterObserver& callback_observer,
+    base::Optional<BluetoothRemoteGattService::GattErrorCode> error_code,
     const std::vector<uint8_t>& value) {
   EXPECT_EQ(0, callback_observer.gatt_characteristic_value_changed_count());
-  std::move(callback).Run(value);
+  std::move(callback).Run(error_code, value);
 }
 
 #if defined(OS_ANDROID) || defined(OS_MAC)
@@ -971,10 +965,9 @@
 
   TestBluetoothAdapterObserver observer(adapter_);
 
-  characteristic1_->ReadRemoteCharacteristic(
-      base::BindOnce(TestCallback, GetReadValueCallback(Call::EXPECTED),
-                     std::cref(observer)),
-      GetGattErrorCallback(Call::NOT_EXPECTED));
+  characteristic1_->ReadRemoteCharacteristic(base::BindOnce(
+      TestCallback, GetReadValueCallback(Call::EXPECTED, Result::SUCCESS),
+      std::cref(observer)));
 
   std::vector<uint8_t> test_vector = {0, 1, 2, 3, 4, 0xf, 0xf0, 0xff};
   SimulateGattCharacteristicRead(characteristic1_, test_vector);
@@ -1099,8 +1092,7 @@
       BluetoothRemoteGattCharacteristic::PROPERTY_READ));
 
   characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::EXPECTED),
-      GetGattErrorCallback(Call::NOT_EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
 
   uint8_t values[] = {0, 1, 2, 3, 4, 0xf, 0xf0, 0xff};
   std::vector<uint8_t> test_vector(values, values + base::size(values));
@@ -1115,8 +1107,7 @@
   // Read again, with different value:
   ResetEventCounts();
   characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::EXPECTED),
-      GetGattErrorCallback(Call::NOT_EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
   std::vector<uint8_t> empty_vector;
   SimulateGattCharacteristicRead(characteristic1_, empty_vector);
   base::RunLoop().RunUntilIdle();
@@ -1259,11 +1250,9 @@
       BluetoothRemoteGattCharacteristic::PROPERTY_READ));
 
   characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::EXPECTED),
-      GetGattErrorCallback(Call::NOT_EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
   characteristic2_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::EXPECTED),
-      GetGattErrorCallback(Call::NOT_EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
 
@@ -1437,9 +1426,11 @@
   std::vector<uint8_t> test_vector_1 = {0, 1, 2, 3, 4};
   std::vector<uint8_t> test_vector_2 = {0xf, 0xf0, 0xff};
 
-  characteristic1_->ReadRemoteCharacteristic(
-      base::BindLambdaForTesting([&](const std::vector<uint8_t>& data) {
-        GetReadValueCallback(Call::EXPECTED).Run(data);
+  characteristic1_->ReadRemoteCharacteristic(base::BindLambdaForTesting(
+      [&](base::Optional<BluetoothRemoteGattService::GattErrorCode> error_code,
+          const std::vector<uint8_t>& data) {
+        GetReadValueCallback(Call::EXPECTED, Result::SUCCESS)
+            .Run(error_code, data);
 
         EXPECT_EQ(1, gatt_read_characteristic_attempts_);
         EXPECT_EQ(1, callback_count_);
@@ -1448,11 +1439,9 @@
         EXPECT_EQ(test_vector_1, characteristic1_->GetValue());
 
         characteristic1_->ReadRemoteCharacteristic(
-            GetReadValueCallback(Call::EXPECTED),
-            GetGattErrorCallback(Call::NOT_EXPECTED));
+            GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
         SimulateGattCharacteristicRead(characteristic1_, test_vector_2);
-      }),
-      GetGattErrorCallback(Call::NOT_EXPECTED));
+      }));
 
   SimulateGattCharacteristicRead(characteristic1_, test_vector_1);
   base::RunLoop().RunUntilIdle();
@@ -1599,8 +1588,11 @@
   std::vector<uint8_t> test_vector_1 = {0, 1, 2, 3, 4};
   std::vector<uint8_t> test_vector_2 = {0xf, 0xf0, 0xff};
 
-  characteristic1_->ReadRemoteCharacteristic(
-      base::BindLambdaForTesting([&](const std::vector<uint8_t>& data) {
+  characteristic1_->ReadRemoteCharacteristic(base::BindLambdaForTesting(
+      [&](base::Optional<BluetoothRemoteGattService::GattErrorCode> error_code,
+          const std::vector<uint8_t>& data) {
+        ASSERT_FALSE(error_code.has_value())
+            << "unexpected error: " << error_code.value();
         EXPECT_EQ(1, gatt_read_characteristic_attempts_);
         EXPECT_EQ(0, gatt_write_characteristic_attempts_);
         EXPECT_EQ(test_vector_1, data);
@@ -1621,12 +1613,7 @@
                 }));
 
         SimulateGattCharacteristicWrite(characteristic1_);
-      }),
-      base::BindLambdaForTesting(
-          [&](BluetoothGattService::GattErrorCode error_code) {
-            ADD_FAILURE() << "unexpected error: " << error_code;
-            loop.Quit();
-          }));
+      }));
 
   SimulateGattCharacteristicRead(characteristic1_, test_vector_1);
   loop.Run();
@@ -1659,9 +1646,11 @@
   std::vector<uint8_t> test_vector_1 = {0, 1, 2, 3, 4};
   std::vector<uint8_t> test_vector_2 = {0xf, 0xf0, 0xff};
 
-  characteristic1_->ReadRemoteCharacteristic(
-      base::BindLambdaForTesting([&](const std::vector<uint8_t>& data) {
-        GetReadValueCallback(Call::EXPECTED).Run(data);
+  characteristic1_->ReadRemoteCharacteristic(base::BindLambdaForTesting(
+      [&](base::Optional<BluetoothRemoteGattService::GattErrorCode> error_code,
+          const std::vector<uint8_t>& data) {
+        GetReadValueCallback(Call::EXPECTED, Result::SUCCESS)
+            .Run(error_code, data);
 
         EXPECT_EQ(1, gatt_read_characteristic_attempts_);
         EXPECT_EQ(0, gatt_write_characteristic_attempts_);
@@ -1674,8 +1663,7 @@
             test_vector_2, GetCallback(Call::EXPECTED),
             GetGattErrorCallback(Call::NOT_EXPECTED));
         SimulateGattCharacteristicWrite(characteristic1_);
-      }),
-      GetGattErrorCallback(Call::NOT_EXPECTED));
+      }));
 
   SimulateGattCharacteristicRead(characteristic1_, test_vector_1);
   base::RunLoop().RunUntilIdle();
@@ -1720,19 +1708,17 @@
         EXPECT_EQ(1, gatt_write_characteristic_attempts_);
         EXPECT_EQ(test_vector_1, last_write_value_);
 
-        characteristic1_->ReadRemoteCharacteristic(
-            base::BindLambdaForTesting([&](const std::vector<uint8_t>& data) {
+        characteristic1_->ReadRemoteCharacteristic(base::BindLambdaForTesting(
+            [&](base::Optional<BluetoothRemoteGattService::GattErrorCode>
+                    error_code,
+                const std::vector<uint8_t>& data) {
+              EXPECT_EQ(error_code, base::nullopt);
               EXPECT_EQ(1, gatt_read_characteristic_attempts_);
               EXPECT_EQ(1, gatt_write_characteristic_attempts_);
               EXPECT_EQ(test_vector_2, data);
               EXPECT_EQ(test_vector_2, characteristic1_->GetValue());
               loop.Quit();
-            }),
-            base::BindLambdaForTesting(
-                [&](BluetoothGattService::GattErrorCode error_code) {
-                  ADD_FAILURE() << "unexpected error: " << error_code;
-                  loop.Quit();
-                }));
+            }));
         SimulateGattCharacteristicRead(characteristic1_, test_vector_2);
       }),
       base::BindLambdaForTesting(
@@ -1783,8 +1769,7 @@
         EXPECT_EQ(test_vector_1, last_write_value_);
 
         characteristic1_->ReadRemoteCharacteristic(
-            GetReadValueCallback(Call::EXPECTED),
-            GetGattErrorCallback(Call::NOT_EXPECTED));
+            GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
         SimulateGattCharacteristicRead(characteristic1_, test_vector_2);
       }),
       GetGattErrorCallback(Call::NOT_EXPECTED));
@@ -1820,8 +1805,7 @@
   TestBluetoothAdapterObserver observer(adapter_);
 
   characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::NOT_EXPECTED),
-      GetGattErrorCallback(Call::EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
   SimulateGattCharacteristicReadError(
       characteristic1_, BluetoothRemoteGattService::GATT_ERROR_INVALID_LENGTH);
   SimulateGattCharacteristicReadError(
@@ -1916,8 +1900,7 @@
 
   SimulateGattCharacteristicReadWillFailSynchronouslyOnce(characteristic1_);
   characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::NOT_EXPECTED),
-      GetGattErrorCallback(Call::EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
   EXPECT_EQ(0, gatt_read_characteristic_attempts_);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0, callback_count_);
@@ -1928,8 +1911,7 @@
   // After failing once, can succeed:
   ResetEventCounts();
   characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::EXPECTED),
-      GetGattErrorCallback(Call::NOT_EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
   EXPECT_EQ(1, gatt_read_characteristic_attempts_);
   std::vector<uint8_t> empty_vector;
   SimulateGattCharacteristicRead(characteristic1_, empty_vector);
@@ -2042,11 +2024,9 @@
       BluetoothRemoteGattCharacteristic::PROPERTY_READ));
 
   characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::EXPECTED),
-      GetGattErrorCallback(Call::NOT_EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
   characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::NOT_EXPECTED),
-      GetGattErrorCallback(Call::EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
 
   base::RunLoop().RunUntilIdle();
 
@@ -2202,17 +2182,13 @@
             ADD_FAILURE() << "unexpected error: " << error_code;
             loop1.Quit();
           }));
-  characteristic1_->ReadRemoteCharacteristic(
-      base::BindLambdaForTesting([&](const std::vector<uint8_t>& data) {
-        ADD_FAILURE() << "unexpected success";
+  characteristic1_->ReadRemoteCharacteristic(base::BindLambdaForTesting(
+      [&](base::Optional<BluetoothRemoteGattService::GattErrorCode> error_code,
+          const std::vector<uint8_t>& data) {
+        EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS,
+                  error_code);
         loop2.Quit();
-      }),
-      base::BindLambdaForTesting(
-          [&](BluetoothGattService::GattErrorCode error_code) {
-            EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS,
-                      error_code);
-            loop2.Quit();
-          }));
+      }));
 
   loop2.Run();
 
@@ -2250,8 +2226,7 @@
       empty_vector, GetCallback(Call::EXPECTED),
       GetGattErrorCallback(Call::NOT_EXPECTED));
   characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::NOT_EXPECTED),
-      GetGattErrorCallback(Call::EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
 
   base::RunLoop().RunUntilIdle();
 
@@ -2294,16 +2269,12 @@
   base::RunLoop loop1;
   base::RunLoop loop2;
   std::vector<uint8_t> empty_vector;
-  characteristic1_->ReadRemoteCharacteristic(
-      base::BindLambdaForTesting([&](const std::vector<uint8_t>& data) {
-        SUCCEED();
+  characteristic1_->ReadRemoteCharacteristic(base::BindLambdaForTesting(
+      [&](base::Optional<BluetoothRemoteGattService::GattErrorCode> error_code,
+          const std::vector<uint8_t>& data) {
+        EXPECT_FALSE(error_code.has_value()) << "unexpected failure";
         loop1.Quit();
-      }),
-      base::BindLambdaForTesting(
-          [&](BluetoothGattService::GattErrorCode error_code) {
-            ADD_FAILURE() << "unexpected error: " << error_code;
-            loop1.Quit();
-          }));
+      }));
   characteristic1_->WriteRemoteCharacteristic(
       empty_vector, WriteType::kWithResponse, base::BindLambdaForTesting([&] {
         ADD_FAILURE() << "unexpected success";
@@ -2349,8 +2320,7 @@
 
   std::vector<uint8_t> empty_vector;
   characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::EXPECTED),
-      GetGattErrorCallback(Call::NOT_EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
   characteristic1_->DeprecatedWriteRemoteCharacteristic(
       empty_vector, GetCallback(Call::NOT_EXPECTED),
       GetGattErrorCallback(Call::EXPECTED));
@@ -2399,8 +2369,7 @@
   TestBluetoothAdapterObserver observer(adapter_);
 
   characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::EXPECTED),
-      GetGattErrorCallback(Call::NOT_EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
 
   std::vector<uint8_t> notification_value = {111};
   SimulateGattCharacteristicChanged(characteristic1_, notification_value);
@@ -4178,8 +4147,7 @@
   SimulateGattDisconnection(device_);
   // Do not yet call RunUntilIdle() to process the disconnect task.
   characteristic1_->ReadRemoteCharacteristic(
-      GetReadValueCallback(Call::NOT_EXPECTED),
-      GetGattErrorCallback(Call::EXPECTED));
+      GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
 
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED,
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_win.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic_win.cc
index 3572d0f..4527cd8 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_win.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_win.cc
@@ -56,10 +56,10 @@
   }
   parent_service_->GetWinAdapter()->NotifyGattCharacteristicRemoved(this);
 
-  if (!read_characteristic_value_callbacks_.first.is_null()) {
-    DCHECK(!read_characteristic_value_callbacks_.second.is_null());
-    std::move(read_characteristic_value_callbacks_.second)
-        .Run(BluetoothRemoteGattService::GATT_ERROR_FAILED);
+  if (read_characteristic_value_callback_) {
+    std::move(read_characteristic_value_callback_)
+        .Run(BluetoothRemoteGattService::GATT_ERROR_FAILED,
+             /*value=*/std::vector<uint8_t>());
   }
 
   if (!write_characteristic_value_callbacks_.first.is_null()) {
@@ -131,25 +131,24 @@
 }
 
 void BluetoothRemoteGattCharacteristicWin::ReadRemoteCharacteristic(
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
 
   if (!characteristic_info_.get()->IsReadable) {
-    std::move(error_callback)
-        .Run(BluetoothRemoteGattService::GATT_ERROR_NOT_PERMITTED);
+    std::move(callback).Run(
+        BluetoothRemoteGattService::GATT_ERROR_NOT_PERMITTED,
+        std::vector<uint8_t>());
     return;
   }
 
   if (characteristic_value_read_or_write_in_progress_) {
-    std::move(error_callback)
-        .Run(BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS);
+    std::move(callback).Run(BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS,
+                            std::vector<uint8_t>());
     return;
   }
 
   characteristic_value_read_or_write_in_progress_ = true;
-  read_characteristic_value_callbacks_ =
-      std::make_pair(std::move(callback), std::move(error_callback));
+  read_characteristic_value_callback_ = std::move(callback);
   task_manager_->PostReadGattCharacteristicValue(
       parent_service_->GetServicePath(), characteristic_info_.get(),
       base::BindOnce(&BluetoothRemoteGattCharacteristicWin::
@@ -363,16 +362,15 @@
   DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
   characteristic_value_read_or_write_in_progress_ = false;
 
-  std::pair<ValueCallback, ErrorCallback> callbacks;
-  callbacks.swap(read_characteristic_value_callbacks_);
+  ValueCallback callback = std::move(read_characteristic_value_callback_);
   if (FAILED(hr)) {
-    std::move(callbacks.second).Run(HRESULTToGattErrorCode(hr));
+    std::move(callback).Run(HRESULTToGattErrorCode(hr), std::vector<uint8_t>());
   } else {
     characteristic_value_.clear();
     for (ULONG i = 0; i < value->DataSize; i++)
       characteristic_value_.push_back(value->Data[i]);
 
-    std::move(callbacks.first).Run(characteristic_value_);
+    std::move(callback).Run(base::nullopt, characteristic_value_);
   }
 }
 
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_win.h b/device/bluetooth/bluetooth_remote_gatt_characteristic_win.h
index 50fcaac8..e14b1384 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_win.h
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_win.h
@@ -42,8 +42,7 @@
   Properties GetProperties() const override;
   Permissions GetPermissions() const override;
   bool IsNotifying() const override;
-  void ReadRemoteCharacteristic(ValueCallback callback,
-                                ErrorCallback error_callback) override;
+  void ReadRemoteCharacteristic(ValueCallback callback) override;
   void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
                                  WriteType write_type,
                                  base::OnceClosure callback,
@@ -113,8 +112,8 @@
   // has been sent out to avoid duplicate notification.
   bool characteristic_added_notified_;
 
-  // ReadRemoteCharacteristic request callbacks.
-  std::pair<ValueCallback, ErrorCallback> read_characteristic_value_callbacks_;
+  // ReadRemoteCharacteristic request callback.
+  ValueCallback read_characteristic_value_callback_;
 
   // WriteRemoteCharacteristic request callbacks.
   std::pair<base::OnceClosure, ErrorCallback>
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc
index 17cfeb2..0c84b10 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc
@@ -107,9 +107,11 @@
 
 BluetoothRemoteGattCharacteristicWinrt::
     ~BluetoothRemoteGattCharacteristicWinrt() {
-  if (pending_read_callbacks_) {
-    std::move(pending_read_callbacks_->error_callback)
-        .Run(BluetoothGattService::GATT_ERROR_FAILED);
+  destructor_called_ = true;
+  if (pending_read_callback_) {
+    std::move(pending_read_callback_)
+        .Run(BluetoothGattService::GATT_ERROR_FAILED,
+             /*value=*/std::vector<uint8_t>());
   }
 
   if (pending_write_callbacks_) {
@@ -151,21 +153,23 @@
 }
 
 void BluetoothRemoteGattCharacteristicWinrt::ReadRemoteCharacteristic(
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   if (!(GetProperties() & PROPERTY_READ)) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::BindOnce(std::move(error_callback),
-                       BluetoothRemoteGattService::GATT_ERROR_NOT_PERMITTED));
+        base::BindOnce(std::move(callback),
+                       BluetoothRemoteGattService::GATT_ERROR_NOT_PERMITTED,
+                       /*value=*/std::vector<uint8_t>()));
     return;
   }
 
-  if (pending_read_callbacks_ || pending_write_callbacks_) {
+  if (destructor_called_ || pending_read_callback_ ||
+      pending_write_callbacks_) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::BindOnce(std::move(error_callback),
-                       BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS));
+        base::BindOnce(std::move(callback),
+                       BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS,
+                       /*value=*/std::vector<uint8_t>()));
     return;
   }
 
@@ -177,9 +181,9 @@
         << "GattCharacteristic::ReadValueWithCacheModeAsync failed: "
         << logging::SystemErrorCodeToString(hr);
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(error_callback),
-                       BluetoothRemoteGattService::GATT_ERROR_FAILED));
+        FROM_HERE, base::BindOnce(std::move(callback),
+                                  BluetoothRemoteGattService::GATT_ERROR_FAILED,
+                                  /*value=*/std::vector<uint8_t>()));
     return;
   }
 
@@ -192,14 +196,13 @@
     BLUETOOTH_LOG(DEBUG) << "PostAsyncResults failed: "
                          << logging::SystemErrorCodeToString(hr);
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(error_callback),
-                       BluetoothRemoteGattService::GATT_ERROR_FAILED));
+        FROM_HERE, base::BindOnce(std::move(callback),
+                                  BluetoothRemoteGattService::GATT_ERROR_FAILED,
+                                  /*value=*/std::vector<uint8_t>()));
     return;
   }
 
-  pending_read_callbacks_ = std::make_unique<PendingReadCallbacks>(
-      std::move(callback), std::move(error_callback));
+  pending_read_callback_ = std::move(callback);
 }
 
 void BluetoothRemoteGattCharacteristicWinrt::WriteRemoteCharacteristic(
@@ -207,7 +210,8 @@
     WriteType write_type,
     base::OnceClosure callback,
     ErrorCallback error_callback) {
-  if (pending_read_callbacks_ || pending_write_callbacks_) {
+  if (destructor_called_ || pending_read_callback_ ||
+      pending_write_callbacks_) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
         base::BindOnce(std::move(error_callback),
@@ -389,13 +393,6 @@
       std::move(success_callback), std::move(split_error_callback.second));
 }
 
-BluetoothRemoteGattCharacteristicWinrt::PendingReadCallbacks::
-    PendingReadCallbacks(ValueCallback callback, ErrorCallback error_callback)
-    : callback(std::move(callback)),
-      error_callback(std::move(error_callback)) {}
-BluetoothRemoteGattCharacteristicWinrt::PendingReadCallbacks::
-    ~PendingReadCallbacks() = default;
-
 BluetoothRemoteGattCharacteristicWinrt::PendingWriteCallbacks::
     PendingWriteCallbacks(base::OnceClosure callback,
                           ErrorCallback error_callback)
@@ -477,12 +474,13 @@
 
 void BluetoothRemoteGattCharacteristicWinrt::OnReadValue(
     ComPtr<IGattReadResult> read_result) {
-  DCHECK(pending_read_callbacks_);
-  auto pending_read_callbacks = std::move(pending_read_callbacks_);
+  DCHECK(pending_read_callback_);
+  auto pending_read_callback = std::move(pending_read_callback_);
 
   if (!read_result) {
-    std::move(pending_read_callbacks->error_callback)
-        .Run(BluetoothGattService::GATT_ERROR_FAILED);
+    std::move(pending_read_callback)
+        .Run(BluetoothRemoteGattService::GATT_ERROR_FAILED,
+             /*value=*/std::vector<uint8_t>());
     return;
   }
 
@@ -491,8 +489,9 @@
   if (FAILED(hr)) {
     BLUETOOTH_LOG(DEBUG) << "Getting GATT Communication Status failed: "
                          << logging::SystemErrorCodeToString(hr);
-    std::move(pending_read_callbacks->error_callback)
-        .Run(BluetoothGattService::GATT_ERROR_FAILED);
+    std::move(pending_read_callback)
+        .Run(BluetoothRemoteGattService::GATT_ERROR_FAILED,
+             /*value=*/std::vector<uint8_t>());
     return;
   }
 
@@ -503,14 +502,16 @@
     if (FAILED(hr)) {
       BLUETOOTH_LOG(DEBUG) << "As IGattReadResult2 failed: "
                            << logging::SystemErrorCodeToString(hr);
-      std::move(pending_read_callbacks->error_callback)
-          .Run(BluetoothGattService::GATT_ERROR_FAILED);
+      std::move(pending_read_callback)
+          .Run(BluetoothRemoteGattService::GATT_ERROR_FAILED,
+               /*value=*/std::vector<uint8_t>());
       return;
     }
 
-    std::move(pending_read_callbacks->error_callback)
+    std::move(pending_read_callback)
         .Run(BluetoothRemoteGattServiceWinrt::GetGattErrorCode(
-            read_result_2.Get()));
+                 read_result_2.Get()),
+             /*value=*/std::vector<uint8_t>());
     return;
   }
 
@@ -519,8 +520,9 @@
   if (FAILED(hr)) {
     BLUETOOTH_LOG(DEBUG) << "Getting Characteristic Value failed: "
                          << logging::SystemErrorCodeToString(hr);
-    std::move(pending_read_callbacks->error_callback)
-        .Run(BluetoothGattService::GATT_ERROR_FAILED);
+    std::move(pending_read_callback)
+        .Run(BluetoothRemoteGattService::GATT_ERROR_FAILED,
+             /*value=*/std::vector<uint8_t>());
     return;
   }
 
@@ -530,13 +532,14 @@
   if (FAILED(hr)) {
     BLUETOOTH_LOG(DEBUG) << "Getting Pointer To Buffer Data failed: "
                          << logging::SystemErrorCodeToString(hr);
-    std::move(pending_read_callbacks->error_callback)
-        .Run(BluetoothGattService::GATT_ERROR_FAILED);
+    std::move(pending_read_callback)
+        .Run(BluetoothRemoteGattService::GATT_ERROR_FAILED,
+             /*value=*/std::vector<uint8_t>());
     return;
   }
 
   value_.assign(data, data + length);
-  std::move(pending_read_callbacks->callback).Run(value_);
+  std::move(pending_read_callback).Run(/*error_code=*/base::nullopt, value_);
 }
 
 void BluetoothRemoteGattCharacteristicWinrt::OnWriteValueWithResultAndOption(
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h b/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h
index 1402fa2..4d9e9e7 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h
@@ -45,8 +45,7 @@
   // BluetoothRemoteGattCharacteristic:
   const std::vector<uint8_t>& GetValue() const override;
   BluetoothRemoteGattService* GetService() const override;
-  void ReadRemoteCharacteristic(ValueCallback callback,
-                                ErrorCallback error_callback) override;
+  void ReadRemoteCharacteristic(ValueCallback callback) override;
   void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
                                  WriteType write_type,
                                  base::OnceClosure callback,
@@ -73,14 +72,6 @@
       ErrorCallback error_callback) override;
 
  private:
-  struct PendingReadCallbacks {
-    PendingReadCallbacks(ValueCallback callback, ErrorCallback error_callback);
-    ~PendingReadCallbacks();
-
-    ValueCallback callback;
-    ErrorCallback error_callback;
-  };
-
   struct PendingWriteCallbacks {
     PendingWriteCallbacks(base::OnceClosure callback,
                           ErrorCallback error_callback);
@@ -144,10 +135,13 @@
   uint16_t attribute_handle_;
   std::string identifier_;
   std::vector<uint8_t> value_;
-  std::unique_ptr<PendingReadCallbacks> pending_read_callbacks_;
+  ValueCallback pending_read_callback_;
   std::unique_ptr<PendingWriteCallbacks> pending_write_callbacks_;
   std::unique_ptr<PendingNotificationCallbacks> pending_notification_callbacks_;
   base::Optional<EventRegistrationToken> value_changed_token_;
+  // The destructor runs callbacks. Methods can use |destructor_called_| to
+  // protect against reentrant calls to a partially deleted instance.
+  bool destructor_called_ = false;
 
   base::WeakPtrFactory<BluetoothRemoteGattCharacteristicWinrt>
       weak_ptr_factory_{this};
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor.h b/device/bluetooth/bluetooth_remote_gatt_descriptor.h
index 25f0d7f..2c52d99 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor.h
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor.h
@@ -33,7 +33,13 @@
 
   // The ValueCallback is used to return the value of a remote characteristic
   // descriptor upon a read request.
-  using ValueCallback = base::OnceCallback<void(const std::vector<uint8_t>&)>;
+  //
+  // This callback is called on both success and failure. On error |error_code|
+  // will contain a value and |value| should be ignored. When successful
+  // |error_code| will have no value and |value| may be used.
+  using ValueCallback = base::OnceCallback<void(
+      base::Optional<BluetoothGattService::GattErrorCode> error_code,
+      const std::vector<uint8_t>& value)>;
 
   // Returns the value of the descriptor. For remote descriptors, this is the
   // most recently cached value of the remote descriptor. For local descriptors
@@ -46,10 +52,8 @@
   virtual BluetoothRemoteGattCharacteristic* GetCharacteristic() const = 0;
 
   // Sends a read request to a remote characteristic descriptor to read its
-  // value. |callback| is called to return the read value on success and
-  // |error_callback| is called for failures.
-  virtual void ReadRemoteDescriptor(ValueCallback callback,
-                                    ErrorCallback error_callback) = 0;
+  // value. |callback| is called to return the read value on success or error.
+  virtual void ReadRemoteDescriptor(ValueCallback callback) = 0;
 
   // Sends a write request to a remote characteristic descriptor, to modify the
   // value of the descriptor with the new value |new_value|. |callback| is
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_android.cc b/device/bluetooth/bluetooth_remote_gatt_descriptor_android.cc
index ecf9188..12fc03f 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_android.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_android.cc
@@ -80,28 +80,27 @@
 }
 
 void BluetoothRemoteGattDescriptorAndroid::ReadRemoteDescriptor(
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   if (read_pending_ || write_pending_) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::BindOnce(std::move(error_callback),
-                       BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS));
+        base::BindOnce(std::move(callback),
+                       BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS,
+                       /*value=*/std::vector<uint8_t>()));
     return;
   }
 
   if (!Java_ChromeBluetoothRemoteGattDescriptor_readRemoteDescriptor(
           AttachCurrentThread(), j_descriptor_)) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(error_callback),
-                       BluetoothRemoteGattServiceAndroid::GATT_ERROR_FAILED));
+        FROM_HERE, base::BindOnce(std::move(callback),
+                                  BluetoothRemoteGattService::GATT_ERROR_FAILED,
+                                  /*value=*/std::vector<uint8_t>()));
     return;
   }
 
   read_pending_ = true;
   read_callback_ = std::move(callback);
-  read_error_callback_ = std::move(error_callback);
 }
 
 void BluetoothRemoteGattDescriptorAndroid::WriteRemoteDescriptor(
@@ -140,16 +139,17 @@
 
   // Clear callbacks before calling to avoid reentrancy issues.
   ValueCallback read_callback = std::move(read_callback_);
-  ErrorCallback read_error_callback = std::move(read_error_callback_);
+  if (!read_callback)
+    return;
 
-  if (status == 0  // android.bluetooth.BluetoothGatt.GATT_SUCCESS
-      && !read_callback.is_null()) {
+  if (status == 0) {  // android.bluetooth.BluetoothGatt.GATT_SUCCESS
     base::android::JavaByteArrayToByteVector(env, value, &value_);
-    std::move(read_callback).Run(value_);
+    std::move(read_callback).Run(/*error_code=*/base::nullopt, value_);
     // TODO(https://crbug.com/584369): Call GattDescriptorValueChanged.
-  } else if (!read_error_callback.is_null()) {
-    std::move(read_error_callback)
-        .Run(BluetoothRemoteGattServiceAndroid::GetGattErrorCode(status));
+  } else {
+    std::move(read_callback)
+        .Run(BluetoothRemoteGattServiceAndroid::GetGattErrorCode(status),
+             /*value=*/std::vector<uint8_t>());
   }
 }
 
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_android.h b/device/bluetooth/bluetooth_remote_gatt_descriptor_android.h
index b1e0672..de7d019 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_android.h
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_android.h
@@ -47,8 +47,7 @@
   BluetoothRemoteGattCharacteristic* GetCharacteristic() const override;
   BluetoothRemoteGattCharacteristic::Permissions GetPermissions()
       const override;
-  void ReadRemoteDescriptor(ValueCallback callback,
-                            ErrorCallback error_callback) override;
+  void ReadRemoteDescriptor(ValueCallback callback) override;
   void WriteRemoteDescriptor(const std::vector<uint8_t>& value,
                              base::OnceClosure callback,
                              ErrorCallback error_callback) override;
@@ -77,7 +76,6 @@
   // ReadRemoteCharacteristic callbacks and pending state.
   bool read_pending_ = false;
   ValueCallback read_callback_;
-  ErrorCallback read_error_callback_;
 
   // WriteRemoteCharacteristic callbacks and pending state.
   bool write_pending_ = false;
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.h b/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.h
index 7bb1a999..9131f0c 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.h
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.h
@@ -37,8 +37,7 @@
   // BluetoothRemoteGattDescriptor
   const std::vector<uint8_t>& GetValue() const override;
   BluetoothRemoteGattCharacteristic* GetCharacteristic() const override;
-  void ReadRemoteDescriptor(ValueCallback callback,
-                            ErrorCallback error_callback) override;
+  void ReadRemoteDescriptor(ValueCallback callback) override;
   void WriteRemoteDescriptor(const std::vector<uint8_t>& new_value,
                              base::OnceClosure callback,
                              ErrorCallback error_callback) override;
@@ -54,6 +53,10 @@
   // Calls callbacks, when -[id<CBPeripheralDelegate>
   // peripheral:didWriteValueForDescriptor:error:] is called.
   void DidWriteValueForDescriptor(NSError* error);
+  bool HasPendingRead() const { return !read_value_callback_.is_null(); }
+  bool HasPendingWrite() const {
+    return !write_value_callbacks_.first.is_null();
+  }
 
   // Returns CoreBluetooth peripheral.
   CBPeripheral* GetCBPeripheral() const;
@@ -69,10 +72,11 @@
   BluetoothUUID uuid_;
   // Descriptor value.
   std::vector<uint8_t> value_;
-  // True if a gatt read or write request is in progress.
-  bool value_read_or_write_in_progress_;
-  // ReadRemoteDescriptor request callbacks.
-  std::pair<ValueCallback, ErrorCallback> read_value_callbacks_;
+  // The destructor runs callbacks. Methods can use |destructor_called_| to
+  // protect against reentrant calls to a partially deleted instance.
+  bool destructor_called_ = false;
+  // ReadRemoteDescriptor request callback.
+  ValueCallback read_value_callback_;
   // WriteRemoteDescriptor request callbacks.
   std::pair<base::OnceClosure, ErrorCallback> write_value_callbacks_;
 };
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.mm b/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.mm
index 03e5c93a..7ae6566 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.mm
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.mm
@@ -47,8 +47,7 @@
     BluetoothRemoteGattCharacteristicMac* characteristic,
     CBDescriptor* descriptor)
     : gatt_characteristic_(characteristic),
-      cb_descriptor_(descriptor, base::scoped_policy::RETAIN),
-      value_read_or_write_in_progress_(false) {
+      cb_descriptor_(descriptor, base::scoped_policy::RETAIN) {
   uuid_ = BluetoothAdapterMac::BluetoothUUIDWithCBUUID([cb_descriptor_ UUID]);
   identifier_ = base::SysNSStringToUTF8(
       [NSString stringWithFormat:@"%s-%p", uuid_.canonical_value().c_str(),
@@ -74,11 +73,13 @@
 }
 
 BluetoothRemoteGattDescriptorMac::~BluetoothRemoteGattDescriptorMac() {
-  if (!read_value_callbacks_.first.is_null()) {
-    std::move(read_value_callbacks_)
-        .second.Run(BluetoothGattService::GATT_ERROR_FAILED);
+  destructor_called_ = true;
+  if (HasPendingRead()) {
+    std::move(read_value_callback_)
+        .Run(BluetoothGattService::GATT_ERROR_FAILED,
+             /*value=*/std::vector<uint8_t>());
   }
-  if (!write_value_callbacks_.first.is_null()) {
+  if (HasPendingWrite()) {
     std::move(write_value_callbacks_)
         .second.Run(BluetoothGattService::GATT_ERROR_FAILED);
   }
@@ -93,20 +94,18 @@
 // value. |callback| is called to return the read value on success and
 // |error_callback| is called for failures.
 void BluetoothRemoteGattDescriptorMac::ReadRemoteDescriptor(
-    ValueCallback callback,
-    ErrorCallback error_callback) {
-  if (value_read_or_write_in_progress_) {
+    ValueCallback callback) {
+  if (destructor_called_ || HasPendingRead() || HasPendingWrite()) {
     DVLOG(1) << *this << ": Read failed, already in progress.";
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::BindOnce(std::move(error_callback),
-                       BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS));
+        base::BindOnce(std::move(callback),
+                       BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS,
+                       /*value=*/std::vector<uint8_t>()));
     return;
   }
   DVLOG(1) << *this << ": Read value.";
-  value_read_or_write_in_progress_ = true;
-  read_value_callbacks_ =
-      std::make_pair(std::move(callback), std::move(error_callback));
+  read_value_callback_ = std::move(callback);
   [GetCBPeripheral() readValueForDescriptor:cb_descriptor_];
 }
 
@@ -114,7 +113,7 @@
     const std::vector<uint8_t>& value,
     base::OnceClosure callback,
     ErrorCallback error_callback) {
-  if (value_read_or_write_in_progress_) {
+  if (destructor_called_ || HasPendingRead() || HasPendingWrite()) {
     DVLOG(1) << *this << ": Write failed, already in progress.";
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
@@ -123,7 +122,6 @@
     return;
   }
   DVLOG(1) << *this << ": Write value.";
-  value_read_or_write_in_progress_ = true;
   write_value_callbacks_ =
       std::make_pair(std::move(callback), std::move(error_callback));
   base::scoped_nsobject<NSData> nsdata_value(
@@ -133,13 +131,10 @@
 
 void BluetoothRemoteGattDescriptorMac::DidUpdateValueForDescriptor(
     NSError* error) {
-  if (!value_read_or_write_in_progress_) {
+  if (!HasPendingRead()) {
     DVLOG(1) << *this << ": Value updated, no read in progress.";
     return;
   }
-  std::pair<ValueCallback, ErrorCallback> callbacks;
-  callbacks.swap(read_value_callbacks_);
-  value_read_or_write_in_progress_ = false;
   RecordDidUpdateValueForDescriptorResult(error);
   if (error) {
     BluetoothGattService::GattErrorCode error_code =
@@ -147,23 +142,24 @@
     DVLOG(1) << *this << ": Read value failed with error: "
              << BluetoothAdapterMac::String(error)
              << ", converted to error code: " << error_code;
-    std::move(callbacks.second).Run(error_code);
+    std::move(read_value_callback_)
+        .Run(error_code,
+             /*value=*/std::vector<uint8_t>());
     return;
   }
   DVLOG(1) << *this << ": Value read.";
   value_ = VectorValueFromObjC([cb_descriptor_ value]);
-  std::move(callbacks.first).Run(value_);
+  std::move(read_value_callback_).Run(/*error_code=*/base::nullopt, value_);
 }
 
 void BluetoothRemoteGattDescriptorMac::DidWriteValueForDescriptor(
     NSError* error) {
-  if (!value_read_or_write_in_progress_) {
+  if (!HasPendingWrite()) {
     DVLOG(1) << *this << ": Value written, no write in progress.";
     return;
   }
   std::pair<base::OnceClosure, ErrorCallback> callbacks;
   callbacks.swap(write_value_callbacks_);
-  value_read_or_write_in_progress_ = false;
   RecordDidWriteValueForDescriptorResult(error);
   if (error) {
     BluetoothGattService::GattErrorCode error_code =
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc b/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
index 8b281d5b..6ea06e2 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
@@ -240,8 +240,8 @@
   }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
-  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
-                                     GetGattErrorCallback(Call::NOT_EXPECTED));
+  descriptor1_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
   EXPECT_EQ(1, gatt_read_descriptor_attempts_);
   std::vector<uint8_t> empty_vector;
   SimulateGattDescriptorRead(descriptor1_, empty_vector);
@@ -305,8 +305,8 @@
   }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
-  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::NOT_EXPECTED),
-                                     GetGattErrorCallback(Call::NOT_EXPECTED));
+  descriptor1_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::NOT_EXPECTED, Result::FAILURE));
 
   RememberDescriptorForSubsequentAction(descriptor1_);
   DeleteDevice(device_);  // TODO(576906) delete only the descriptor.
@@ -366,8 +366,8 @@
   }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
-  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
-                                     GetGattErrorCallback(Call::NOT_EXPECTED));
+  descriptor1_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
   EXPECT_EQ(1, gatt_read_descriptor_attempts_);
 
   uint8_t values[] = {0, 1, 2, 3, 4, 0xf, 0xf0, 0xff};
@@ -430,8 +430,8 @@
   }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
-  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
-                                     GetGattErrorCallback(Call::NOT_EXPECTED));
+  descriptor1_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
   EXPECT_EQ(1, gatt_read_descriptor_attempts_);
 
   uint8_t values[] = {0, 1, 2, 3, 4, 0xf, 0xf0, 0xff};
@@ -445,8 +445,8 @@
 
   // Read again, with different value:
   ResetEventCounts();
-  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
-                                     GetGattErrorCallback(Call::NOT_EXPECTED));
+  descriptor1_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
   EXPECT_EQ(1, gatt_read_descriptor_attempts_);
   std::vector<uint8_t> empty_vector;
   SimulateGattDescriptorRead(descriptor1_, empty_vector);
@@ -521,10 +521,10 @@
   }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
-  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
-                                     GetGattErrorCallback(Call::NOT_EXPECTED));
-  descriptor2_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
-                                     GetGattErrorCallback(Call::NOT_EXPECTED));
+  descriptor1_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
+  descriptor2_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
   EXPECT_EQ(2, gatt_read_descriptor_attempts_);
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -609,8 +609,8 @@
   }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
-  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::NOT_EXPECTED),
-                                     GetGattErrorCallback(Call::EXPECTED));
+  descriptor1_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
   SimulateGattDescriptorReadError(
       descriptor1_, BluetoothRemoteGattService::GATT_ERROR_INVALID_LENGTH);
   SimulateGattDescriptorReadError(
@@ -667,8 +667,8 @@
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   SimulateGattDescriptorReadWillFailSynchronouslyOnce(descriptor1_);
-  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::NOT_EXPECTED),
-                                     GetGattErrorCallback(Call::EXPECTED));
+  descriptor1_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
   EXPECT_EQ(0, gatt_read_descriptor_attempts_);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0, callback_count_);
@@ -678,8 +678,8 @@
 
   // After failing once, can succeed:
   ResetEventCounts();
-  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
-                                     GetGattErrorCallback(Call::NOT_EXPECTED));
+  descriptor1_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
   EXPECT_EQ(1, gatt_read_descriptor_attempts_);
   std::vector<uint8_t> empty_vector;
   SimulateGattDescriptorRead(descriptor1_, empty_vector);
@@ -746,10 +746,10 @@
   }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
-  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
-                                     GetGattErrorCallback(Call::NOT_EXPECTED));
-  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::NOT_EXPECTED),
-                                     GetGattErrorCallback(Call::EXPECTED));
+  descriptor1_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
+  descriptor1_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
 
   base::RunLoop().RunUntilIdle();
 
@@ -834,8 +834,8 @@
   std::vector<uint8_t> empty_vector;
   descriptor1_->WriteRemoteDescriptor(empty_vector, GetCallback(Call::EXPECTED),
                                       GetGattErrorCallback(Call::NOT_EXPECTED));
-  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::NOT_EXPECTED),
-                                     GetGattErrorCallback(Call::EXPECTED));
+  descriptor1_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::EXPECTED, Result::FAILURE));
 
   base::RunLoop().RunUntilIdle();
 
@@ -874,8 +874,8 @@
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
   std::vector<uint8_t> empty_vector;
-  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
-                                     GetGattErrorCallback(Call::NOT_EXPECTED));
+  descriptor1_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
   descriptor1_->WriteRemoteDescriptor(empty_vector,
                                       GetCallback(Call::NOT_EXPECTED),
                                       GetGattErrorCallback(Call::EXPECTED));
@@ -913,9 +913,9 @@
 
   SimulateGattDisconnection(device_);
   // Don't run the disconnect task.
-  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::NOT_EXPECTED),
-                                     // TODO(crbug.com/621901): Expect an error.
-                                     GetGattErrorCallback(Call::NOT_EXPECTED));
+  // TODO(crbug.com/621901): Expect an error.
+  descriptor1_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::NOT_EXPECTED, Result::FAILURE));
 
   base::RunLoop().RunUntilIdle();
   // TODO(crbug.com/621901): Test error callback was called.
@@ -959,8 +959,8 @@
   }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
-  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
-                                     GetGattErrorCallback(Call::NOT_EXPECTED));
+  descriptor1_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
   EXPECT_EQ(1, gatt_read_descriptor_attempts_);
 
   std::string test_string = "Hello";
@@ -981,8 +981,8 @@
   }
   ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate());
 
-  descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED),
-                                     GetGattErrorCallback(Call::NOT_EXPECTED));
+  descriptor1_->ReadRemoteDescriptor(
+      GetReadValueCallback(Call::EXPECTED, Result::SUCCESS));
   EXPECT_EQ(1, gatt_read_descriptor_attempts_);
 
   const short test_number = 0x1234;
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_win.cc b/device/bluetooth/bluetooth_remote_gatt_descriptor_win.cc
index 9197c23..456c18b 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_win.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_win.cc
@@ -69,13 +69,12 @@
 }
 
 void BluetoothRemoteGattDescriptorWin::ReadRemoteDescriptor(
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
 
   NOTIMPLEMENTED();
-  std::move(error_callback)
-      .Run(BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED);
+  std::move(callback).Run(BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED,
+                          /*value=*/std::vector<uint8_t>());
 }
 
 void BluetoothRemoteGattDescriptorWin::WriteRemoteDescriptor(
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_win.h b/device/bluetooth/bluetooth_remote_gatt_descriptor_win.h
index ffa7b324..f99cde3 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_win.h
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_win.h
@@ -38,8 +38,7 @@
   BluetoothRemoteGattCharacteristic* GetCharacteristic() const override;
   BluetoothRemoteGattCharacteristic::Permissions GetPermissions()
       const override;
-  void ReadRemoteDescriptor(ValueCallback callback,
-                            ErrorCallback error_callback) override;
+  void ReadRemoteDescriptor(ValueCallback callback) override;
   void WriteRemoteDescriptor(const std::vector<uint8_t>& new_value,
                              base::OnceClosure callback,
                              ErrorCallback error_callback) override;
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.cc b/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.cc
index 7b845065..92d8b71 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.cc
@@ -100,13 +100,13 @@
 }
 
 void BluetoothRemoteGattDescriptorWinrt::ReadRemoteDescriptor(
-    ValueCallback callback,
-    ErrorCallback error_callback) {
-  if (pending_read_callbacks_ || pending_write_callbacks_) {
+    ValueCallback callback) {
+  if (pending_read_callback_ || pending_write_callbacks_) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::BindOnce(std::move(error_callback),
-                       BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS));
+        base::BindOnce(std::move(callback),
+                       BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS,
+                       /*value=*/std::vector<uint8_t>()));
     return;
   }
 
@@ -118,9 +118,9 @@
         << "GattDescriptor::ReadValueWithCacheModeAsync failed: "
         << logging::SystemErrorCodeToString(hr);
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(error_callback),
-                       BluetoothRemoteGattService::GATT_ERROR_FAILED));
+        FROM_HERE, base::BindOnce(std::move(callback),
+                                  BluetoothRemoteGattService::GATT_ERROR_FAILED,
+                                  /*value=*/std::vector<uint8_t>()));
     return;
   }
 
@@ -133,21 +133,20 @@
     BLUETOOTH_LOG(ERROR) << "PostAsyncResults failed: "
                          << logging::SystemErrorCodeToString(hr);
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(error_callback),
-                       BluetoothRemoteGattService::GATT_ERROR_FAILED));
+        FROM_HERE, base::BindOnce(std::move(callback),
+                                  BluetoothRemoteGattService::GATT_ERROR_FAILED,
+                                  /*value=*/std::vector<uint8_t>()));
     return;
   }
 
-  pending_read_callbacks_ = std::make_unique<PendingReadCallbacks>(
-      std::move(callback), std::move(error_callback));
+  pending_read_callback_ = std::move(callback);
 }
 
 void BluetoothRemoteGattDescriptorWinrt::WriteRemoteDescriptor(
     const std::vector<uint8_t>& value,
     base::OnceClosure callback,
     ErrorCallback error_callback) {
-  if (pending_read_callbacks_ || pending_write_callbacks_) {
+  if (pending_read_callback_ || pending_write_callbacks_) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
         base::BindOnce(std::move(error_callback),
@@ -215,15 +214,6 @@
   return descriptor_.Get();
 }
 
-BluetoothRemoteGattDescriptorWinrt::PendingReadCallbacks::PendingReadCallbacks(
-    ValueCallback callback,
-    ErrorCallback error_callback)
-    : callback(std::move(callback)),
-      error_callback(std::move(error_callback)) {}
-
-BluetoothRemoteGattDescriptorWinrt::PendingReadCallbacks::
-    ~PendingReadCallbacks() = default;
-
 BluetoothRemoteGattDescriptorWinrt::PendingWriteCallbacks::
     PendingWriteCallbacks(base::OnceClosure callback,
                           ErrorCallback error_callback)
@@ -250,14 +240,15 @@
 
 void BluetoothRemoteGattDescriptorWinrt::OnReadValue(
     ComPtr<IGattReadResult> read_result) {
-  DCHECK(pending_read_callbacks_);
-  auto pending_read_callbacks = std::move(pending_read_callbacks_);
+  DCHECK(pending_read_callback_);
+  auto pending_read_callback = std::move(pending_read_callback_);
 
   if (!read_result) {
     BLUETOOTH_LOG(ERROR)
         << "GattDescriptor::ReadValueWithCacheModeAsync returned no result";
-    std::move(pending_read_callbacks->error_callback)
-        .Run(BluetoothGattService::GATT_ERROR_FAILED);
+    std::move(pending_read_callback)
+        .Run(BluetoothGattService::GATT_ERROR_FAILED,
+             /*value=*/std::vector<uint8_t>());
     return;
   }
 
@@ -266,8 +257,9 @@
   if (FAILED(hr)) {
     BLUETOOTH_LOG(ERROR) << "Getting GATT Communication Status failed: "
                          << logging::SystemErrorCodeToString(hr);
-    std::move(pending_read_callbacks->error_callback)
-        .Run(BluetoothGattService::GATT_ERROR_FAILED);
+    std::move(pending_read_callback)
+        .Run(BluetoothGattService::GATT_ERROR_FAILED,
+             /*value=*/std::vector<uint8_t>());
     return;
   }
 
@@ -278,14 +270,16 @@
     if (FAILED(hr)) {
       BLUETOOTH_LOG(ERROR) << "As IGattReadResult2 failed: "
                            << logging::SystemErrorCodeToString(hr);
-      std::move(pending_read_callbacks->error_callback)
-          .Run(BluetoothGattService::GATT_ERROR_FAILED);
+      std::move(pending_read_callback)
+          .Run(BluetoothGattService::GATT_ERROR_FAILED,
+               /*value=*/std::vector<uint8_t>());
       return;
     }
 
-    std::move(pending_read_callbacks->error_callback)
+    std::move(pending_read_callback)
         .Run(BluetoothRemoteGattServiceWinrt::GetGattErrorCode(
-            read_result_2.Get()));
+                 read_result_2.Get()),
+             /*value=*/std::vector<uint8_t>());
     return;
   }
 
@@ -294,8 +288,9 @@
   if (FAILED(hr)) {
     BLUETOOTH_LOG(ERROR) << "Getting Descriptor Value failed: "
                          << logging::SystemErrorCodeToString(hr);
-    std::move(pending_read_callbacks->error_callback)
-        .Run(BluetoothGattService::GATT_ERROR_FAILED);
+    std::move(pending_read_callback)
+        .Run(BluetoothGattService::GATT_ERROR_FAILED,
+             /*value=*/std::vector<uint8_t>());
     return;
   }
 
@@ -305,13 +300,14 @@
   if (FAILED(hr)) {
     BLUETOOTH_LOG(ERROR) << "Getting Pointer To Buffer Data failed: "
                          << logging::SystemErrorCodeToString(hr);
-    std::move(pending_read_callbacks->error_callback)
-        .Run(BluetoothGattService::GATT_ERROR_FAILED);
+    std::move(pending_read_callback)
+        .Run(BluetoothGattService::GATT_ERROR_FAILED,
+             /*value=*/std::vector<uint8_t>());
     return;
   }
 
   value_.assign(data, data + length);
-  std::move(pending_read_callbacks->callback).Run(value_);
+  std::move(pending_read_callback).Run(/*error_code=*/base::nullopt, value_);
 }
 
 void BluetoothRemoteGattDescriptorWinrt::OnWriteValueWithResult(
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.h b/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.h
index c042044..049171ea 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.h
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.h
@@ -42,8 +42,7 @@
   // BluetoothRemoteGattDescriptor:
   const std::vector<uint8_t>& GetValue() const override;
   BluetoothRemoteGattCharacteristic* GetCharacteristic() const override;
-  void ReadRemoteDescriptor(ValueCallback callback,
-                            ErrorCallback error_callback) override;
+  void ReadRemoteDescriptor(ValueCallback callback) override;
   void WriteRemoteDescriptor(const std::vector<uint8_t>& value,
                              base::OnceClosure callback,
                              ErrorCallback error_callback) override;
@@ -52,14 +51,6 @@
   GetDescriptorForTesting();
 
  private:
-  struct PendingReadCallbacks {
-    PendingReadCallbacks(ValueCallback callback, ErrorCallback error_callback);
-    ~PendingReadCallbacks();
-
-    ValueCallback callback;
-    ErrorCallback error_callback;
-  };
-
   struct PendingWriteCallbacks {
     PendingWriteCallbacks(base::OnceClosure callback,
                           ErrorCallback error_callback);
@@ -94,7 +85,7 @@
   BluetoothUUID uuid_;
   std::string identifier_;
   std::vector<uint8_t> value_;
-  std::unique_ptr<PendingReadCallbacks> pending_read_callbacks_;
+  ValueCallback pending_read_callback_;
   std::unique_ptr<PendingWriteCallbacks> pending_write_callbacks_;
 
   base::WeakPtrFactory<BluetoothRemoteGattDescriptorWinrt> weak_ptr_factory_{
diff --git a/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc b/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc
index 8f48317..3105bbf 100644
--- a/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc
+++ b/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc
@@ -262,9 +262,16 @@
 
   void SuccessCallback() { ++success_callback_count_; }
 
-  void ValueCallback(const std::vector<uint8_t>& value) {
-    ++success_callback_count_;
-    last_read_value_ = value;
+  void ValueCallback(
+      base::Optional<BluetoothRemoteGattService::GattErrorCode> error_code,
+      const std::vector<uint8_t>& value) {
+    if (error_code.has_value()) {
+      ++error_callback_count_;
+      last_service_error_ = error_code.value();
+    } else {
+      ++success_callback_count_;
+      last_read_value_ = value;
+    }
   }
 
   void GattConnectionCallback(std::unique_ptr<BluetoothGattConnection> conn) {
@@ -1139,8 +1146,6 @@
   EXPECT_EQ(kBodySensorLocationUUID, characteristic->GetUUID());
   characteristic->ReadRemoteCharacteristic(
       base::BindOnce(&BluetoothGattBlueZTest::ValueCallback,
-                     base::Unretained(this)),
-      base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                      base::Unretained(this)));
   EXPECT_EQ(2, success_callback_count_);
   EXPECT_EQ(4, error_callback_count_);
@@ -1160,8 +1165,6 @@
   EXPECT_EQ(kBodySensorLocationUUID, characteristic->GetUUID());
   characteristic->ReadRemoteCharacteristic(
       base::BindOnce(&BluetoothGattBlueZTest::ValueCallback,
-                     base::Unretained(this)),
-      base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                      base::Unretained(this)));
 
   // Callback counts shouldn't change, this one will be delayed until after
@@ -1173,8 +1176,6 @@
   // Next read should error because IN_PROGRESS
   characteristic->ReadRemoteCharacteristic(
       base::BindOnce(&BluetoothGattBlueZTest::ValueCallback,
-                     base::Unretained(this)),
-      base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                      base::Unretained(this)));
   EXPECT_EQ(5, error_callback_count_);
   EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS,
@@ -1190,8 +1191,6 @@
   fake_bluetooth_gatt_characteristic_client_->SetAuthorized(false);
   characteristic->ReadRemoteCharacteristic(
       base::BindOnce(&BluetoothGattBlueZTest::ValueCallback,
-                     base::Unretained(this)),
-      base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                      base::Unretained(this)));
   EXPECT_EQ(3, success_callback_count_);
   EXPECT_EQ(6, error_callback_count_);
@@ -1204,8 +1203,6 @@
   fake_bluetooth_gatt_characteristic_client_->SetAuthenticated(false);
   characteristic->ReadRemoteCharacteristic(
       base::BindOnce(&BluetoothGattBlueZTest::ValueCallback,
-                     base::Unretained(this)),
-      base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                      base::Unretained(this)));
   EXPECT_EQ(3, success_callback_count_);
   EXPECT_EQ(7, error_callback_count_);
@@ -1360,8 +1357,6 @@
   EXPECT_EQ(kBodySensorLocationUUID, characteristic->GetUUID());
   characteristic->ReadRemoteCharacteristic(
       base::BindOnce(&BluetoothGattBlueZTest::ValueCallback,
-                     base::Unretained(this)),
-      base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                      base::Unretained(this)));
   EXPECT_EQ(2, success_callback_count_);
   EXPECT_EQ(4, error_callback_count_);
@@ -1381,8 +1376,6 @@
   EXPECT_EQ(kBodySensorLocationUUID, characteristic->GetUUID());
   characteristic->ReadRemoteCharacteristic(
       base::BindOnce(&BluetoothGattBlueZTest::ValueCallback,
-                     base::Unretained(this)),
-      base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                      base::Unretained(this)));
 
   // Callback counts shouldn't change, this one will be delayed until after
@@ -1394,8 +1387,6 @@
   // Next read should error because IN_PROGRESS
   characteristic->ReadRemoteCharacteristic(
       base::BindOnce(&BluetoothGattBlueZTest::ValueCallback,
-                     base::Unretained(this)),
-      base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                      base::Unretained(this)));
   EXPECT_EQ(5, error_callback_count_);
   EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS,
@@ -1411,8 +1402,6 @@
   fake_bluetooth_gatt_characteristic_client_->SetAuthorized(false);
   characteristic->ReadRemoteCharacteristic(
       base::BindOnce(&BluetoothGattBlueZTest::ValueCallback,
-                     base::Unretained(this)),
-      base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                      base::Unretained(this)));
   EXPECT_EQ(3, success_callback_count_);
   EXPECT_EQ(6, error_callback_count_);
@@ -1425,8 +1414,6 @@
   fake_bluetooth_gatt_characteristic_client_->SetAuthenticated(false);
   characteristic->ReadRemoteCharacteristic(
       base::BindOnce(&BluetoothGattBlueZTest::ValueCallback,
-                     base::Unretained(this)),
-      base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                      base::Unretained(this)));
   EXPECT_EQ(3, success_callback_count_);
   EXPECT_EQ(7, error_callback_count_);
@@ -1465,21 +1452,18 @@
                                      ->GetBodySensorLocationPath()
                                      .value());
 
-  characteristic->ReadRemoteCharacteristic(
-      base::BindLambdaForTesting([&](const std::vector<uint8_t>& data) {
-        ValueCallback(data);
+  characteristic->ReadRemoteCharacteristic(base::BindLambdaForTesting(
+      [&](base::Optional<BluetoothRemoteGattService::GattErrorCode> error_code,
+          const std::vector<uint8_t>& data) {
+        ValueCallback(error_code, data);
         EXPECT_EQ(1, success_callback_count_);
         EXPECT_EQ(0, error_callback_count_);
         EXPECT_EQ(characteristic->GetValue(), last_read_value_);
 
         characteristic->ReadRemoteCharacteristic(
             base::BindOnce(&BluetoothGattBlueZTest::ValueCallback,
-                           base::Unretained(this)),
-            base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                            base::Unretained(this)));
-      }),
-      base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
-                     base::Unretained(this)));
+      }));
   EXPECT_EQ(2, success_callback_count_);
   EXPECT_EQ(0, error_callback_count_);
   EXPECT_EQ(characteristic->GetValue(), last_read_value_);
@@ -1615,9 +1599,10 @@
                                      ->GetBodySensorLocationPath()
                                      .value());
 
-  characteristic->ReadRemoteCharacteristic(
-      base::BindLambdaForTesting([&](const std::vector<uint8_t>& data) {
-        ValueCallback(data);
+  characteristic->ReadRemoteCharacteristic(base::BindLambdaForTesting(
+      [&](base::Optional<BluetoothRemoteGattService::GattErrorCode> error_code,
+          const std::vector<uint8_t>& data) {
+        ValueCallback(error_code, data);
         EXPECT_EQ(1, success_callback_count_);
         EXPECT_EQ(0, error_callback_count_);
         EXPECT_EQ(characteristic->GetValue(), last_read_value_);
@@ -1634,9 +1619,7 @@
                            base::Unretained(this)),
             base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                            base::Unretained(this)));
-      }),
-      base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
-                     base::Unretained(this)));
+      }));
   EXPECT_EQ(2, success_callback_count_);
   EXPECT_EQ(0, error_callback_count_);
 }
@@ -1671,9 +1654,10 @@
                                      ->GetBodySensorLocationPath()
                                      .value());
 
-  characteristic->ReadRemoteCharacteristic(
-      base::BindLambdaForTesting([&](const std::vector<uint8_t>& data) {
-        ValueCallback(data);
+  characteristic->ReadRemoteCharacteristic(base::BindLambdaForTesting(
+      [&](base::Optional<BluetoothRemoteGattService::GattErrorCode> error_code,
+          const std::vector<uint8_t>& data) {
+        ValueCallback(error_code, data);
         EXPECT_EQ(1, success_callback_count_);
         EXPECT_EQ(0, error_callback_count_);
         EXPECT_EQ(characteristic->GetValue(), last_read_value_);
@@ -1690,9 +1674,7 @@
                            base::Unretained(this)),
             base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                            base::Unretained(this)));
-      }),
-      base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
-                     base::Unretained(this)));
+      }));
   EXPECT_EQ(2, success_callback_count_);
   EXPECT_EQ(0, error_callback_count_);
 }
@@ -1741,8 +1723,6 @@
 
         characteristic->ReadRemoteCharacteristic(
             base::BindOnce(&BluetoothGattBlueZTest::ValueCallback,
-                           base::Unretained(this)),
-            base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                            base::Unretained(this)));
       }),
       base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
@@ -1796,8 +1776,6 @@
 
         characteristic->ReadRemoteCharacteristic(
             base::BindOnce(&BluetoothGattBlueZTest::ValueCallback,
-                           base::Unretained(this)),
-            base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                            base::Unretained(this)));
       }),
       base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
@@ -1908,8 +1886,6 @@
   // successful read.
   descriptor->ReadRemoteDescriptor(
       base::BindOnce(&BluetoothGattBlueZTest::ValueCallback,
-                     base::Unretained(this)),
-      base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                      base::Unretained(this)));
   EXPECT_EQ(1, success_callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -1938,8 +1914,6 @@
   // Read value. The value should remain unchanged.
   descriptor->ReadRemoteDescriptor(
       base::BindOnce(&BluetoothGattBlueZTest::ValueCallback,
-                     base::Unretained(this)),
-      base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                      base::Unretained(this)));
   EXPECT_EQ(2, success_callback_count_);
   EXPECT_EQ(1, error_callback_count_);
@@ -1964,8 +1938,6 @@
   // Read the new descriptor value. We should receive a value updated event.
   descriptor->ReadRemoteDescriptor(
       base::BindOnce(&BluetoothGattBlueZTest::ValueCallback,
-                     base::Unretained(this)),
-      base::BindOnce(&BluetoothGattBlueZTest::ServiceErrorCallback,
                      base::Unretained(this)));
   EXPECT_EQ(4, success_callback_count_);
   EXPECT_EQ(1, error_callback_count_);
diff --git a/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc b/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc
index 4795e7c7..0557992 100644
--- a/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc
+++ b/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc
@@ -155,8 +155,7 @@
 }
 
 void BluetoothRemoteGattCharacteristicBlueZ::ReadRemoteCharacteristic(
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   DVLOG(1) << "Sending GATT characteristic read request to characteristic: "
            << GetIdentifier() << ", UUID: " << GetUUID().canonical_value()
            << ".";
@@ -164,13 +163,14 @@
   DCHECK_GE(num_of_characteristic_value_read_in_progress_, 0);
   ++num_of_characteristic_value_read_in_progress_;
 
+  auto split_callback = base::SplitOnceCallback(std::move(callback));
   bluez::BluezDBusManager::Get()
       ->GetBluetoothGattCharacteristicClient()
       ->ReadValue(
-          object_path(), std::move(callback),
+          object_path(), std::move(split_callback.first),
           base::BindOnce(&BluetoothRemoteGattCharacteristicBlueZ::OnReadError,
                          weak_ptr_factory_.GetWeakPtr(),
-                         std::move(error_callback)));
+                         std::move(split_callback.second)));
 }
 
 void BluetoothRemoteGattCharacteristicBlueZ::WriteRemoteCharacteristic(
@@ -403,15 +403,17 @@
 }
 
 void BluetoothRemoteGattCharacteristicBlueZ::OnReadError(
-    ErrorCallback error_callback,
+    ValueCallback callback,
     const std::string& error_name,
     const std::string& error_message) {
   DVLOG(1) << "Operation failed: " << error_name
            << ", message: " << error_message;
   --num_of_characteristic_value_read_in_progress_;
   DCHECK_GE(num_of_characteristic_value_read_in_progress_, 0);
-  std::move(error_callback)
-      .Run(BluetoothGattServiceBlueZ::DBusErrorToServiceError(error_name));
+  std::move(callback).Run(
+      base::make_optional(
+          BluetoothGattServiceBlueZ::DBusErrorToServiceError(error_name)),
+      /*value=*/std::vector<uint8_t>());
 }
 
 void BluetoothRemoteGattCharacteristicBlueZ::OnWriteError(
diff --git a/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h b/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h
index 4e29fae2..7fc989f 100644
--- a/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h
+++ b/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h
@@ -55,8 +55,7 @@
   const std::vector<uint8_t>& GetValue() const override;
   device::BluetoothRemoteGattService* GetService() const override;
   bool IsNotifying() const override;
-  void ReadRemoteCharacteristic(ValueCallback callback,
-                                ErrorCallback error_callback) override;
+  void ReadRemoteCharacteristic(ValueCallback callback) override;
   void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
                                  WriteType write_type,
                                  base::OnceClosure callback,
@@ -124,7 +123,7 @@
 
   // Called by dbus:: on unsuccessful completion of a request to read
   // the characteristic value.
-  void OnReadError(ErrorCallback error_callback,
+  void OnReadError(ValueCallback callback,
                    const std::string& error_name,
                    const std::string& error_message);
 
diff --git a/device/bluetooth/bluez/bluetooth_remote_gatt_descriptor_bluez.cc b/device/bluetooth/bluez/bluetooth_remote_gatt_descriptor_bluez.cc
index 4c761750..f59af4f 100644
--- a/device/bluetooth/bluez/bluetooth_remote_gatt_descriptor_bluez.cc
+++ b/device/bluetooth/bluez/bluetooth_remote_gatt_descriptor_bluez.cc
@@ -7,11 +7,13 @@
 #include <iterator>
 #include <ostream>
 
+#include <base/callback_helpers.h>
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
 #include "dbus/property.h"
+#include "device/bluetooth/bluetooth_gatt_service.h"
 #include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
 #include "device/bluetooth/bluez/bluetooth_gatt_characteristic_bluez.h"
 #include "device/bluetooth/bluez/bluetooth_gatt_descriptor_bluez.h"
@@ -80,17 +82,17 @@
 }
 
 void BluetoothRemoteGattDescriptorBlueZ::ReadRemoteDescriptor(
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   DVLOG(1) << "Sending GATT characteristic descriptor read request to "
            << "descriptor: " << GetIdentifier()
            << ", UUID: " << GetUUID().canonical_value();
 
+  auto split_callback = base::SplitOnceCallback(std::move(callback));
   bluez::BluezDBusManager::Get()->GetBluetoothGattDescriptorClient()->ReadValue(
-      object_path(), std::move(callback),
-      base::BindOnce(&BluetoothRemoteGattDescriptorBlueZ::OnError,
+      object_path(), std::move(split_callback.first),
+      base::BindOnce(&BluetoothRemoteGattDescriptorBlueZ::OnReadError,
                      weak_ptr_factory_.GetWeakPtr(),
-                     std::move(error_callback)));
+                     std::move(split_callback.second)));
 }
 
 void BluetoothRemoteGattDescriptorBlueZ::WriteRemoteDescriptor(
@@ -110,6 +112,18 @@
                                   std::move(error_callback)));
 }
 
+void BluetoothRemoteGattDescriptorBlueZ::OnReadError(
+    ValueCallback callback,
+    const std::string& error_name,
+    const std::string& error_message) {
+  DVLOG(1) << "Operation failed: " << error_name
+           << ", message: " << error_message;
+
+  std::move(callback).Run(
+      BluetoothGattServiceBlueZ::DBusErrorToServiceError(error_name),
+      /*value=*/std::vector<uint8_t>());
+}
+
 void BluetoothRemoteGattDescriptorBlueZ::OnError(
     ErrorCallback error_callback,
     const std::string& error_name,
diff --git a/device/bluetooth/bluez/bluetooth_remote_gatt_descriptor_bluez.h b/device/bluetooth/bluez/bluetooth_remote_gatt_descriptor_bluez.h
index 513185b..743c008 100644
--- a/device/bluetooth/bluez/bluetooth_remote_gatt_descriptor_bluez.h
+++ b/device/bluetooth/bluez/bluetooth_remote_gatt_descriptor_bluez.h
@@ -35,8 +35,7 @@
   device::BluetoothRemoteGattCharacteristic* GetCharacteristic() const override;
   device::BluetoothRemoteGattCharacteristic::Permissions GetPermissions()
       const override;
-  void ReadRemoteDescriptor(ValueCallback callback,
-                            ErrorCallback error_callback) override;
+  void ReadRemoteDescriptor(ValueCallback callback) override;
   void WriteRemoteDescriptor(const std::vector<uint8_t>& new_value,
                              base::OnceClosure callback,
                              ErrorCallback error_callback) override;
@@ -48,7 +47,13 @@
       BluetoothRemoteGattCharacteristicBlueZ* characteristic,
       const dbus::ObjectPath& object_path);
 
-  // Called by dbus:: on unsuccessful completion of a request to read or write
+  // Called by dbus:: on unsuccessful completion of a request to read
+  // the descriptor value.
+  void OnReadError(ValueCallback callback,
+                   const std::string& error_name,
+                   const std::string& error_message);
+
+  // Called by dbus:: on unsuccessful completion of a request to write
   // the descriptor value.
   void OnError(ErrorCallback error_callback,
                const std::string& error_name,
diff --git a/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc b/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc
index 8b88155..45f26847 100644
--- a/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc
+++ b/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc
@@ -134,12 +134,10 @@
 }
 
 void BluetoothRemoteGattCharacteristicCast::ReadRemoteCharacteristic(
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   remote_characteristic_->Read(base::BindOnce(
       &BluetoothRemoteGattCharacteristicCast::OnReadRemoteCharacteristic,
-      weak_factory_.GetWeakPtr(), std::move(callback),
-      std::move(error_callback)));
+      weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void BluetoothRemoteGattCharacteristicCast::WriteRemoteCharacteristic(
@@ -214,15 +212,15 @@
 
 void BluetoothRemoteGattCharacteristicCast::OnReadRemoteCharacteristic(
     ValueCallback callback,
-    ErrorCallback error_callback,
     bool success,
     const std::vector<uint8_t>& result) {
   if (success) {
     value_ = result;
-    std::move(callback).Run(result);
+    std::move(callback).Run(/*error_code=*/base::nullopt, result);
     return;
   }
-  std::move(error_callback).Run(BluetoothGattService::GATT_ERROR_FAILED);
+  std::move(callback).Run(BluetoothGattService::GATT_ERROR_FAILED,
+                          /*value=*/std::vector<uint8_t>());
 }
 
 void BluetoothRemoteGattCharacteristicCast::OnWriteRemoteCharacteristic(
diff --git a/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.h b/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.h
index af4dd71..dd297da 100644
--- a/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.h
+++ b/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.h
@@ -45,8 +45,7 @@
   // BluetoothRemoteGattCharacteristic implementation:
   const std::vector<uint8_t>& GetValue() const override;
   BluetoothRemoteGattService* GetService() const override;
-  void ReadRemoteCharacteristic(ValueCallback callback,
-                                ErrorCallback error_callback) override;
+  void ReadRemoteCharacteristic(ValueCallback callback) override;
   void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
                                  WriteType write_type,
                                  base::OnceClosure callback,
@@ -70,13 +69,13 @@
       base::OnceClosure callback,
       ErrorCallback error_callback) override;
 
-  // Called when the remote characteristic has been read or the operation has
-  // failed. If the former, |success| will be true, and |result| will be
-  // valid. In this case, |value_| is updated and |callback| is run with
-  // |result|. If |success| is false, |result| is ignored and |error_callback|
-  // is run.
+  // Called upon completation of a read (success or failure) of a remote
+  // characteristic. When successful |success| will be true and |result|
+  // will contain the characteristic value. In this case |value_| is updated
+  // and |callback| is run with |result| and no error code. Upon failure
+  // |success| is false, |result| is undefined, and |callback| is run with an
+  // appropriate error code and the value is invalid.
   void OnReadRemoteCharacteristic(ValueCallback callback,
-                                  ErrorCallback error_callback,
                                   bool success,
                                   const std::vector<uint8_t>& result);
 
diff --git a/device/bluetooth/cast/bluetooth_remote_gatt_descriptor_cast.cc b/device/bluetooth/cast/bluetooth_remote_gatt_descriptor_cast.cc
index ac7010d..da890ed 100644
--- a/device/bluetooth/cast/bluetooth_remote_gatt_descriptor_cast.cc
+++ b/device/bluetooth/cast/bluetooth_remote_gatt_descriptor_cast.cc
@@ -50,12 +50,10 @@
 }
 
 void BluetoothRemoteGattDescriptorCast::ReadRemoteDescriptor(
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   remote_descriptor_->Read(
       base::BindOnce(&BluetoothRemoteGattDescriptorCast::OnReadRemoteDescriptor,
-                     weak_factory_.GetWeakPtr(), std::move(callback),
-                     std::move(error_callback)));
+                     weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void BluetoothRemoteGattDescriptorCast::WriteRemoteDescriptor(
@@ -72,15 +70,15 @@
 
 void BluetoothRemoteGattDescriptorCast::OnReadRemoteDescriptor(
     ValueCallback callback,
-    ErrorCallback error_callback,
     bool success,
     const std::vector<uint8_t>& result) {
   if (success) {
     value_ = result;
-    std::move(callback).Run(result);
+    std::move(callback).Run(/*error_code=*/base::nullopt, result);
     return;
   }
-  std::move(error_callback).Run(BluetoothGattService::GATT_ERROR_FAILED);
+  std::move(callback).Run(BluetoothGattService::GATT_ERROR_FAILED,
+                          /*value=*/std::vector<uint8_t>());
 }
 
 void BluetoothRemoteGattDescriptorCast::OnWriteRemoteDescriptor(
diff --git a/device/bluetooth/cast/bluetooth_remote_gatt_descriptor_cast.h b/device/bluetooth/cast/bluetooth_remote_gatt_descriptor_cast.h
index d74bcaf..f799cb0 100644
--- a/device/bluetooth/cast/bluetooth_remote_gatt_descriptor_cast.h
+++ b/device/bluetooth/cast/bluetooth_remote_gatt_descriptor_cast.h
@@ -40,8 +40,7 @@
   // BluetoothRemoteGattDescriptor implementation:
   const std::vector<uint8_t>& GetValue() const override;
   BluetoothRemoteGattCharacteristic* GetCharacteristic() const override;
-  void ReadRemoteDescriptor(ValueCallback callback,
-                            ErrorCallback error_callback) override;
+  void ReadRemoteDescriptor(ValueCallback callback) override;
   void WriteRemoteDescriptor(const std::vector<uint8_t>& new_value,
                              base::OnceClosure callback,
                              ErrorCallback error_callback) override;
@@ -50,10 +49,9 @@
   // Called when the remote descriptor has been read or the operation has
   // failed. If the former, |success| will be true, and |result| will be
   // valid. In this case, |value_| is updated and |callback| is run with
-  // |result|. If |success| is false, |result| is ignored and |error_callback|
-  // is run.
+  // |result|. If |success| is false, |callback| will be called with
+  // an appropriate error_code and the value should be ignored.
   void OnReadRemoteDescriptor(ValueCallback callback,
-                              ErrorCallback error_callback,
                               bool success,
                               const std::vector<uint8_t>& result);
 
diff --git a/device/bluetooth/dbus/bluetooth_gatt_attribute_value_delegate.h b/device/bluetooth/dbus/bluetooth_gatt_attribute_value_delegate.h
index 0e3fd2c..13c0efc7e 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_attribute_value_delegate.h
+++ b/device/bluetooth/dbus/bluetooth_gatt_attribute_value_delegate.h
@@ -41,9 +41,7 @@
   // out if left pending for too long causing a disconnection.
   virtual void GetValue(
       const dbus::ObjectPath& device_path,
-      device::BluetoothLocalGattService::Delegate::ValueCallback callback,
-      device::BluetoothLocalGattService::Delegate::ErrorCallback
-          error_callback) = 0;
+      device::BluetoothLocalGattService::Delegate::ValueCallback callback) = 0;
 
   // This method will be called, when a remote device requests to write the
   // value of the exported GATT attribute. Invoke |callback| to report
diff --git a/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc b/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc
index 6311997..062efc9 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc
+++ b/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc
@@ -312,7 +312,7 @@
     if (bytes)
       value.assign(bytes, bytes + length);
 
-    std::move(callback).Run(value);
+    std::move(callback).Run(/*error_code=*/base::nullopt, value);
   }
 
   // Called when a response for a failed method call is received.
diff --git a/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h b/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h
index 9e03e8f5..4eef31d7 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h
+++ b/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h
@@ -12,6 +12,7 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "build/chromeos_buildflags.h"
 #include "dbus/object_path.h"
 #include "dbus/property.h"
@@ -80,8 +81,9 @@
   using ErrorCallback =
       base::OnceCallback<void(const std::string& error_name,
                               const std::string& error_message)>;
-  using ValueCallback =
-      base::OnceCallback<void(const std::vector<uint8_t>& value)>;
+  using ValueCallback = base::OnceCallback<void(
+      base::Optional<device::BluetoothGattService::GattErrorCode> error_code,
+      const std::vector<uint8_t>& value)>;
 
   ~BluetoothGattCharacteristicClient() override;
 
diff --git a/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.cc b/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.cc
index 3b7f335..5538a14 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.cc
+++ b/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.cc
@@ -18,16 +18,14 @@
 
 void BluetoothGattCharacteristicDelegateWrapper::GetValue(
     const dbus::ObjectPath& device_path,
-    device::BluetoothLocalGattService::Delegate::ValueCallback callback,
-    device::BluetoothLocalGattService::Delegate::ErrorCallback error_callback) {
+    device::BluetoothLocalGattService::Delegate::ValueCallback callback) {
   device::BluetoothDevice* device = GetDeviceWithPath(device_path);
   if (!device) {
     LOG(WARNING) << "Bluetooth device not found: " << device_path.value();
     return;
   }
-  service()->GetDelegate()->OnCharacteristicReadRequest(
-      device, characteristic_, 0, std::move(callback),
-      std::move(error_callback));
+  service()->GetDelegate()->OnCharacteristicReadRequest(device, characteristic_,
+                                                        0, std::move(callback));
 }
 
 void BluetoothGattCharacteristicDelegateWrapper::SetValue(
diff --git a/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.h b/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.h
index def89cc7..4b175f4 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.h
+++ b/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.h
@@ -29,11 +29,9 @@
       BluetoothLocalGattCharacteristicBlueZ* characteristic);
 
   // BluetoothGattAttributeValueDelegate overrides:
-  void GetValue(
-      const dbus::ObjectPath& device_path,
-      device::BluetoothLocalGattService::Delegate::ValueCallback callback,
-      device::BluetoothLocalGattService::Delegate::ErrorCallback error_callback)
-      override;
+  void GetValue(const dbus::ObjectPath& device_path,
+                device::BluetoothLocalGattService::Delegate::ValueCallback
+                    callback) override;
   void SetValue(const dbus::ObjectPath& device_path,
                 const std::vector<uint8_t>& value,
                 base::OnceClosure callback,
diff --git a/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.cc b/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.cc
index 1a3ec9d..735381e 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.cc
+++ b/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.cc
@@ -304,20 +304,13 @@
     // the delegate, which should know how to handle it.
   }
 
-  // GetValue() promises to only call either the success or error callback.
-  auto split_response_sender =
-      base::SplitOnceCallback(std::move(response_sender));
-
   DCHECK(delegate_);
   delegate_->GetValue(
       device_path,
       base::BindOnce(
           &BluetoothGattCharacteristicServiceProviderImpl::OnReadValue,
           weak_ptr_factory_.GetWeakPtr(), method_call,
-          std::move(split_response_sender.first)),
-      base::BindOnce(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure,
-                     weak_ptr_factory_.GetWeakPtr(), method_call,
-                     std::move(split_response_sender.second)));
+          std::move(response_sender)));
 }
 
 void BluetoothGattCharacteristicServiceProviderImpl::WriteValue(
@@ -365,9 +358,10 @@
           &BluetoothGattCharacteristicServiceProviderImpl::OnWriteValue,
           weak_ptr_factory_.GetWeakPtr(), method_call,
           std::move(split_response_sender.first)),
-      base::BindOnce(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure,
-                     weak_ptr_factory_.GetWeakPtr(), method_call,
-                     std::move(split_response_sender.second)));
+      base::BindOnce(
+          &BluetoothGattCharacteristicServiceProviderImpl::OnWriteValueFailure,
+          weak_ptr_factory_.GetWeakPtr(), method_call,
+          std::move(split_response_sender.second)));
 }
 
 void BluetoothGattCharacteristicServiceProviderImpl::PrepareWriteValue(
@@ -423,9 +417,10 @@
           &BluetoothGattCharacteristicServiceProviderImpl::OnWriteValue,
           weak_ptr_factory_.GetWeakPtr(), method_call,
           std::move(split_response_sender.first)),
-      base::BindOnce(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure,
-                     weak_ptr_factory_.GetWeakPtr(), method_call,
-                     std::move(split_response_sender.second)));
+      base::BindOnce(
+          &BluetoothGattCharacteristicServiceProviderImpl::OnWriteValueFailure,
+          weak_ptr_factory_.GetWeakPtr(), method_call,
+          std::move(split_response_sender.second)));
 }
 
 void BluetoothGattCharacteristicServiceProviderImpl::StartNotify(
@@ -489,7 +484,16 @@
 void BluetoothGattCharacteristicServiceProviderImpl::OnReadValue(
     dbus::MethodCall* method_call,
     dbus::ExportedObject::ResponseSender response_sender,
+    base::Optional<device::BluetoothGattService::GattErrorCode> error_code,
     const std::vector<uint8_t>& value) {
+  if (error_code.has_value()) {
+    DVLOG(2) << "Failed to read characteristic value. Report error.";
+    std::move(response_sender)
+        .Run(dbus::ErrorResponse::FromMethodCall(
+            method_call, kErrorFailed, "Failed to read characteristic value."));
+    return;
+  }
+
   DVLOG(3) << "Characteristic value obtained from delegate. Responding to "
               "ReadValue.";
 
@@ -542,13 +546,13 @@
   writer->CloseContainer(&array_writer);
 }
 
-void BluetoothGattCharacteristicServiceProviderImpl::OnFailure(
+void BluetoothGattCharacteristicServiceProviderImpl::OnWriteValueFailure(
     dbus::MethodCall* method_call,
     dbus::ExportedObject::ResponseSender response_sender) {
-  DVLOG(2) << "Failed to get/set characteristic value. Report error.";
+  DVLOG(2) << "Failed to set characteristic value. Report error.";
   std::unique_ptr<dbus::ErrorResponse> error_response =
       dbus::ErrorResponse::FromMethodCall(
-          method_call, kErrorFailed, "Failed to get/set characteristic value.");
+          method_call, kErrorFailed, "Failed to set characteristic value.");
   std::move(response_sender).Run(std::move(error_response));
 }
 
diff --git a/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.h b/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.h
index 95c140e7..615d8fe 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.h
+++ b/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.h
@@ -42,6 +42,11 @@
   void SendValueChanged(const std::vector<uint8_t>& value) override;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(BluetoothGattCharacteristicServiceProviderTest,
+                           ReadValueSuccess);
+  FRIEND_TEST_ALL_PREFIXES(BluetoothGattCharacteristicServiceProviderTest,
+                           ReadValueFailure);
+
   // Returns true if the current thread is on the origin thread.
   bool OnOriginThread();
 
@@ -102,19 +107,22 @@
 
   // Called by the Delegate in response to a method to call to read the value
   // of this characteristic.
-  void OnReadValue(dbus::MethodCall* method_call,
-                   dbus::ExportedObject::ResponseSender response_sender,
-                   const std::vector<uint8_t>& value);
+  void OnReadValue(
+      dbus::MethodCall* method_call,
+      dbus::ExportedObject::ResponseSender response_sender,
+      base::Optional<device::BluetoothGattService::GattErrorCode> error_code,
+      const std::vector<uint8_t>& value);
 
   // Called by the Delegate in response to a method to call to write the value
   // of this characteristic.
   void OnWriteValue(dbus::MethodCall* method_call,
                     dbus::ExportedObject::ResponseSender response_sender);
 
-  // Called by the Delegate in response to a failed method call to get or set
+  // Called by the Delegate in response to a failed method call to set
   // the characteristic value.
-  void OnFailure(dbus::MethodCall* method_call,
-                 dbus::ExportedObject::ResponseSender response_sender);
+  void OnWriteValueFailure(
+      dbus::MethodCall* method_call,
+      dbus::ExportedObject::ResponseSender response_sender);
 
   const dbus::ObjectPath& object_path() const override;
 
diff --git a/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_unittest.cc b/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_unittest.cc
new file mode 100644
index 0000000..d68de23c
--- /dev/null
+++ b/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_unittest.cc
@@ -0,0 +1,90 @@
+// 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 <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/optional.h"
+#include "base/test/bind.h"
+#include "dbus/bus.h"
+#include "dbus/message.h"
+#include "device/bluetooth/bluetooth_gatt_service.h"
+#include "device/bluetooth/dbus/bluetooth_gatt_attribute_value_delegate.h"
+#include "device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.h"
+#include "device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace bluez {
+
+TEST(BluetoothGattCharacteristicServiceProviderTest, ReadValueSuccess) {
+  auto method_call =
+      std::make_unique<dbus::MethodCall>("com.example.Interface", "SomeMethod");
+  method_call->SetSerial(123);
+  method_call->SetReplySerial(456);
+  bool callback_called = false;
+
+  BluetoothGattCharacteristicServiceProviderImpl provider_impl(
+      /*bus=*/nullptr,
+      /*object_path=*/dbus::ObjectPath(),
+      /*delegate=*/std::unique_ptr<BluetoothGattAttributeValueDelegate>(),
+      /*uuid=*/std::string(),
+      /*flags=*/std::vector<std::string>(),
+      /*service_path=*/dbus::ObjectPath());
+
+  const std::vector<uint8_t> read_value = {1, 2, 3};
+  provider_impl.OnReadValue(
+      method_call.get(),
+      base::BindLambdaForTesting([&callback_called, read_value](
+                                     std::unique_ptr<dbus::Response> response) {
+        EXPECT_EQ(response->GetMessageType(), DBUS_MESSAGE_TYPE_METHOD_RETURN);
+        dbus::MessageReader reader(response.get());
+        EXPECT_EQ(reader.GetDataType(), dbus::Message::ARRAY);
+        const uint8_t* bytes = nullptr;
+        size_t length = 0;
+        EXPECT_TRUE(reader.PopArrayOfBytes(&bytes, &length));
+        EXPECT_EQ(length, read_value.size());
+        callback_called = true;
+      }),
+      /*error_code=*/base::nullopt, read_value);
+
+  EXPECT_TRUE(callback_called);
+}
+
+TEST(BluetoothGattCharacteristicServiceProviderTest, ReadValueFailure) {
+  auto method_call =
+      std::make_unique<dbus::MethodCall>("com.example.Interface", "SomeMethod");
+  method_call->SetSerial(123);
+  method_call->SetReplySerial(456);
+  bool callback_called = false;
+
+  BluetoothGattCharacteristicServiceProviderImpl provider_impl(
+      /*bus=*/nullptr,
+      /*object_path=*/dbus::ObjectPath(),
+      /*delegate=*/std::unique_ptr<BluetoothGattAttributeValueDelegate>(),
+      /*uuid=*/std::string(),
+      /*flags=*/std::vector<std::string>(),
+      /*service_path=*/dbus::ObjectPath());
+
+  const std::vector<uint8_t> read_value = {1, 2, 3};
+  provider_impl.OnReadValue(
+      method_call.get(),
+      base::BindLambdaForTesting(
+          [&callback_called](std::unique_ptr<dbus::Response> response) {
+            EXPECT_EQ(response->GetMessageType(), DBUS_MESSAGE_TYPE_ERROR);
+            dbus::MessageReader reader(response.get());
+            EXPECT_NE(reader.GetDataType(), dbus::Message::ARRAY);
+            const uint8_t* bytes = nullptr;
+            size_t length = 0;
+            EXPECT_FALSE(reader.PopArrayOfBytes(&bytes, &length));
+            callback_called = true;
+          }),
+      device::BluetoothGattService::GATT_ERROR_FAILED, read_value);
+
+  EXPECT_TRUE(callback_called);
+}
+
+}  // namespace bluez
diff --git a/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc b/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc
index c2fbd87..e7e9c54 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc
+++ b/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc
@@ -221,7 +221,7 @@
     if (bytes)
       value.assign(bytes, bytes + length);
 
-    std::move(callback).Run(value);
+    std::move(callback).Run(/*error_code=*/base::nullopt, value);
   }
 
   // Called when a response for a failed method call is received.
diff --git a/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.h b/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.h
index b9f9a96..bd5168e 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.h
+++ b/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.h
@@ -12,9 +12,11 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "dbus/object_path.h"
 #include "dbus/property.h"
 #include "device/bluetooth/bluetooth_export.h"
+#include "device/bluetooth/bluetooth_gatt_service.h"
 #include "device/bluetooth/dbus/bluez_dbus_client.h"
 
 namespace bluez {
@@ -68,8 +70,9 @@
   using ErrorCallback =
       base::OnceCallback<void(const std::string& error_name,
                               const std::string& error_message)>;
-  using ValueCallback =
-      base::OnceCallback<void(const std::vector<uint8_t>& value)>;
+  using ValueCallback = base::OnceCallback<void(
+      base::Optional<device::BluetoothGattService::GattErrorCode> error_code,
+      const std::vector<uint8_t>& value)>;
 
   ~BluetoothGattDescriptorClient() override;
 
diff --git a/device/bluetooth/dbus/bluetooth_gatt_descriptor_delegate_wrapper.cc b/device/bluetooth/dbus/bluetooth_gatt_descriptor_delegate_wrapper.cc
index a60c511..1bae1d5 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_descriptor_delegate_wrapper.cc
+++ b/device/bluetooth/dbus/bluetooth_gatt_descriptor_delegate_wrapper.cc
@@ -15,11 +15,9 @@
 
 void BluetoothGattDescriptorDelegateWrapper::GetValue(
     const dbus::ObjectPath& device_path,
-    device::BluetoothLocalGattService::Delegate::ValueCallback callback,
-    device::BluetoothLocalGattService::Delegate::ErrorCallback error_callback) {
+    device::BluetoothLocalGattService::Delegate::ValueCallback callback) {
   service()->GetDelegate()->OnDescriptorReadRequest(
-      GetDeviceWithPath(device_path), descriptor_, 0, std::move(callback),
-      std::move(error_callback));
+      GetDeviceWithPath(device_path), descriptor_, 0, std::move(callback));
 }
 
 void BluetoothGattDescriptorDelegateWrapper::SetValue(
diff --git a/device/bluetooth/dbus/bluetooth_gatt_descriptor_delegate_wrapper.h b/device/bluetooth/dbus/bluetooth_gatt_descriptor_delegate_wrapper.h
index 7b947bc..54e0355 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_descriptor_delegate_wrapper.h
+++ b/device/bluetooth/dbus/bluetooth_gatt_descriptor_delegate_wrapper.h
@@ -29,11 +29,9 @@
       BluetoothLocalGattDescriptorBlueZ* descriptor);
 
   // BluetoothGattAttributeValueDelegate overrides:
-  void GetValue(
-      const dbus::ObjectPath& device_path,
-      device::BluetoothLocalGattService::Delegate::ValueCallback callback,
-      device::BluetoothLocalGattService::Delegate::ErrorCallback error_callback)
-      override;
+  void GetValue(const dbus::ObjectPath& device_path,
+                device::BluetoothLocalGattService::Delegate::ValueCallback
+                    callback) override;
   void SetValue(const dbus::ObjectPath& device_path,
                 const std::vector<uint8_t>& value,
                 base::OnceClosure callback,
diff --git a/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.cc b/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.cc
index 58c7046..87fc596 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.cc
+++ b/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.cc
@@ -10,6 +10,7 @@
 #include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/strings/string_util.h"
+#include "device/bluetooth/bluetooth_gatt_service.h"
 #include "device/bluetooth/dbus/bluetooth_gatt_attribute_helpers.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
@@ -277,10 +278,7 @@
       device_path,
       base::BindOnce(&BluetoothGattDescriptorServiceProviderImpl::OnReadValue,
                      weak_ptr_factory_.GetWeakPtr(), method_call,
-                     std::move(split_response_sender.first)),
-      base::BindOnce(&BluetoothGattDescriptorServiceProviderImpl::OnFailure,
-                     weak_ptr_factory_.GetWeakPtr(), method_call,
-                     std::move(split_response_sender.second)));
+                     std::move(split_response_sender.first)));
 }
 
 void BluetoothGattDescriptorServiceProviderImpl::WriteValue(
@@ -327,9 +325,10 @@
       base::BindOnce(&BluetoothGattDescriptorServiceProviderImpl::OnWriteValue,
                      weak_ptr_factory_.GetWeakPtr(), method_call,
                      std::move(split_response_sender.first)),
-      base::BindOnce(&BluetoothGattDescriptorServiceProviderImpl::OnFailure,
-                     weak_ptr_factory_.GetWeakPtr(), method_call,
-                     std::move(split_response_sender.second)));
+      base::BindOnce(
+          &BluetoothGattDescriptorServiceProviderImpl::OnWriteFailure,
+          weak_ptr_factory_.GetWeakPtr(), method_call,
+          std::move(split_response_sender.second)));
 }
 
 void BluetoothGattDescriptorServiceProviderImpl::OnExported(
@@ -343,7 +342,17 @@
 void BluetoothGattDescriptorServiceProviderImpl::OnReadValue(
     dbus::MethodCall* method_call,
     dbus::ExportedObject::ResponseSender response_sender,
+    base::Optional<device::BluetoothGattService::GattErrorCode> error_code,
     const std::vector<uint8_t>& value) {
+  if (error_code.has_value()) {
+    DVLOG(2) << "Failed to get descriptor value. Report error.";
+    std::unique_ptr<dbus::ErrorResponse> error_response =
+        dbus::ErrorResponse::FromMethodCall(method_call, kErrorFailed,
+                                            "Failed to get descriptor value.");
+    std::move(response_sender).Run(std::move(error_response));
+    return;
+  }
+
   DVLOG(3) << "Descriptor value obtained from delegate. Responding to "
               "ReadValue.";
 
@@ -396,13 +405,13 @@
   writer->CloseContainer(&array_writer);
 }
 
-void BluetoothGattDescriptorServiceProviderImpl::OnFailure(
+void BluetoothGattDescriptorServiceProviderImpl::OnWriteFailure(
     dbus::MethodCall* method_call,
     dbus::ExportedObject::ResponseSender response_sender) {
-  DVLOG(2) << "Failed to get/set descriptor value. Report error.";
+  DVLOG(2) << "Failed to set descriptor value. Report error.";
   std::unique_ptr<dbus::ErrorResponse> error_response =
-      dbus::ErrorResponse::FromMethodCall(
-          method_call, kErrorFailed, "Failed to get/set descriptor value.");
+      dbus::ErrorResponse::FromMethodCall(method_call, kErrorFailed,
+                                          "Failed to set descriptor value.");
   std::move(response_sender).Run(std::move(error_response));
 }
 
diff --git a/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.h b/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.h
index 199256f7..d21f34a 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.h
+++ b/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.h
@@ -78,19 +78,21 @@
 
   // Called by the Delegate in response to a method to call to read the value
   // of this descriptor.
-  void OnReadValue(dbus::MethodCall* method_call,
-                   dbus::ExportedObject::ResponseSender response_sender,
-                   const std::vector<uint8_t>& value);
+  void OnReadValue(
+      dbus::MethodCall* method_call,
+      dbus::ExportedObject::ResponseSender response_sender,
+      base::Optional<device::BluetoothGattService::GattErrorCode> error_code,
+      const std::vector<uint8_t>& value);
 
   // Called by the Delegate in response to a method to call to write the value
   // of this descriptor.
   void OnWriteValue(dbus::MethodCall* method_call,
                     dbus::ExportedObject::ResponseSender response_sender);
 
-  // Called by the Delegate in response to a failed method call to get or set
+  // Called by the Delegate in response to a failed method call to set
   // the descriptor value.
-  void OnFailure(dbus::MethodCall* method_call,
-                 dbus::ExportedObject::ResponseSender response_sender);
+  void OnWriteFailure(dbus::MethodCall* method_call,
+                      dbus::ExportedObject::ResponseSender response_sender);
 
   const dbus::ObjectPath& object_path() const override;
 
diff --git a/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc b/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc
index 0010c53..9b5d999b 100644
--- a/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc
+++ b/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc
@@ -575,7 +575,7 @@
   DCHECK(properties);
 
   properties->value.ReplaceValue(value);
-  std::move(callback).Run(value);
+  std::move(callback).Run(/*error_code=*/base::nullopt, value);
 }
 
 std::vector<uint8_t>
diff --git a/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.cc b/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.cc
index a53194f..c2a337f 100644
--- a/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.cc
+++ b/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.cc
@@ -10,7 +10,9 @@
 #include "base/callback.h"
 #include "base/logging.h"
 #include "base/strings/string_util.h"
+#include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_gatt_characteristic.h"
+#include "device/bluetooth/bluetooth_gatt_service.h"
 #include "device/bluetooth/dbus/bluetooth_gatt_attribute_value_delegate.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
 #include "device/bluetooth/dbus/fake_bluetooth_gatt_manager_client.h"
@@ -116,8 +118,7 @@
 
 void FakeBluetoothGattCharacteristicServiceProvider::GetValue(
     const dbus::ObjectPath& device_path,
-    device::BluetoothLocalGattService::Delegate::ValueCallback callback,
-    device::BluetoothLocalGattService::Delegate::ErrorCallback error_callback) {
+    device::BluetoothLocalGattService::Delegate::ValueCallback callback) {
   DVLOG(1) << "GATT characteristic value Get request: " << object_path_.value()
            << " UUID: " << uuid_;
   // Check if this characteristic is registered.
@@ -126,20 +127,21 @@
           bluez::BluezDBusManager::Get()->GetBluetoothGattManagerClient());
   if (!fake_bluetooth_gatt_manager_client->IsServiceRegistered(service_path_)) {
     DVLOG(1) << "GATT characteristic not registered.";
-    std::move(error_callback).Run();
+    std::move(callback).Run(device::BluetoothGattService::GATT_ERROR_FAILED,
+                            /*value=*/std::vector<uint8_t>());
     return;
   }
 
   if (!CanRead(flags_)) {
     DVLOG(1) << "GATT characteristic not readable.";
-    std::move(error_callback).Run();
+    std::move(callback).Run(device::BluetoothGattService::GATT_ERROR_FAILED,
+                            /*value=*/std::vector<uint8_t>());
     return;
   }
 
   // Pass on to the delegate.
   DCHECK(delegate_);
-  delegate_->GetValue(device_path, std::move(callback),
-                      std::move(error_callback));
+  delegate_->GetValue(device_path, std::move(callback));
 }
 
 void FakeBluetoothGattCharacteristicServiceProvider::SetValue(
diff --git a/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.h b/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.h
index 3f093f47..27ec2e3 100644
--- a/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.h
+++ b/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.h
@@ -41,9 +41,7 @@
   // GATT manager.
   void GetValue(
       const dbus::ObjectPath& device_path,
-      device::BluetoothLocalGattService::Delegate::ValueCallback callback,
-      device::BluetoothLocalGattService::Delegate::ErrorCallback
-          error_callback);
+      device::BluetoothLocalGattService::Delegate::ValueCallback callback);
   void SetValue(const dbus::ObjectPath& device_path,
                 const std::vector<uint8_t>& value,
                 base::OnceClosure callback,
diff --git a/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.cc b/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.cc
index b2d848e..8f40476 100644
--- a/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.cc
+++ b/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.cc
@@ -120,7 +120,8 @@
     }
   }
 
-  std::move(callback).Run(iter->second->properties->value.value());
+  std::move(callback).Run(/*error_code=*/base::nullopt,
+                          iter->second->properties->value.value());
 }
 
 void FakeBluetoothGattDescriptorClient::WriteValue(
diff --git a/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_service_provider.cc b/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_service_provider.cc
index 80d73a235..c16a6db 100644
--- a/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_service_provider.cc
+++ b/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_service_provider.cc
@@ -97,8 +97,7 @@
 
 void FakeBluetoothGattDescriptorServiceProvider::GetValue(
     const dbus::ObjectPath& device_path,
-    device::BluetoothLocalGattService::Delegate::ValueCallback callback,
-    device::BluetoothLocalGattService::Delegate::ErrorCallback error_callback) {
+    device::BluetoothLocalGattService::Delegate::ValueCallback callback) {
   DVLOG(1) << "GATT descriptor value Get request: " << object_path_.value()
            << " UUID: " << uuid_;
   // Check if this descriptor is registered.
@@ -116,20 +115,22 @@
   if (!fake_bluetooth_gatt_manager_client->IsServiceRegistered(
           characteristic->service_path())) {
     DVLOG(1) << "GATT descriptor not registered.";
-    std::move(error_callback).Run();
+    std::move(callback).Run(
+        device::BluetoothGattService::GattErrorCode::GATT_ERROR_FAILED,
+        /*value=*/std::vector<uint8_t>());
     return;
   }
 
   if (!CanRead(flags_)) {
-    DVLOG(1) << "GATT descriptor not readable.";
-    std::move(error_callback).Run();
+    std::move(callback).Run(
+        device::BluetoothGattService::GattErrorCode::GATT_ERROR_FAILED,
+        /*value=*/std::vector<uint8_t>());
     return;
   }
 
   // Pass on to the delegate.
   DCHECK(delegate_);
-  delegate_->GetValue(device_path, std::move(callback),
-                      std::move(error_callback));
+  delegate_->GetValue(device_path, std::move(callback));
 }
 
 void FakeBluetoothGattDescriptorServiceProvider::SetValue(
diff --git a/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_service_provider.h b/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_service_provider.h
index 785a999..a1aa918 100644
--- a/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_service_provider.h
+++ b/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_service_provider.h
@@ -41,9 +41,7 @@
   // GATT manager.
   void GetValue(
       const dbus::ObjectPath& device_path,
-      device::BluetoothLocalGattService::Delegate::ValueCallback callback,
-      device::BluetoothLocalGattService::Delegate::ErrorCallback
-          error_callback);
+      device::BluetoothLocalGattService::Delegate::ValueCallback callback);
   void SetValue(const dbus::ObjectPath& device_path,
                 const std::vector<uint8_t>& value,
                 base::OnceClosure callback,
diff --git a/device/bluetooth/device.cc b/device/bluetooth/device.cc
index a922046..aee2806 100644
--- a/device/bluetooth/device.cc
+++ b/device/bluetooth/device.cc
@@ -152,14 +152,9 @@
     return;
   }
 
-  auto split_callback = base::SplitOnceCallback(std::move(callback));
   characteristic->ReadRemoteCharacteristic(
       base::BindOnce(&Device::OnReadRemoteCharacteristic,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     std::move(split_callback.first)),
-      base::BindOnce(&Device::OnReadRemoteCharacteristicError,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     std::move(split_callback.second)));
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void Device::WriteValueForCharacteristic(
@@ -264,14 +259,9 @@
     return;
   }
 
-  auto split_callback = base::SplitOnceCallback(std::move(callback));
   descriptor->ReadRemoteDescriptor(
       base::BindOnce(&Device::OnReadRemoteDescriptor,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     std::move(split_callback.first)),
-      base::BindOnce(&Device::OnReadRemoteDescriptorError,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     std::move(split_callback.second)));
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void Device::WriteValueForDescriptor(const std::string& service_id,
@@ -347,17 +337,18 @@
 
 void Device::OnReadRemoteCharacteristic(
     ReadValueForCharacteristicCallback callback,
+    base::Optional<device::BluetoothRemoteGattService::GattErrorCode>
+        error_code,
     const std::vector<uint8_t>& value) {
+  if (error_code.has_value()) {
+    std::move(callback).Run(
+        mojo::ConvertTo<mojom::GattResult>(error_code.value()),
+        base::nullopt /* value */);
+    return;
+  }
   std::move(callback).Run(mojom::GattResult::SUCCESS, std::move(value));
 }
 
-void Device::OnReadRemoteCharacteristicError(
-    ReadValueForCharacteristicCallback callback,
-    device::BluetoothGattService::GattErrorCode error_code) {
-  std::move(callback).Run(mojo::ConvertTo<mojom::GattResult>(error_code),
-                          base::nullopt /* value */);
-}
-
 void Device::OnWriteRemoteCharacteristic(
     WriteValueForCharacteristicCallback callback) {
   std::move(callback).Run(mojom::GattResult::SUCCESS);
@@ -369,16 +360,18 @@
   std::move(callback).Run(mojo::ConvertTo<mojom::GattResult>(error_code));
 }
 
-void Device::OnReadRemoteDescriptor(ReadValueForDescriptorCallback callback,
-                                    const std::vector<uint8_t>& value) {
-  std::move(callback).Run(mojom::GattResult::SUCCESS, std::move(value));
-}
-
-void Device::OnReadRemoteDescriptorError(
+void Device::OnReadRemoteDescriptor(
     ReadValueForDescriptorCallback callback,
-    device::BluetoothGattService::GattErrorCode error_code) {
-  std::move(callback).Run(mojo::ConvertTo<mojom::GattResult>(error_code),
-                          base::nullopt /* value */);
+    base::Optional<device::BluetoothRemoteGattService::GattErrorCode>
+        error_code,
+    const std::vector<uint8_t>& value) {
+  if (error_code.has_value()) {
+    std::move(callback).Run(
+        mojo::ConvertTo<mojom::GattResult>(error_code.value()),
+        /*value=*/base::nullopt);
+    return;
+  }
+  std::move(callback).Run(mojom::GattResult::SUCCESS, std::move(value));
 }
 
 void Device::OnWriteRemoteDescriptor(WriteValueForDescriptorCallback callback) {
diff --git a/device/bluetooth/device.h b/device/bluetooth/device.h
index 48f407c4..b5e2f24 100644
--- a/device/bluetooth/device.h
+++ b/device/bluetooth/device.h
@@ -87,12 +87,11 @@
   mojom::ServiceInfoPtr ConstructServiceInfoStruct(
       const device::BluetoothRemoteGattService& service);
 
-  void OnReadRemoteCharacteristic(ReadValueForCharacteristicCallback callback,
-                                  const std::vector<uint8_t>& value);
-
-  void OnReadRemoteCharacteristicError(
+  void OnReadRemoteCharacteristic(
       ReadValueForCharacteristicCallback callback,
-      device::BluetoothGattService::GattErrorCode error_code);
+      base::Optional<device::BluetoothRemoteGattService::GattErrorCode>
+          error_code,
+      const std::vector<uint8_t>& value);
 
   void OnWriteRemoteCharacteristic(
       WriteValueForCharacteristicCallback callback);
@@ -101,12 +100,11 @@
       WriteValueForCharacteristicCallback callback,
       device::BluetoothGattService::GattErrorCode error_code);
 
-  void OnReadRemoteDescriptor(ReadValueForDescriptorCallback callback,
-                              const std::vector<uint8_t>& value);
-
-  void OnReadRemoteDescriptorError(
+  void OnReadRemoteDescriptor(
       ReadValueForDescriptorCallback callback,
-      device::BluetoothGattService::GattErrorCode error_code);
+      base::Optional<device::BluetoothRemoteGattService::GattErrorCode>
+          error_code,
+      const std::vector<uint8_t>& value);
 
   void OnWriteRemoteDescriptor(WriteValueForDescriptorCallback callback);
 
diff --git a/device/bluetooth/test/bluetooth_test.cc b/device/bluetooth/test/bluetooth_test.cc
index dc30a57..0d8567b 100644
--- a/device/bluetooth/test/bluetooth_test.cc
+++ b/device/bluetooth/test/bluetooth_test.cc
@@ -311,15 +311,35 @@
   ++actual_success_callback_calls_;
 }
 
-void BluetoothTestBase::ReadValueCallback(Call expected,
-                                          const std::vector<uint8_t>& value) {
-  ++callback_count_;
+void BluetoothTestBase::ReadValueCallback(
+    Call expected,
+    Result expected_result,
+    base::Optional<BluetoothGattService::GattErrorCode> error_code,
+    const std::vector<uint8_t>& value) {
+  if (expected_result == Result::FAILURE) {
+    if (error_code.has_value())
+      read_results_.failure.actual++;
+    else
+      read_results_.success.unexpected++;
+  } else {
+    if (!error_code.has_value())
+      read_results_.success.actual++;
+    else
+      read_results_.failure.unexpected++;
+  }
   last_read_value_ = value;
 
+  if (error_code.has_value()) {
+    error_callback_count_++;
+    last_gatt_error_code_ = error_code.value();
+  } else {
+    callback_count_++;
+  }
+
   if (expected == Call::EXPECTED)
-    ++actual_success_callback_calls_;
+    read_callback_calls_.actual++;
   else
-    unexpected_success_callback_ = true;
+    read_callback_calls_.unexpected++;
 }
 
 void BluetoothTestBase::ErrorCallback(Call expected) {
@@ -469,11 +489,16 @@
 }
 
 BluetoothRemoteGattCharacteristic::ValueCallback
-BluetoothTestBase::GetReadValueCallback(Call expected) {
-  if (expected == Call::EXPECTED)
-    ++expected_success_callback_calls_;
+BluetoothTestBase::GetReadValueCallback(Call expected, Result expected_result) {
+  if (expected == Call::EXPECTED) {
+    read_callback_calls_.expected++;
+    if (expected_result == Result::SUCCESS)
+      read_results_.success.expected++;
+    else
+      read_results_.failure.expected++;
+  }
   return base::BindOnce(&BluetoothTestBase::ReadValueCallback,
-                        weak_factory_.GetWeakPtr(), expected);
+                        weak_factory_.GetWeakPtr(), expected, expected_result);
 }
 
 BluetoothAdapter::ErrorCallback BluetoothTestBase::GetErrorCallback(
diff --git a/device/bluetooth/test/bluetooth_test.h b/device/bluetooth/test/bluetooth_test.h
index 6308c40..871d739 100644
--- a/device/bluetooth/test/bluetooth_test.h
+++ b/device/bluetooth/test/bluetooth_test.h
@@ -43,6 +43,8 @@
  public:
   enum class Call { EXPECTED, NOT_EXPECTED };
 
+  enum class Result { SUCCESS, FAILURE };
+
   // List of devices that can be simulated with
   // SimulateConnectedLowEnergyDevice().
   // GENERIC_DEVICE:
@@ -486,8 +488,7 @@
   virtual void SimulateLocalGattCharacteristicValueReadRequest(
       BluetoothDevice* from_device,
       BluetoothLocalGattCharacteristic* characteristic,
-      BluetoothLocalGattService::Delegate::ValueCallback value_callback,
-      base::OnceClosure error_callback) {}
+      BluetoothLocalGattService::Delegate::ValueCallback value_callback) {}
 
   // Simulates write a value to a locally hosted GATT characteristic by a
   // remote central device.
@@ -515,8 +516,7 @@
   virtual void SimulateLocalGattDescriptorValueReadRequest(
       BluetoothDevice* from_device,
       BluetoothLocalGattDescriptor* descriptor,
-      BluetoothLocalGattService::Delegate::ValueCallback value_callback,
-      base::OnceClosure error_callback) {}
+      BluetoothLocalGattService::Delegate::ValueCallback value_callback) {}
 
   // Simulates write a value to a locally hosted GATT descriptor by a
   // remote central device.
@@ -616,7 +616,11 @@
       std::unique_ptr<BluetoothGattNotifySession>);
   void StopNotifyCallback(Call expected);
   void StopNotifyCheckForPrecedingCalls(int num_of_preceding_calls);
-  void ReadValueCallback(Call expected, const std::vector<uint8_t>& value);
+  void ReadValueCallback(
+      Call expected,
+      Result expected_result,
+      base::Optional<BluetoothGattService::GattErrorCode> error_code,
+      const std::vector<uint8_t>& value);
   void ErrorCallback(Call expected);
   void AdvertisementErrorCallback(Call expected,
                                   BluetoothAdvertisement::ErrorCode error_code);
@@ -650,7 +654,8 @@
   base::OnceClosure GetStopNotifyCheckForPrecedingCalls(
       int num_of_preceding_calls);
   BluetoothRemoteGattCharacteristic::ValueCallback GetReadValueCallback(
-      Call expected);
+      Call expected,
+      Result expected_result);
   BluetoothAdapter::ErrorCallback GetErrorCallback(Call expected);
   BluetoothAdapter::AdvertisementErrorCallback GetAdvertisementErrorCallback(
       Call expected);
@@ -673,6 +678,19 @@
   void RemoveTimedOutDevices();
 
  protected:
+  // The expected/actual counts for tests.
+  struct EventCounts {
+    int unexpected = 0;
+    int expected = 0;
+    int actual = 0;
+  };
+
+  // Counts for an expected metric being tested.
+  struct ResultCounts {
+    EventCounts success;
+    EventCounts failure;
+  };
+
   // Utility method to simplify creading a low energy device of a given
   // |device_ordinal|.
   LowEnergyDeviceData GetLowEnergyDeviceData(int device_ordinal) const;
@@ -692,7 +710,8 @@
   std::vector<std::unique_ptr<BluetoothGattNotifySession>> notify_sessions_;
   std::vector<uint8_t> last_read_value_;
   std::vector<uint8_t> last_write_value_;
-  BluetoothRemoteGattService::GattErrorCode last_gatt_error_code_;
+  BluetoothRemoteGattService::GattErrorCode last_gatt_error_code_ =
+      BluetoothRemoteGattService::GATT_ERROR_UNKNOWN;
 
   int callback_count_ = 0;
   int error_callback_count_ = 0;
@@ -714,6 +733,9 @@
   bool unexpected_success_callback_ = false;
   bool unexpected_error_callback_ = false;
 
+  EventCounts read_callback_calls_;
+  ResultCounts read_results_;
+
   base::WeakPtrFactory<BluetoothTestBase> weak_factory_{this};
 };
 
diff --git a/device/bluetooth/test/bluetooth_test_bluez.cc b/device/bluetooth/test/bluetooth_test_bluez.cc
index 5f2c9e17..b23d581 100644
--- a/device/bluetooth/test/bluetooth_test_bluez.cc
+++ b/device/bluetooth/test/bluetooth_test_bluez.cc
@@ -35,8 +35,9 @@
 void GetValueCallback(
     base::OnceClosure quit_closure,
     BluetoothLocalGattService::Delegate::ValueCallback value_callback,
+    base::Optional<BluetoothGattService::GattErrorCode> error_code,
     const std::vector<uint8_t>& value) {
-  std::move(value_callback).Run(value);
+  std::move(value_callback).Run(error_code, value);
   std::move(quit_closure).Run();
 }
 
@@ -160,8 +161,7 @@
 void BluetoothTestBlueZ::SimulateLocalGattCharacteristicValueReadRequest(
     BluetoothDevice* from_device,
     BluetoothLocalGattCharacteristic* characteristic,
-    BluetoothLocalGattService::Delegate::ValueCallback value_callback,
-    base::OnceClosure error_callback) {
+    BluetoothLocalGattService::Delegate::ValueCallback value_callback) {
   bluez::BluetoothLocalGattCharacteristicBlueZ* characteristic_bluez =
       static_cast<bluez::BluetoothLocalGattCharacteristicBlueZ*>(
           characteristic);
@@ -184,9 +184,7 @@
   characteristic_provider->GetValue(
       GetDevicePath(from_device),
       base::BindOnce(&GetValueCallback, run_loop.QuitClosure(),
-                     std::move(value_callback)),
-      base::BindOnce(&ClosureCallback, run_loop.QuitClosure(),
-                     std::move(error_callback)));
+                     std::move(value_callback)));
   run_loop.Run();
 }
 
@@ -264,8 +262,7 @@
 void BluetoothTestBlueZ::SimulateLocalGattDescriptorValueReadRequest(
     BluetoothDevice* from_device,
     BluetoothLocalGattDescriptor* descriptor,
-    BluetoothLocalGattService::Delegate::ValueCallback value_callback,
-    base::OnceClosure error_callback) {
+    BluetoothLocalGattService::Delegate::ValueCallback value_callback) {
   bluez::BluetoothLocalGattDescriptorBlueZ* descriptor_bluez =
       static_cast<bluez::BluetoothLocalGattDescriptorBlueZ*>(descriptor);
   bluez::FakeBluetoothGattManagerClient* fake_bluetooth_gatt_manager_client =
@@ -286,9 +283,7 @@
   descriptor_provider->GetValue(
       GetDevicePath(from_device),
       base::BindOnce(&GetValueCallback, run_loop.QuitClosure(),
-                     std::move(value_callback)),
-      base::BindOnce(&ClosureCallback, run_loop.QuitClosure(),
-                     std::move(error_callback)));
+                     std::move(value_callback)));
   run_loop.Run();
 }
 
diff --git a/device/bluetooth/test/bluetooth_test_bluez.h b/device/bluetooth/test/bluetooth_test_bluez.h
index 8497892..f53796a 100644
--- a/device/bluetooth/test/bluetooth_test_bluez.h
+++ b/device/bluetooth/test/bluetooth_test_bluez.h
@@ -39,8 +39,8 @@
   void SimulateLocalGattCharacteristicValueReadRequest(
       BluetoothDevice* from_device,
       BluetoothLocalGattCharacteristic* characteristic,
-      BluetoothLocalGattService::Delegate::ValueCallback value_callback,
-      base::OnceClosure error_callback) override;
+      BluetoothLocalGattService::Delegate::ValueCallback value_callback)
+      override;
   void SimulateLocalGattCharacteristicValueWriteRequest(
       BluetoothDevice* from_device,
       BluetoothLocalGattCharacteristic* characteristic,
@@ -58,8 +58,8 @@
   void SimulateLocalGattDescriptorValueReadRequest(
       BluetoothDevice* from_device,
       BluetoothLocalGattDescriptor* descriptor,
-      BluetoothLocalGattService::Delegate::ValueCallback value_callback,
-      base::OnceClosure error_callback) override;
+      BluetoothLocalGattService::Delegate::ValueCallback value_callback)
+      override;
   void SimulateLocalGattDescriptorValueWriteRequest(
       BluetoothDevice* from_device,
       BluetoothLocalGattDescriptor* descriptor,
diff --git a/device/bluetooth/test/fake_remote_gatt_characteristic.cc b/device/bluetooth/test/fake_remote_gatt_characteristic.cc
index 64f025f..658b0d9 100644
--- a/device/bluetooth/test/fake_remote_gatt_characteristic.cc
+++ b/device/bluetooth/test/fake_remote_gatt_characteristic.cc
@@ -129,13 +129,11 @@
 }
 
 void FakeRemoteGattCharacteristic::ReadRemoteCharacteristic(
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(&FakeRemoteGattCharacteristic::DispatchReadResponse,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
-                     std::move(error_callback)));
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void FakeRemoteGattCharacteristic::WriteRemoteCharacteristic(
@@ -231,8 +229,7 @@
 }
 
 void FakeRemoteGattCharacteristic::DispatchReadResponse(
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   DCHECK(next_read_response_);
   uint16_t gatt_code = next_read_response_->gatt_code();
   base::Optional<std::vector<uint8_t>> value = next_read_response_->value();
@@ -242,12 +239,12 @@
     case mojom::kGATTSuccess:
       DCHECK(value);
       value_ = std::move(value.value());
-      std::move(callback).Run(value_);
+      std::move(callback).Run(base::nullopt, value_);
       break;
     case mojom::kGATTInvalidHandle:
       DCHECK(!value);
-      std::move(error_callback)
-          .Run(device::BluetoothGattService::GATT_ERROR_FAILED);
+      std::move(callback).Run(device::BluetoothGattService::GATT_ERROR_FAILED,
+                              std::vector<uint8_t>());
       break;
     default:
       NOTREACHED();
diff --git a/device/bluetooth/test/fake_remote_gatt_characteristic.h b/device/bluetooth/test/fake_remote_gatt_characteristic.h
index c6071e9e..53e1e80a 100644
--- a/device/bluetooth/test/fake_remote_gatt_characteristic.h
+++ b/device/bluetooth/test/fake_remote_gatt_characteristic.h
@@ -88,8 +88,7 @@
   // device::BluetoothRemoteGattCharacteristic overrides:
   const std::vector<uint8_t>& GetValue() const override;
   device::BluetoothRemoteGattService* GetService() const override;
-  void ReadRemoteCharacteristic(ValueCallback callback,
-                                ErrorCallback error_callback) override;
+  void ReadRemoteCharacteristic(ValueCallback callback) override;
   void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
                                  WriteType write_type,
                                  base::OnceClosure callback,
@@ -125,8 +124,7 @@
       ErrorCallback error_callback) override;
 
  private:
-  void DispatchReadResponse(ValueCallback callback,
-                            ErrorCallback error_callback);
+  void DispatchReadResponse(ValueCallback callback);
   void DispatchWriteResponse(base::OnceClosure callback,
                              ErrorCallback error_callback,
                              const std::vector<uint8_t>& value,
diff --git a/device/bluetooth/test/fake_remote_gatt_descriptor.cc b/device/bluetooth/test/fake_remote_gatt_descriptor.cc
index 74b5801f..58573e7 100644
--- a/device/bluetooth/test/fake_remote_gatt_descriptor.cc
+++ b/device/bluetooth/test/fake_remote_gatt_descriptor.cc
@@ -62,14 +62,11 @@
   return characteristic_;
 }
 
-void FakeRemoteGattDescriptor::ReadRemoteDescriptor(
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+void FakeRemoteGattDescriptor::ReadRemoteDescriptor(ValueCallback callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(&FakeRemoteGattDescriptor::DispatchReadResponse,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
-                     std::move(error_callback)));
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void FakeRemoteGattDescriptor::WriteRemoteDescriptor(
@@ -83,24 +80,25 @@
                      std::move(error_callback), value));
 }
 
-void FakeRemoteGattDescriptor::DispatchReadResponse(
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+void FakeRemoteGattDescriptor::DispatchReadResponse(ValueCallback callback) {
   DCHECK(next_read_response_);
   uint16_t gatt_code = next_read_response_->gatt_code();
   base::Optional<std::vector<uint8_t>> value = next_read_response_->value();
   next_read_response_.reset();
 
-  if (gatt_code == mojom::kGATTSuccess) {
-    DCHECK(value);
-    value_ = std::move(value.value());
-    std::move(callback).Run(value_);
-    return;
-  } else if (gatt_code == mojom::kGATTInvalidHandle) {
-    DCHECK(!value);
-    std::move(error_callback)
-        .Run(device::BluetoothGattService::GATT_ERROR_FAILED);
-    return;
+  switch (gatt_code) {
+    case mojom::kGATTSuccess:
+      DCHECK(value);
+      value_ = std::move(value.value());
+      std::move(callback).Run(/*error_code=*/base::nullopt, value_);
+      break;
+    case mojom::kGATTInvalidHandle:
+      DCHECK(!value);
+      std::move(callback).Run(device::BluetoothGattService::GATT_ERROR_FAILED,
+                              /*value=*/std::vector<uint8_t>());
+      break;
+    default:
+      NOTREACHED();
   }
 }
 
diff --git a/device/bluetooth/test/fake_remote_gatt_descriptor.h b/device/bluetooth/test/fake_remote_gatt_descriptor.h
index 0516fc1..f6e1bf2 100644
--- a/device/bluetooth/test/fake_remote_gatt_descriptor.h
+++ b/device/bluetooth/test/fake_remote_gatt_descriptor.h
@@ -57,15 +57,13 @@
   // device::BluetoothRemoteGattDescriptor overrides:
   const std::vector<uint8_t>& GetValue() const override;
   device::BluetoothRemoteGattCharacteristic* GetCharacteristic() const override;
-  void ReadRemoteDescriptor(ValueCallback callback,
-                            ErrorCallback error_callback) override;
+  void ReadRemoteDescriptor(ValueCallback callback) override;
   void WriteRemoteDescriptor(const std::vector<uint8_t>& value,
                              base::OnceClosure callback,
                              ErrorCallback error_callback) override;
 
  private:
-  void DispatchReadResponse(ValueCallback callback,
-                            ErrorCallback error_callback);
+  void DispatchReadResponse(ValueCallback callback);
 
   void DispatchWriteResponse(base::OnceClosure callback,
                              ErrorCallback error_callback,
diff --git a/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h b/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h
index e301b745..49e36bb5 100644
--- a/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h
+++ b/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h
@@ -68,10 +68,10 @@
   }
   MOCK_METHOD2(StopNotifySession_,
                void(BluetoothGattNotifySession*, base::OnceClosure&));
-  void ReadRemoteCharacteristic(ValueCallback c, ErrorCallback ec) override {
-    ReadRemoteCharacteristic_(c, ec);
+  void ReadRemoteCharacteristic(ValueCallback c) override {
+    ReadRemoteCharacteristic_(c);
   }
-  MOCK_METHOD2(ReadRemoteCharacteristic_, void(ValueCallback&, ErrorCallback&));
+  MOCK_METHOD1(ReadRemoteCharacteristic_, void(ValueCallback&));
   void WriteRemoteCharacteristic(const std::vector<uint8_t>& v,
                                  WriteType t,
                                  base::OnceClosure c,
diff --git a/device/bluetooth/test/mock_bluetooth_gatt_descriptor.h b/device/bluetooth/test/mock_bluetooth_gatt_descriptor.h
index e1de291..06af5cb 100644
--- a/device/bluetooth/test/mock_bluetooth_gatt_descriptor.h
+++ b/device/bluetooth/test/mock_bluetooth_gatt_descriptor.h
@@ -36,10 +36,10 @@
   MOCK_CONST_METHOD0(GetCharacteristic, BluetoothRemoteGattCharacteristic*());
   MOCK_CONST_METHOD0(GetPermissions,
                      BluetoothRemoteGattCharacteristic::Permissions());
-  void ReadRemoteDescriptor(ValueCallback c, ErrorCallback ec) override {
-    ReadRemoteDescriptor_(c, ec);
+  void ReadRemoteDescriptor(ValueCallback c) override {
+    ReadRemoteDescriptor_(c);
   }
-  MOCK_METHOD2(ReadRemoteDescriptor_, void(ValueCallback&, ErrorCallback&));
+  MOCK_METHOD1(ReadRemoteDescriptor_, void(ValueCallback&));
   void WriteRemoteDescriptor(const std::vector<uint8_t>& v,
                              base::OnceClosure c,
                              ErrorCallback ec) override {
diff --git a/device/bluetooth/test/test_bluetooth_local_gatt_service_delegate.cc b/device/bluetooth/test/test_bluetooth_local_gatt_service_delegate.cc
index e637730..824388c6 100644
--- a/device/bluetooth/test/test_bluetooth_local_gatt_service_delegate.cc
+++ b/device/bluetooth/test/test_bluetooth_local_gatt_service_delegate.cc
@@ -24,16 +24,17 @@
     const BluetoothDevice* device,
     const BluetoothLocalGattCharacteristic* characteristic,
     int offset,
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   EXPECT_EQ(expected_characteristic_->GetIdentifier(),
             characteristic->GetIdentifier());
   if (should_fail_) {
-    std::move(error_callback).Run();
+    std::move(callback).Run(BluetoothGattService::GATT_ERROR_FAILED,
+                            /*value=*/std::vector<uint8_t>());
     return;
   }
   last_seen_device_ = device->GetIdentifier();
-  std::move(callback).Run(BluetoothGattServerTest::GetValue(value_to_write_));
+  std::move(callback).Run(/*error_code=*/base::nullopt,
+                          BluetoothGattServerTest::GetValue(value_to_write_));
 }
 
 void TestBluetoothLocalGattServiceDelegate::OnCharacteristicWriteRequest(
@@ -81,15 +82,16 @@
     const BluetoothDevice* device,
     const BluetoothLocalGattDescriptor* descriptor,
     int offset,
-    ValueCallback callback,
-    ErrorCallback error_callback) {
+    ValueCallback callback) {
   EXPECT_EQ(expected_descriptor_->GetIdentifier(), descriptor->GetIdentifier());
   if (should_fail_) {
-    std::move(error_callback).Run();
+    std::move(callback).Run(BluetoothRemoteGattService::GATT_ERROR_FAILED,
+                            /*value=*/std::vector<uint8_t>());
     return;
   }
   last_seen_device_ = device->GetIdentifier();
-  std::move(callback).Run(BluetoothGattServerTest::GetValue(value_to_write_));
+  std::move(callback).Run(/*error_code=*/base::nullopt,
+                          BluetoothGattServerTest::GetValue(value_to_write_));
 }
 
 void TestBluetoothLocalGattServiceDelegate::OnDescriptorWriteRequest(
diff --git a/device/bluetooth/test/test_bluetooth_local_gatt_service_delegate.h b/device/bluetooth/test/test_bluetooth_local_gatt_service_delegate.h
index 3a0189cf..4be93a9c 100644
--- a/device/bluetooth/test/test_bluetooth_local_gatt_service_delegate.h
+++ b/device/bluetooth/test/test_bluetooth_local_gatt_service_delegate.h
@@ -27,8 +27,7 @@
       const BluetoothDevice* device,
       const BluetoothLocalGattCharacteristic* characteristic,
       int offset,
-      ValueCallback callback,
-      ErrorCallback error_callback) override;
+      ValueCallback callback) override;
   void OnCharacteristicWriteRequest(
       const BluetoothDevice* device,
       const BluetoothLocalGattCharacteristic* characteristic,
@@ -47,8 +46,7 @@
   void OnDescriptorReadRequest(const BluetoothDevice* device,
                                const BluetoothLocalGattDescriptor* descriptor,
                                int offset,
-                               ValueCallback callback,
-                               ErrorCallback error_callback) override;
+                               ValueCallback callback) override;
 
   void OnDescriptorWriteRequest(const BluetoothDevice* device,
                                 const BluetoothLocalGattDescriptor* descriptor,
diff --git a/device/fido/cable/fido_ble_connection.cc b/device/fido/cable/fido_ble_connection.cc
index 6b94db1..9228d17 100644
--- a/device/fido/cable/fido_ble_connection.cc
+++ b/device/fido/cable/fido_ble_connection.cc
@@ -106,8 +106,17 @@
   std::move(callback).Run(false);
 }
 
-void OnReadServiceRevisionBitfield(ServiceRevisionsCallback callback,
-                                   const std::vector<uint8_t>& value) {
+void OnReadServiceRevisionBitfield(
+    ServiceRevisionsCallback callback,
+    base::Optional<device::BluetoothRemoteGattService::GattErrorCode>
+        error_code,
+    const std::vector<uint8_t>& value) {
+  if (error_code.has_value()) {
+    FIDO_LOG(ERROR) << "Error while reading Service Revision Bitfield: "
+                    << ToString(error_code.value());
+    std::move(callback).Run({});
+    return;
+  }
   if (value.empty()) {
     FIDO_LOG(DEBUG) << "Service Revision Bitfield is empty.";
     std::move(callback).Run({});
@@ -139,14 +148,6 @@
   std::move(callback).Run(std::move(service_revisions));
 }
 
-void OnReadServiceRevisionBitfieldError(
-    ServiceRevisionsCallback callback,
-    BluetoothGattService::GattErrorCode error_code) {
-  FIDO_LOG(ERROR) << "Error while reading Service Revision Bitfield: "
-                  << ToString(error_code);
-  std::move(callback).Run({});
-}
-
 }  // namespace
 
 FidoBleConnection::FidoBleConnection(BluetoothAdapter* adapter,
@@ -220,12 +221,8 @@
   }
 
   FIDO_LOG(DEBUG) << "Read Control Point Length";
-  // Work around legacy APIs. Only one of the callbacks to
-  // ReadRemoteCharacteristic() gets invoked, but we don't know which one.
-  auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
   control_point_length->ReadRemoteCharacteristic(
-      base::BindOnce(OnReadControlPointLength, copyable_callback),
-      base::BindOnce(OnReadControlPointLengthError, copyable_callback));
+      base::BindOnce(OnReadControlPointLength, std::move(callback)));
 }
 
 void FidoBleConnection::WriteControlPoint(const std::vector<uint8_t>& data,
@@ -371,8 +368,7 @@
         &FidoBleConnection::OnReadServiceRevisions, weak_factory_.GetWeakPtr());
     fido_service->GetCharacteristic(*service_revision_bitfield_id_)
         ->ReadRemoteCharacteristic(
-            base::BindOnce(OnReadServiceRevisionBitfield, callback),
-            base::BindOnce(OnReadServiceRevisionBitfieldError, callback));
+            base::BindOnce(OnReadServiceRevisionBitfield, callback));
     return;
   }
 
@@ -518,7 +514,15 @@
 // static
 void FidoBleConnection::OnReadControlPointLength(
     ControlPointLengthCallback callback,
+    base::Optional<device::BluetoothRemoteGattService::GattErrorCode>
+        error_code,
     const std::vector<uint8_t>& value) {
+  if (error_code.has_value()) {
+    FIDO_LOG(ERROR) << "Error reading Control Point Length: "
+                    << ToString(error_code.value());
+    std::move(callback).Run(base::nullopt);
+    return;
+  }
   if (value.size() != 2) {
     FIDO_LOG(ERROR) << "Wrong Control Point Length: " << value.size()
                     << " bytes";
@@ -531,13 +535,4 @@
   std::move(callback).Run(length);
 }
 
-// static
-void FidoBleConnection::OnReadControlPointLengthError(
-    ControlPointLengthCallback callback,
-    BluetoothGattService::GattErrorCode error_code) {
-  FIDO_LOG(ERROR) << "Error reading Control Point Length: "
-                  << ToString(error_code);
-  std::move(callback).Run(base::nullopt);
-}
-
 }  // namespace device
diff --git a/device/fido/cable/fido_ble_connection.h b/device/fido/cable/fido_ble_connection.h
index ebc940a..b6b8fef 100644
--- a/device/fido/cable/fido_ble_connection.h
+++ b/device/fido/cable/fido_ble_connection.h
@@ -105,11 +105,11 @@
   void OnStartNotifySessionError(
       BluetoothGattService::GattErrorCode error_code);
 
-  static void OnReadControlPointLength(ControlPointLengthCallback callback,
-                                       const std::vector<uint8_t>& value);
-  static void OnReadControlPointLengthError(
+  static void OnReadControlPointLength(
       ControlPointLengthCallback callback,
-      BluetoothGattService::GattErrorCode error_code);
+      base::Optional<device::BluetoothRemoteGattService::GattErrorCode>
+          error_code,
+      const std::vector<uint8_t>& value);
 
   std::unique_ptr<BluetoothGattConnection> connection_;
   std::unique_ptr<BluetoothGattNotifySession> notify_session_;
diff --git a/device/fido/cable/fido_ble_connection_unittest.cc b/device/fido/cable/fido_ble_connection_unittest.cc
index 333d601..4ad4d13 100644
--- a/device/fido/cable/fido_ble_connection_unittest.cc
+++ b/device/fido/cable/fido_ble_connection_unittest.cc
@@ -157,10 +157,10 @@
 
     ON_CALL(*fido_service_revision_bitfield_, ReadRemoteCharacteristic_)
         .WillByDefault(Invoke(
-            [=](BluetoothRemoteGattCharacteristic::ValueCallback& callback,
-                BluetoothRemoteGattCharacteristic::ErrorCallback&) {
+            [=](BluetoothRemoteGattCharacteristic::ValueCallback& callback) {
               base::ThreadTaskRunnerHandle::Get()->PostTask(
                   FROM_HERE, base::BindOnce(std::move(callback),
+                                            /*error_code=*/base::nullopt,
                                             std::vector<uint8_t>(
                                                 {kDefaultServiceRevision})));
             }));
@@ -231,54 +231,48 @@
 
   void SetNextReadControlPointLengthReponse(bool success,
                                             const std::vector<uint8_t>& value) {
-    EXPECT_CALL(*fido_control_point_length_, ReadRemoteCharacteristic_(_, _))
-        .WillOnce(Invoke(
-            [success, value](
-                BluetoothRemoteGattCharacteristic::ValueCallback& callback,
-                BluetoothRemoteGattCharacteristic::ErrorCallback&
-                    error_callback) {
-              base::ThreadTaskRunnerHandle::Get()->PostTask(
-                  FROM_HERE,
-                  success ? base::BindOnce(std::move(callback), value)
-                          : base::BindOnce(
-                                std::move(error_callback),
-                                BluetoothGattService::GATT_ERROR_FAILED));
-            }));
+    EXPECT_CALL(*fido_control_point_length_, ReadRemoteCharacteristic_(_))
+        .WillOnce(Invoke([success, value](
+                             BluetoothRemoteGattCharacteristic::ValueCallback&
+                                 callback) {
+          base::Optional<BluetoothRemoteGattService::GattErrorCode> error_code;
+          if (!success)
+            error_code = BluetoothRemoteGattService::GATT_ERROR_FAILED;
+          base::ThreadTaskRunnerHandle::Get()->PostTask(
+              FROM_HERE,
+              base::BindOnce(std::move(callback), error_code, value));
+        }));
   }
 
   void SetNextReadServiceRevisionResponse(bool success,
                                           const std::vector<uint8_t>& value) {
-    EXPECT_CALL(*fido_service_revision_, ReadRemoteCharacteristic_(_, _))
-        .WillOnce(Invoke(
-            [success, value](
-                BluetoothRemoteGattCharacteristic::ValueCallback& callback,
-                BluetoothRemoteGattCharacteristic::ErrorCallback&
-                    error_callback) {
-              base::ThreadTaskRunnerHandle::Get()->PostTask(
-                  FROM_HERE,
-                  success ? base::BindOnce(std::move(callback), value)
-                          : base::BindOnce(
-                                std::move(error_callback),
-                                BluetoothGattService::GATT_ERROR_FAILED));
-            }));
+    EXPECT_CALL(*fido_service_revision_, ReadRemoteCharacteristic_(_))
+        .WillOnce(Invoke([success, value](
+                             BluetoothRemoteGattCharacteristic::ValueCallback&
+                                 callback) {
+          base::Optional<BluetoothRemoteGattService::GattErrorCode> error_code;
+          if (!success)
+            error_code = BluetoothRemoteGattService::GATT_ERROR_FAILED;
+          base::ThreadTaskRunnerHandle::Get()->PostTask(
+              FROM_HERE,
+              base::BindOnce(std::move(callback), error_code, value));
+        }));
   }
 
   void SetNextReadServiceRevisionBitfieldResponse(
       bool success,
       const std::vector<uint8_t>& value) {
-    EXPECT_CALL(*fido_service_revision_bitfield_,
-                ReadRemoteCharacteristic_(_, _))
+    EXPECT_CALL(*fido_service_revision_bitfield_, ReadRemoteCharacteristic_(_))
         .WillOnce(Invoke(
             [success, value](
-                BluetoothRemoteGattCharacteristic::ValueCallback& callback,
-                BluetoothRemoteGattCharacteristic::ErrorCallback&
-                    error_callback) {
+                BluetoothRemoteGattCharacteristic::ValueCallback& callback) {
+              auto error_code =
+                  success ? base::nullopt
+                          : base::make_optional(
+                                BluetoothRemoteGattService::GATT_ERROR_FAILED);
               base::ThreadTaskRunnerHandle::Get()->PostTask(
                   FROM_HERE,
-                  success ? base::BindOnce(std::move(callback), value)
-                          : base::BindOnce(
-                                std::move(error_callback),
-                                BluetoothGattService::GATT_ERROR_FAILED));
+                  base::BindOnce(std::move(callback), error_code, value));
             }));
   }
 
diff --git a/docs/speed/addressing_performance_regressions.md b/docs/speed/addressing_performance_regressions.md
index c4b8fcc..3c3fdcee 100644
--- a/docs/speed/addressing_performance_regressions.md
+++ b/docs/speed/addressing_performance_regressions.md
@@ -18,6 +18,8 @@
 [this spreadsheet](https://docs.google.com/spreadsheets/d/1xaAo0_SU3iDfGdqDJZX_jRV0QtkufwHUKH3kQKF3YQs/edit#gid=0),
 who you can cc on a performance bug if you have questions.
 
+(Googlers only) For broad overview of various performance tools available for Chrome and where lab perf fits in, see [go/chrome-performance-how](http://go/chrome-performance-how).
+
 ## Understanding the bisect results
 
 ### The bug comment
diff --git a/docs/speed/binary_size/binary_size_explainer.md b/docs/speed/binary_size/binary_size_explainer.md
new file mode 100644
index 0000000..947c561
--- /dev/null
+++ b/docs/speed/binary_size/binary_size_explainer.md
@@ -0,0 +1,234 @@
+# Binary Size Explainer
+
+This document explains the overhead of each kind of binary size, where
+"overhead" is the performance cost of existing (e.g.  does not include the
+overhead of loading an image, just the overhead of an image existing).  This
+document focuses on Android, but several types of size are the same on other
+platforms.
+
+*** note
+See [optimization_advice.md] for ideas on how to reduce
+size.
+***
+
+[optimization_advice.md]: optimization_advice.md
+
+[TOC]
+
+## How big is Chrome?
+
+* Chrome's per-commit size is tracked in [chromeperf](https://chromeperf.appspot.com/report):
+  * For Android, look for "resource\_sizes".
+    * Googlers, see also [go/chromeapksizes], and [go/chromemilestonesizes].
+  * For other platforms: look for "sizes".
+* As of 2019, Chrome for Android (arm32) grows by about 100kb per week.
+
+[go/chromeapksizes]: http://go/chromeapksizes
+[go/chromemilestonesizes]: http://go/chromemilestonesizes
+ 
+## Why care about binary size?
+
+* Takes disk space away from users.
+  * Much less so than Chrome's cache, but this is space that cannot be
+    cleared, and on Android is displayed prominently in the Play Store and in
+    system settings.
+* Takes disk space on system images for Android and Chrome OS.
+  * It routinely happens that system images fill up, and all components need
+    to do their part to fit into the space.
+* Binary size is a "proxy metric" for other performance metrics.
+  * It's a metric that is trivial to measure, and correlates to RAM usage,
+    start-up speed etc.  (there are nuances, which we try to capture in this
+    doc).
+* Binary size affects users' perception of Chrome's performance.
+  * E.g. Large app size leads users to think Chrome is "bloated".
+* Binary size is much easier to stay on top of than it is to retroactively fix.
+
+Googlers - see [go/chromebinarysizematters] for more links to studies.
+
+[go/chromebinarysizematters]: http://go/chromebinarysizematters
+
+### How much effort should I spend to reduce binary size?
+
+Chrome is currently not under any hard growth limits, but to mitigate
+unnecessary bloat of this shared resource, everyone should ensure that
+reasonable diligence is taken to minimize growth.
+
+* "Reasonable diligence" requires judgement and varies depending on the change.
+  A rough guideline:
+  * For small (<50kb) increases: spend a day trying to reduce (unless it's obviously not possible).
+  * For larger increases: Try to understand and document why the change requires so much size.
+  * If you are unsure, reach out on [binary-size@] for advice.
+* The [android-binary-size][size_trybot] trybot will alert for single commits that increase
+  binary size on 32-bit Android by more than 16kb.
+  * While such increases are often fine, this helps prevent unexpected
+  increases.
+* It typically takes about a week of engineering time to reduce Android's binary size by 50kb.
+
+[binary-size@]: https://groups.google.com/a/chromium.org/g/binary-size/
+[size_trybot]: /docs/speed/binary_size/android_binary_size_trybot.md
+
+## Where Size Lives Matters
+
+### WebView vs Chrome
+
+Android WebView is used by the majority of Android apps, so overhead (other
+than clean memory) introduced into WebView has a multiplicative effect on the
+OS.  See
+[android\_build\_instructions.md]
+for how packaging of Chrome / WebView changes by OS version.
+
+[android\_build\_instructions.md]: /docs/android_build_instructions.md#Multiple-Chrome-Targets
+
+
+### APK Splits
+
+Chrome ships as an [Android App Bundle], and consists of several APK splits.
+
+[Android App Bundle]: /docs/android_dynamic_feature_modules.md#about-bundles
+
+#### The "base" split
+
+* Loaded on start-up by every process.
+* Keeping its dex size minimal is crucial, since it has both RAM and start-up
+  overhead _per-renderer_.
+
+
+#### In the "chrome" feature split
+
+* Loaded on start-up by the browser process.
+* Important to keep dex size small in order to have Chrome start-up quickly,
+  and to minimize our baseline memory requirement.
+
+
+#### In another feature split
+
+* Since they are loaded on-demand, binary size matters proportionally to the
+  number of users that use the module.
+
+#### In a DFM
+
+* These are feature splits that are not even downloaded until needed. Binary
+  size matters proportionally to the number of users that use the module.
+
+## Types of Overhead
+
+Here we define some terms used to describe overhead.
+
+**Clean Memory:**
+
+* Backed by a file on disk, and mapped to virtual memory via `mmap()`.
+* Shared between processes.
+* Paged into RAM on-demand by the OS.
+* Fast when disk cache is hot, and slower when disk cache is cold.
+
+**Dirty Memory:**
+
+* Not backed by a file, and thus cannot be safely paged out (except to zram)
+
+## Overhead for Each Type of Binary Size
+
+### Native code Size
+
+#### Code (.text)
+
+Machine instructions.
+
+* **RAM:** _Clean Memory_, with good locality thanks to PGO.
+* **Start-up:** No effect beyond page faults.
+
+#### Read-only Data (.rodata)
+
+String literals, global constants, etc.
+
+* **RAM:** _Clean Memory_, with poor locality (most is paged in after a short amount of time)
+* **Start-up:** No effect beyond page faults.
+
+#### Read-only-after-relocated Data (.data.rel.ro)
+
+Read-only data that must be adjusted based on the base load address of the executable.
+
+* **RAM:** _Dirty-ish Memory_. It's complicated.  Refer to
+  [native_relocations.md].
+* **Start-up:** Some overhead. Again, read the linked doc.
+
+[native relocations.md]: /docs/native_relocations.md#why-do-they-matter
+
+#### Mutable Data (.data)
+
+Global non-const variables.
+
+* **RAM:** _Dirty Memory_
+* **Start-up:** The entire section is loaded before any code is run.
+
+### Native resources (.pak)
+
+UI Images, chrome:// pages, UI strings, etc.
+
+* **RAM:** _Clean Memory_
+* **Start-up:** Platform-dependent. None on Android. Page faults on desktop.
+* Locality improved [using an orderfile] on some platforms.
+
+[using an orderfile]: /tools/gritsettings/README.md#:~:text=internal%20translation%20process.-,startup_resources_%5Bplatform%5D.txt,-%3A%20These%20files%20provide
+
+### Android: Java code size (.dex)
+
+Java bytecode in the [DEX file format], stored in `classes.dex`, `classes2.dex`,
+...  When Android installs Chrome, this bytecode is turned into machine code and
+stored as `.odex` & `.vdex` files.  The size of these files depends on the OS
+version and dex compilation profile.  Generally, they are 1x the size of
+uncompressed `.dex` on Android Go, and 4x the size of uncompressed `.dex` on
+other devices.
+
+* **RAM:** Mostly _Clean Memory_, but Some _Dirty Memory_ as well.
+  * E.g.: The number of method declarations (referred to as "method count")
+    directly corresponds to dirty RAM, where 1 entry = 4 bytes (on arm32).
+* **Start-up:** Impact proportional to overall size.  Mitigated by packaging
+  code into [feature splits] when possible.
+
+[DEX file format]: https://source.android.com/devices/tech/dalvik/dex-format
+[feature splits]: /docs/android_dynamic_feature_modules.md#:~:text=files%2C%20known%20as%20%E2%80%9C-,feature%20splits,-%E2%80%9D.%20Feature%20splits%20have
+
+#### More about Method Count
+
+When changing `.java code`, the change in method count is
+[shown in code reviews][size_trybot]. The count is the number of method
+references added/removed after optimization.  Which methods are added/removed
+can be seen in the "APK Breakdown" link by checking "Method Count Mode".
+
+Method count is a useful thing to look at because:
+
+* Each method reference has overhead within the dex file format, and for smallish methods, contributes more to binary size than its corresponding executable code.
+* Method references that survive R8 optimization show how optimizable your abstractions are. Try to use low-overhead (or zero-overhead) abstractions. If you find that you're adding a lot of methods, you should see whether a different abstraction would result in fewer methods.
+
+### Android: Resources
+
+#### resources.arsc
+
+All files within `res/`, as well as individual entries with `res/values` files
+contribute to this file.  It consists of a string table, plus one 2D array
+for each resource type (strings, drawable, etc).  The overhead (not included
+actual data) for each table is: `# [unique configs] * # of resources * sizeof(entry)`.
+
+* **RAM:** _Clean Memory_, with poor locality (most is paged in after a short
+  amount of time)
+* **Start-up:** No effect beyond page faults.
+* Its table-based file format means it's important to keep the number of unique
+  configs as small as possible.
+
+[unique configs]: https://developer.android.com/guide/topics/resources/providing-resources#QualifierRules
+
+#### res/... (drawables/layouts/xml)
+
+Files that are packaged within the `res/` directory of the `.apk`.  These also
+have an entry within `resources.arsc` that maps to them from a resource ID.
+
+* **RAM:** None unless resources are accessed.
+* **Start-up**: None unless resources are accessed.
+
+
+### Other Assets
+
+ICU data, V8 snapshot, etc.
+
+* **RAM:** _Clean Memory_
+* **Start-up:** No effect beyond page faults.
diff --git a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc
index ceca7b9..5cfc1b6 100644
--- a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc
+++ b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc
@@ -633,12 +633,9 @@
   }
 
   characteristic->ReadRemoteCharacteristic(
-      base::BindOnce(
-          &BluetoothLowEnergyEventRouter::OnReadRemoteCharacteristicSuccess,
-          weak_ptr_factory_.GetWeakPtr(), instance_id, std::move(callback)),
-      base::BindOnce(&BluetoothLowEnergyEventRouter::OnError,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     std::move(error_callback)));
+      base::BindOnce(&BluetoothLowEnergyEventRouter::OnReadRemoteCharacteristic,
+                     weak_ptr_factory_.GetWeakPtr(), instance_id,
+                     std::move(callback), std::move(error_callback)));
 }
 
 void BluetoothLowEnergyEventRouter::WriteCharacteristicValue(
@@ -799,11 +796,8 @@
   }
 
   descriptor->ReadRemoteDescriptor(
-      base::BindOnce(
-          &BluetoothLowEnergyEventRouter::OnReadRemoteDescriptorSuccess,
-          weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
-      base::BindOnce(&BluetoothLowEnergyEventRouter::OnError,
-                     weak_ptr_factory_.GetWeakPtr(),
+      base::BindOnce(&BluetoothLowEnergyEventRouter::OnReadRemoteDescriptor,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                      std::move(error_callback)));
 }
 
@@ -1089,8 +1083,7 @@
     const device::BluetoothDevice* device,
     const device::BluetoothLocalGattCharacteristic* characteristic,
     int offset,
-    Delegate::ValueCallback value_callback,
-    Delegate::ErrorCallback error_callback) {
+    Delegate::ValueCallback value_callback) {
   const std::string& service_id = characteristic->GetService()->GetIdentifier();
   if (service_id_to_extension_id_.find(service_id) ==
       service_id_to_extension_id_.end()) {
@@ -1101,9 +1094,10 @@
 
   const std::string& extension_id = service_id_to_extension_id_.at(service_id);
   apibtle::Request request;
+  // TODO(crbug.com/730593): Delete NullCallback when write callbacks combined.
   request.request_id = StoreSentRequest(
       extension_id, std::make_unique<AttributeValueRequest>(
-                        std::move(value_callback), std::move(error_callback)));
+                        std::move(value_callback), base::NullCallback()));
   PopulateDevice(device, &request);
   DispatchEventToExtension(
       extension_id, events::BLUETOOTH_LOW_ENERGY_ON_CHARACTERISTIC_READ_REQUEST,
@@ -1160,8 +1154,7 @@
     const device::BluetoothDevice* device,
     const device::BluetoothLocalGattDescriptor* descriptor,
     int offset,
-    Delegate::ValueCallback value_callback,
-    Delegate::ErrorCallback error_callback) {
+    Delegate::ValueCallback value_callback) {
   const std::string& service_id =
       descriptor->GetCharacteristic()->GetService()->GetIdentifier();
   if (service_id_to_extension_id_.find(service_id) ==
@@ -1174,9 +1167,10 @@
   const std::string& extension_id = service_id_to_extension_id_.at(service_id);
 
   apibtle::Request request;
+  // TODO(crbug.com/730593): Delete NullCallback when write callbacks combined.
   request.request_id = StoreSentRequest(
       extension_id, std::make_unique<AttributeValueRequest>(
-                        std::move(value_callback), std::move(error_callback)));
+                        std::move(value_callback), base::NullCallback()));
   PopulateDevice(device, &request);
   DispatchEventToExtension(
       extension_id,
@@ -1391,7 +1385,7 @@
   }
 
   if (request->type == AttributeValueRequest::ATTRIBUTE_READ_REQUEST) {
-    std::move(request->value_callback).Run(value);
+    std::move(request->value_callback).Run(/*error_code=*/base::nullopt, value);
   } else {
     std::move(request->success_callback).Run();
   }
@@ -1607,10 +1601,17 @@
   return descriptor;
 }
 
-void BluetoothLowEnergyEventRouter::OnReadRemoteCharacteristicSuccess(
+void BluetoothLowEnergyEventRouter::OnReadRemoteCharacteristic(
     const std::string& characteristic_instance_id,
     base::OnceClosure callback,
+    ErrorCallback error_callback,
+    base::Optional<BluetoothRemoteGattService::GattErrorCode> error_code,
     const std::vector<uint8_t>& value) {
+  if (error_code.has_value()) {
+    VLOG(2) << "Remote characteristic value read failed.";
+    std::move(error_callback).Run(GattErrorToRouterError(error_code.value()));
+    return;
+  }
   VLOG(2) << "Remote characteristic value read successful.";
 
   BluetoothRemoteGattCharacteristic* characteristic =
@@ -1620,9 +1621,17 @@
   std::move(callback).Run();
 }
 
-void BluetoothLowEnergyEventRouter::OnReadRemoteDescriptorSuccess(
+void BluetoothLowEnergyEventRouter::OnReadRemoteDescriptor(
     base::OnceClosure callback,
+    ErrorCallback error_callback,
+    base::Optional<device::BluetoothRemoteGattService::GattErrorCode>
+        error_code,
     const std::vector<uint8_t>& value) {
+  if (error_code.has_value()) {
+    VLOG(2) << "Remote characteristic/descriptor value read failed.";
+    std::move(error_callback).Run(GattErrorToRouterError(error_code.value()));
+    return;
+  }
   VLOG(2) << "Remote descriptor value read successful.";
   std::move(callback).Run();
 }
@@ -1674,7 +1683,7 @@
 void BluetoothLowEnergyEventRouter::OnError(
     ErrorCallback error_callback,
     BluetoothRemoteGattService::GattErrorCode error_code) {
-  VLOG(2) << "Remote characteristic/descriptor value read/write failed.";
+  VLOG(2) << "Remote characteristic/descriptor operation failed.";
 
   std::move(error_callback).Run(GattErrorToRouterError(error_code));
 }
@@ -1756,7 +1765,7 @@
     const std::string& extension_id,
     const std::string& characteristic_id,
     ErrorCallback error_callback,
-    device::BluetoothRemoteGattService::GattErrorCode error_code) {
+    BluetoothRemoteGattService::GattErrorCode error_code) {
   VLOG(2) << "Failed to create value update session for characteristic: "
           << characteristic_id;
 
diff --git a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.h b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.h
index 34bfbf77..2b2eb70 100644
--- a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.h
+++ b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.h
@@ -310,8 +310,7 @@
       const device::BluetoothDevice* device,
       const device::BluetoothLocalGattCharacteristic* characteristic,
       int offset,
-      Delegate::ValueCallback value_callback,
-      Delegate::ErrorCallback error_callback) override;
+      Delegate::ValueCallback value_callback) override;
   void OnCharacteristicWriteRequest(
       const device::BluetoothDevice* device,
       const device::BluetoothLocalGattCharacteristic* characteristic,
@@ -331,8 +330,7 @@
       const device::BluetoothDevice* device,
       const device::BluetoothLocalGattDescriptor* descriptor,
       int offset,
-      Delegate::ValueCallback value_callback,
-      Delegate::ErrorCallback error_callback) override;
+      Delegate::ValueCallback value_callback) override;
   void OnDescriptorWriteRequest(
       const device::BluetoothDevice* device,
       const device::BluetoothLocalGattDescriptor* descriptor,
@@ -435,14 +433,21 @@
 
   // Dispatches a BLUETOOTH_LOW_ENERGY_ON_CHARACTERISTIC_VALUE_CHANGED and runs
   // |callback|.
-  void OnReadRemoteCharacteristicSuccess(
+  void OnReadRemoteCharacteristic(
       const std::string& characteristic_instance_id,
       base::OnceClosure callback,
+      ErrorCallback error_callback,
+      base::Optional<device::BluetoothRemoteGattService::GattErrorCode>
+          error_code,
       const std::vector<uint8_t>& value);
 
   // Runs |callback|.
-  void OnReadRemoteDescriptorSuccess(base::OnceClosure callback,
-                                     const std::vector<uint8_t>& value);
+  void OnReadRemoteDescriptor(
+      base::OnceClosure callback,
+      ErrorCallback error_callback,
+      base::Optional<device::BluetoothRemoteGattService::GattErrorCode>
+          error_code,
+      const std::vector<uint8_t>& value);
 
   // Called by BluetoothDevice in response to a call to CreateGattConnection.
   void OnCreateGattConnection(
@@ -464,7 +469,7 @@
 
   // Called by BluetoothRemoteGattCharacteristic and
   // BluetoothRemoteGattDescriptor in
-  // case of an error during the read/write operations.
+  // case of an error during the write operations.
   void OnError(ErrorCallback error_callback,
                device::BluetoothRemoteGattService::GattErrorCode error_code);
 
diff --git a/extensions/renderer/api/automation/automation_api_util.cc b/extensions/renderer/api/automation/automation_api_util.cc
index 96da04eb..a0fcb66 100644
--- a/extensions/renderer/api/automation/automation_api_util.cc
+++ b/extensions/renderer/api/automation/automation_api_util.cc
@@ -141,6 +141,8 @@
     case ui::AXEventGenerator::Event::LIVE_STATUS_CHANGED:
     case ui::AXEventGenerator::Event::LOAD_COMPLETE:
     case ui::AXEventGenerator::Event::LOAD_START:
+    case ui::AXEventGenerator::Event::MENU_POPUP_END:
+    case ui::AXEventGenerator::Event::MENU_POPUP_START:
     case ui::AXEventGenerator::Event::MENU_ITEM_SELECTED:
     case ui::AXEventGenerator::Event::MULTILINE_STATE_CHANGED:
     case ui::AXEventGenerator::Event::MULTISELECTABLE_STATE_CHANGED:
diff --git a/fuchsia/engine/browser/frame_impl.cc b/fuchsia/engine/browser/frame_impl.cc
index 9e95136d..7ec50b7d 100644
--- a/fuchsia/engine/browser/frame_impl.cc
+++ b/fuchsia/engine/browser/frame_impl.cc
@@ -1131,6 +1131,7 @@
 
 void FrameImpl::RenderFrameCreated(content::RenderFrameHost* frame_host) {
   // The top-level frame is given a transparent background color.
+  // GetView() is guaranteed to be non-null until |frame_host| teardown.
   if (frame_host == web_contents()->GetMainFrame())
     frame_host->GetView()->SetBackgroundColor(SK_AlphaTRANSPARENT);
 }
diff --git a/fuchsia/engine/renderer/DEPS b/fuchsia/engine/renderer/DEPS
index b6a9ae1..3a2d94b 100644
--- a/fuchsia/engine/renderer/DEPS
+++ b/fuchsia/engine/renderer/DEPS
@@ -4,6 +4,5 @@
   "+media",
   "+mojo/public/cpp/bindings",
   "+services/service_manager/public/cpp",
-  "+third_party/blink/public/common/associated_interfaces",
-  "+third_party/blink/public/platform/url_loader_throttle_provider.h",
+  "+third_party/blink/public",
 ]
diff --git a/fuchsia/engine/renderer/web_engine_content_renderer_client.cc b/fuchsia/engine/renderer/web_engine_content_renderer_client.cc
index d8f50e0..be17bddf 100644
--- a/fuchsia/engine/renderer/web_engine_content_renderer_client.cc
+++ b/fuchsia/engine/renderer/web_engine_content_renderer_client.cc
@@ -13,6 +13,7 @@
 #include "components/on_load_script_injector/renderer/on_load_script_injector.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_view.h"
 #include "fuchsia/engine/common/cast_streaming.h"
 #include "fuchsia/engine/features.h"
 #include "fuchsia/engine/renderer/cast_streaming_demuxer.h"
@@ -23,6 +24,7 @@
 #include "services/network/public/cpp/features.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
+#include "third_party/blink/public/web/web_view.h"
 #include "third_party/widevine/cdm/widevine_cdm_common.h"
 
 namespace {
@@ -133,6 +135,14 @@
 
 void WebEngineContentRendererClient::RenderFrameCreated(
     content::RenderFrame* render_frame) {
+  // If this is a top-level frame then it should have a transparent background.
+  // Both the RenderView and WebView should be guaranteed to be non-null, since
+  // the |render_frame| was only just created.
+  if (render_frame->IsMainFrame()) {
+    render_frame->GetRenderView()->GetWebView()->SetBaseBackgroundColor(
+        SK_AlphaTRANSPARENT);
+  }
+
   // Add WebEngine services to the new RenderFrame.
   // The objects' lifetimes are bound to the RenderFrame's lifetime.
   new on_load_script_injector::OnLoadScriptInjector(render_frame);
diff --git a/gpu/command_buffer/service/shared_image_backing_d3d.cc b/gpu/command_buffer/service/shared_image_backing_d3d.cc
index 893f4d19..1f98a72 100644
--- a/gpu/command_buffer/service/shared_image_backing_d3d.cc
+++ b/gpu/command_buffer/service/shared_image_backing_d3d.cc
@@ -4,6 +4,7 @@
 
 #include "gpu/command_buffer/service/shared_image_backing_d3d.h"
 
+#include "base/memory/ptr_util.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "components/viz/common/resources/resource_format_utils.h"
 #include "components/viz/common/resources/resource_sizes.h"
@@ -17,26 +18,353 @@
 
 namespace {
 
-class ScopedRestoreTexture2D {
+bool SupportsVideoFormat(DXGI_FORMAT dxgi_format) {
+  switch (dxgi_format) {
+    case DXGI_FORMAT_NV12:
+    case DXGI_FORMAT_P010:
+    case DXGI_FORMAT_B8G8R8A8_UNORM:
+    case DXGI_FORMAT_R10G10B10A2_UNORM:
+    case DXGI_FORMAT_R16G16B16A16_FLOAT:
+      return true;
+    default:
+      return false;
+  }
+}
+
+size_t NumPlanes(DXGI_FORMAT dxgi_format) {
+  switch (dxgi_format) {
+    case DXGI_FORMAT_NV12:
+    case DXGI_FORMAT_P010:
+      return 2;
+    case DXGI_FORMAT_B8G8R8A8_UNORM:
+    case DXGI_FORMAT_R10G10B10A2_UNORM:
+    case DXGI_FORMAT_R16G16B16A16_FLOAT:
+      return 1;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
+viz::ResourceFormat PlaneFormat(DXGI_FORMAT dxgi_format, size_t plane) {
+  DCHECK_LT(plane, NumPlanes(dxgi_format));
+  switch (dxgi_format) {
+    // TODO(crbug.com/1011555): P010 formats are not fully supported by Skia.
+    // Treat them the same as NV12 for the time being.
+    case DXGI_FORMAT_NV12:
+    case DXGI_FORMAT_P010:
+      // Y plane is accessed as R8 and UV plane is accessed as RG88 in D3D.
+      return plane == 0 ? viz::RED_8 : viz::RG_88;
+    case DXGI_FORMAT_B8G8R8A8_UNORM:
+      return viz::BGRA_8888;
+    case DXGI_FORMAT_R10G10B10A2_UNORM:
+      return viz::RGBA_1010102;
+    case DXGI_FORMAT_R16G16B16A16_FLOAT:
+      return viz::RGBA_F16;
+    default:
+      NOTREACHED();
+      return viz::BGRA_8888;
+  }
+}
+
+gfx::Size PlaneSize(DXGI_FORMAT dxgi_format,
+                    const gfx::Size& size,
+                    size_t plane) {
+  DCHECK_LT(plane, NumPlanes(dxgi_format));
+  switch (dxgi_format) {
+    case DXGI_FORMAT_NV12:
+    case DXGI_FORMAT_P010:
+      // Y plane is full size and UV plane is accessed as half size in D3D.
+      return plane == 0 ? size : gfx::Size(size.width() / 2, size.height() / 2);
+    case DXGI_FORMAT_B8G8R8A8_UNORM:
+    case DXGI_FORMAT_R10G10B10A2_UNORM:
+    case DXGI_FORMAT_R16G16B16A16_FLOAT:
+      return size;
+    default:
+      NOTREACHED();
+      return gfx::Size();
+  }
+}
+
+class ScopedRestoreTexture {
  public:
-  explicit ScopedRestoreTexture2D(gl::GLApi* api) : api_(api) {
+  ScopedRestoreTexture(gl::GLApi* api, GLenum target)
+      : api_(api), target_(target) {
+    DCHECK(target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES);
     GLint binding = 0;
-    api->glGetIntegervFn(GL_TEXTURE_BINDING_2D, &binding);
+    api->glGetIntegervFn(target == GL_TEXTURE_2D
+                             ? GL_TEXTURE_BINDING_2D
+                             : GL_TEXTURE_BINDING_EXTERNAL_OES,
+                         &binding);
     prev_binding_ = binding;
   }
 
-  ~ScopedRestoreTexture2D() {
-    api_->glBindTextureFn(GL_TEXTURE_2D, prev_binding_);
-  }
+  ~ScopedRestoreTexture() { api_->glBindTextureFn(target_, prev_binding_); }
 
  private:
   gl::GLApi* const api_;
+  const GLenum target_;
   GLuint prev_binding_ = 0;
-  DISALLOW_COPY_AND_ASSIGN(ScopedRestoreTexture2D);
+  DISALLOW_COPY_AND_ASSIGN(ScopedRestoreTexture);
 };
 
+scoped_refptr<gles2::TexturePassthrough> CreateGLTexture(
+    viz::ResourceFormat format,
+    const gfx::Size& size,
+    const gfx::ColorSpace& color_space,
+    Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
+    Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain = nullptr,
+    GLenum texture_target = GL_TEXTURE_2D,
+    unsigned array_slice = 0u,
+    unsigned plane_index = 0u) {
+  gl::GLApi* const api = gl::g_current_gl_context;
+  ScopedRestoreTexture scoped_restore(api, texture_target);
+
+  GLuint service_id = 0;
+  api->glGenTexturesFn(1, &service_id);
+  api->glBindTextureFn(texture_target, service_id);
+
+  // The GL internal format can differ from the underlying swap chain or texture
+  // format e.g. RGBA or RGB instead of BGRA or RED/RG for NV12 texture planes.
+  // See EGL_ANGLE_d3d_texture_client_buffer spec for format restrictions.
+  const auto internal_format = viz::GLInternalFormat(format);
+  const auto data_type = viz::GLDataType(format);
+  auto image = base::MakeRefCounted<gl::GLImageD3D>(
+      size, internal_format, data_type, color_space, d3d11_texture, array_slice,
+      plane_index, swap_chain);
+  DCHECK_EQ(image->GetDataFormat(), viz::GLDataFormat(format));
+  if (!image->Initialize()) {
+    DLOG(ERROR) << "GLImageD3D::Initialize failed";
+    api->glDeleteTexturesFn(1, &service_id);
+    return nullptr;
+  }
+  if (!image->BindTexImage(texture_target)) {
+    DLOG(ERROR) << "GLImageD3D::BindTexImage failed";
+    api->glDeleteTexturesFn(1, &service_id);
+    return nullptr;
+  }
+
+  auto texture = base::MakeRefCounted<gles2::TexturePassthrough>(
+      service_id, texture_target);
+  texture->SetLevelImage(texture_target, 0, image.get());
+  GLint texture_memory_size = 0;
+  api->glGetTexParameterivFn(texture_target, GL_MEMORY_SIZE_ANGLE,
+                             &texture_memory_size);
+  texture->SetEstimatedSize(texture_memory_size);
+
+  return texture;
+}
+
 }  // anonymous namespace
 
+SharedImageBackingD3D::SharedState::SharedState(
+    base::win::ScopedHandle shared_handle,
+    Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex)
+    : shared_handle_(std::move(shared_handle)),
+      dxgi_keyed_mutex_(std::move(dxgi_keyed_mutex)) {}
+
+SharedImageBackingD3D::SharedState::~SharedState() {
+  DCHECK(!acquired_for_d3d12_);
+  DCHECK_EQ(acquired_for_d3d11_count_, 0);
+  shared_handle_.Close();
+}
+
+bool SharedImageBackingD3D::SharedState::BeginAccessD3D12(
+    uint64_t* acquire_key) {
+  if (!dxgi_keyed_mutex_) {
+    DLOG(ERROR) << "D3D12 access not supported without keyed mutex";
+    return false;
+  }
+  if (acquired_for_d3d12_ || acquired_for_d3d11_count_ > 0) {
+    DLOG(ERROR) << "Recursive BeginAccess not supported";
+    return false;
+  }
+  *acquire_key = acquire_key_;
+  acquire_key_++;
+  acquired_for_d3d12_ = true;
+  return true;
+}
+
+void SharedImageBackingD3D::SharedState::EndAccessD3D12() {
+  acquired_for_d3d12_ = false;
+}
+
+bool SharedImageBackingD3D::SharedState::BeginAccessD3D11() {
+  // Nop for shared images that are created without keyed mutex (D3D11 only).
+  if (!dxgi_keyed_mutex_)
+    return true;
+
+  if (acquired_for_d3d12_) {
+    DLOG(ERROR) << "Recursive BeginAccess not supported";
+    return false;
+  }
+  if (acquired_for_d3d11_count_ > 0) {
+    acquired_for_d3d11_count_++;
+    return true;
+  }
+  const HRESULT hr = dxgi_keyed_mutex_->AcquireSync(acquire_key_, INFINITE);
+  if (FAILED(hr)) {
+    DLOG(ERROR) << "Unable to acquire the keyed mutex " << std::hex << hr;
+    return false;
+  }
+  acquire_key_++;
+  acquired_for_d3d11_count_++;
+  return true;
+}
+
+void SharedImageBackingD3D::SharedState::EndAccessD3D11() {
+  // Nop for shared images that are created without keyed mutex (D3D11 only).
+  if (!dxgi_keyed_mutex_)
+    return;
+
+  DCHECK_GT(acquired_for_d3d11_count_, 0);
+  acquired_for_d3d11_count_--;
+  if (acquired_for_d3d11_count_ == 0) {
+    const HRESULT hr = dxgi_keyed_mutex_->ReleaseSync(acquire_key_);
+    if (FAILED(hr))
+      DLOG(ERROR) << "Unable to release the keyed mutex " << std::hex << hr;
+  }
+}
+
+HANDLE SharedImageBackingD3D::SharedState::GetSharedHandle() const {
+  return shared_handle_.Get();
+}
+
+// static
+std::unique_ptr<SharedImageBackingD3D>
+SharedImageBackingD3D::CreateFromSwapChainBuffer(
+    const Mailbox& mailbox,
+    viz::ResourceFormat format,
+    const gfx::Size& size,
+    const gfx::ColorSpace& color_space,
+    GrSurfaceOrigin surface_origin,
+    SkAlphaType alpha_type,
+    uint32_t usage,
+    Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
+    Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain,
+    size_t buffer_index) {
+  auto gl_texture =
+      CreateGLTexture(format, size, color_space, d3d11_texture, swap_chain);
+  if (!gl_texture) {
+    DLOG(ERROR) << "Failed to create GL texture";
+    return nullptr;
+  }
+  return base::WrapUnique(new SharedImageBackingD3D(
+      mailbox, format, size, color_space, surface_origin, alpha_type, usage,
+      std::move(d3d11_texture), std::move(gl_texture), std::move(swap_chain),
+      buffer_index));
+}
+
+// static
+std::unique_ptr<SharedImageBackingD3D>
+SharedImageBackingD3D::CreateFromSharedHandle(
+    const Mailbox& mailbox,
+    viz::ResourceFormat format,
+    const gfx::Size& size,
+    const gfx::ColorSpace& color_space,
+    GrSurfaceOrigin surface_origin,
+    SkAlphaType alpha_type,
+    uint32_t usage,
+    Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
+    base::win::ScopedHandle shared_handle) {
+  DCHECK(shared_handle.IsValid());
+  // Keyed mutexes are required for Dawn interop but are not used for XR
+  // composition where fences are used instead.
+  Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex;
+  d3d11_texture.As(&dxgi_keyed_mutex);
+  DCHECK(!(usage & SHARED_IMAGE_USAGE_WEBGPU) || dxgi_keyed_mutex);
+
+  auto shared_state = base::MakeRefCounted<SharedState>(
+      std::move(shared_handle), std::move(dxgi_keyed_mutex));
+
+  // Creating the GL texture doesn't require exclusive access to the underlying
+  // D3D11 texture.
+  auto gl_texture = CreateGLTexture(format, size, color_space, d3d11_texture);
+  if (!gl_texture) {
+    DLOG(ERROR) << "Failed to create GL texture";
+    return nullptr;
+  }
+
+  return base::WrapUnique(new SharedImageBackingD3D(
+      mailbox, format, size, color_space, surface_origin, alpha_type, usage,
+      std::move(d3d11_texture), std::move(gl_texture), /*swap_chain=*/nullptr,
+      /*buffer_index=*/0, std::move(shared_state)));
+}
+
+std::unique_ptr<SharedImageBackingD3D>
+SharedImageBackingD3D::CreateFromGLTexture(
+    const Mailbox& mailbox,
+    viz::ResourceFormat format,
+    const gfx::Size& size,
+    const gfx::ColorSpace& color_space,
+    GrSurfaceOrigin surface_origin,
+    SkAlphaType alpha_type,
+    uint32_t usage,
+    Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
+    scoped_refptr<gles2::TexturePassthrough> gl_texture) {
+  return base::WrapUnique(new SharedImageBackingD3D(
+      mailbox, format, size, color_space, surface_origin, alpha_type, usage,
+      std::move(d3d11_texture), std::move(gl_texture)));
+}
+
+// static
+std::vector<std::unique_ptr<SharedImageBackingD3D>>
+SharedImageBackingD3D::CreateFromVideoTexture(
+    base::span<const Mailbox> mailboxes,
+    DXGI_FORMAT dxgi_format,
+    const gfx::Size& size,
+    uint32_t usage,
+    Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
+    unsigned array_slice,
+    base::win::ScopedHandle shared_handle) {
+  DCHECK(SupportsVideoFormat(dxgi_format));
+  DCHECK_EQ(mailboxes.size(), NumPlanes(dxgi_format));
+
+  // Shared handle and keyed mutex are required for Dawn interop.
+  Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex;
+  d3d11_texture.As(&dxgi_keyed_mutex);
+  DCHECK(!(usage & gpu::SHARED_IMAGE_USAGE_WEBGPU) ||
+         (shared_handle.IsValid() && dxgi_keyed_mutex));
+
+  // Share the same keyed mutex state for all the plane backings.
+  auto shared_state = base::MakeRefCounted<SharedState>(
+      std::move(shared_handle), std::move(dxgi_keyed_mutex));
+
+  std::vector<std::unique_ptr<SharedImageBackingD3D>> shared_images(
+      NumPlanes(dxgi_format));
+  for (size_t plane_index = 0; plane_index < shared_images.size();
+       plane_index++) {
+    const auto& mailbox = mailboxes[plane_index];
+
+    const auto plane_format = PlaneFormat(dxgi_format, plane_index);
+    const auto plane_size = PlaneSize(dxgi_format, size, plane_index);
+
+    // Shared image does not need to store the colorspace since it is already
+    // stored on the VideoFrame which is provided upon presenting the overlay.
+    // To prevent the developer from mistakenly using it, provide the invalid
+    // value from default-construction.
+    constexpr gfx::ColorSpace kInvalidColorSpace;
+
+    auto gl_texture = CreateGLTexture(
+        plane_format, plane_size, kInvalidColorSpace, d3d11_texture,
+        /*swap_chain=*/nullptr, GL_TEXTURE_EXTERNAL_OES, array_slice,
+        plane_index);
+    if (!gl_texture) {
+      DLOG(ERROR) << "Failed to create GL texture";
+      return {};
+    }
+
+    shared_images[plane_index] = base::WrapUnique(new SharedImageBackingD3D(
+        mailbox, plane_format, plane_size, kInvalidColorSpace,
+        kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage, d3d11_texture,
+        std::move(gl_texture), /*swap_chain=*/nullptr, /*buffer_index=*/0,
+        shared_state));
+    shared_images[plane_index]->SetCleared();
+  }
+
+  return shared_images;
+}
+
 SharedImageBackingD3D::SharedImageBackingD3D(
     const Mailbox& mailbox,
     viz::ResourceFormat format,
@@ -45,13 +373,11 @@
     GrSurfaceOrigin surface_origin,
     SkAlphaType alpha_type,
     uint32_t usage,
-    Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain,
-    scoped_refptr<gles2::TexturePassthrough> texture,
-    scoped_refptr<gl::GLImage> image,
-    size_t buffer_index,
     Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
-    base::win::ScopedHandle shared_handle,
-    Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex)
+    scoped_refptr<gles2::TexturePassthrough> gl_texture,
+    Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain,
+    size_t buffer_index,
+    scoped_refptr<SharedState> shared_state)
     : ClearTrackingSharedImageBacking(mailbox,
                                       format,
                                       size,
@@ -59,28 +385,23 @@
                                       surface_origin,
                                       alpha_type,
                                       usage,
-                                      texture->estimated_size(),
+                                      gl_texture->estimated_size(),
                                       false /* is_thread_safe */),
-      swap_chain_(std::move(swap_chain)),
-      texture_(std::move(texture)),
-      image_(std::move(image)),
-      buffer_index_(buffer_index),
       d3d11_texture_(std::move(d3d11_texture)),
-      shared_handle_(std::move(shared_handle)),
-      dxgi_keyed_mutex_(std::move(dxgi_keyed_mutex)) {
-  DCHECK(texture_);
+      gl_texture_(std::move(gl_texture)),
+      swap_chain_(std::move(swap_chain)),
+      buffer_index_(buffer_index),
+      shared_state_(std::move(shared_state)) {
+  DCHECK(gl_texture_);
 }
 
 SharedImageBackingD3D::~SharedImageBackingD3D() {
   if (!have_context())
-    texture_->MarkContextLost();
-  texture_ = nullptr;
-  swap_chain_ = nullptr;
+    gl_texture_->MarkContextLost();
+  gl_texture_ = nullptr;
+  shared_state_ = nullptr;
+  swap_chain_.Reset();
   d3d11_texture_.Reset();
-  dxgi_keyed_mutex_.Reset();
-  keyed_mutex_acquire_key_ = 0;
-  keyed_mutex_acquired_ = false;
-  shared_handle_.Close();
 
 #if BUILDFLAG(USE_DAWN)
   external_image_ = nullptr;
@@ -94,7 +415,7 @@
 
 bool SharedImageBackingD3D::ProduceLegacyMailbox(
     MailboxManager* mailbox_manager) {
-  mailbox_manager->ProduceTexture(mailbox(), texture_.get());
+  mailbox_manager->ProduceTexture(mailbox(), gl_texture_.get());
   return true;
 }
 
@@ -114,7 +435,7 @@
 
   // Persistently open the shared handle by caching it on this backing.
   if (!external_image_) {
-    DCHECK(shared_handle_.IsValid());
+    DCHECK(base::win::HandleTraits::IsHandleValid(GetSharedHandle()));
 
     const viz::ResourceFormat viz_resource_format = format();
     const WGPUTextureFormat wgpu_format =
@@ -136,7 +457,7 @@
     dawn_native::d3d12::ExternalImageDescriptorDXGISharedHandle
         externalImageDesc;
     externalImageDesc.cTextureDescriptor = &texture_descriptor;
-    externalImageDesc.sharedHandle = shared_handle_.Get();
+    externalImageDesc.sharedHandle = GetSharedHandle();
 
     external_image_ = dawn_native::d3d12::ExternalImageDXGI::Create(
         device, &externalImageDesc);
@@ -163,65 +484,38 @@
   // various GPU dumps.
   auto client_guid = GetSharedImageGUIDForTracing(mailbox());
   base::trace_event::MemoryAllocatorDumpGuid service_guid =
-      gl::GetGLTextureServiceGUIDForTracing(texture_->service_id());
+      gl::GetGLTextureServiceGUIDForTracing(gl_texture_->service_id());
   pmd->CreateSharedGlobalAllocatorDump(service_guid);
 
   int importance = 2;  // This client always owns the ref.
   pmd->AddOwnershipEdge(client_guid, service_guid, importance);
 
   // Swap chain textures only have one level backed by an image.
-  image_->OnMemoryDump(pmd, client_tracing_id, dump_name);
+  GetGLImage()->OnMemoryDump(pmd, client_tracing_id, dump_name);
 }
 
 bool SharedImageBackingD3D::BeginAccessD3D12(uint64_t* acquire_key) {
-  if (keyed_mutex_acquired_) {
-    DLOG(ERROR) << "Recursive BeginAccess not supported";
-    return false;
-  }
-  *acquire_key = keyed_mutex_acquire_key_;
-  keyed_mutex_acquire_key_++;
-  keyed_mutex_acquired_ = true;
-  return true;
+  return shared_state_->BeginAccessD3D12(acquire_key);
 }
 
 void SharedImageBackingD3D::EndAccessD3D12() {
-  keyed_mutex_acquired_ = false;
+  shared_state_->EndAccessD3D12();
 }
 
 bool SharedImageBackingD3D::BeginAccessD3D11() {
-  if (dxgi_keyed_mutex_) {
-    if (keyed_mutex_acquired_) {
-      DLOG(ERROR) << "Recursive BeginAccess not supported";
-      return false;
-    }
-    const HRESULT hr =
-        dxgi_keyed_mutex_->AcquireSync(keyed_mutex_acquire_key_, INFINITE);
-    if (FAILED(hr)) {
-      DLOG(ERROR) << "Unable to acquire the keyed mutex " << std::hex << hr;
-      return false;
-    }
-    keyed_mutex_acquire_key_++;
-    keyed_mutex_acquired_ = true;
-  }
-  return true;
+  return shared_state_->BeginAccessD3D11();
 }
+
 void SharedImageBackingD3D::EndAccessD3D11() {
-  if (dxgi_keyed_mutex_) {
-    const HRESULT hr = dxgi_keyed_mutex_->ReleaseSync(keyed_mutex_acquire_key_);
-    if (FAILED(hr)) {
-      DLOG(ERROR) << "Unable to release the keyed mutex " << std::hex << hr;
-      return;
-    }
-    keyed_mutex_acquired_ = false;
-  }
+  shared_state_->EndAccessD3D11();
 }
 
 HANDLE SharedImageBackingD3D::GetSharedHandle() const {
-  return shared_handle_.Get();
+  return shared_state_->GetSharedHandle();
 }
 
 gl::GLImage* SharedImageBackingD3D::GetGLImage() const {
-  return image_.get();
+  return gl_texture_->GetLevelImage(gl_texture_->target(), 0u);
 }
 
 bool SharedImageBackingD3D::PresentSwapChain() {
@@ -244,10 +538,12 @@
   }
 
   gl::GLApi* const api = gl::g_current_gl_context;
-  ScopedRestoreTexture2D scoped_restore(api);
 
-  api->glBindTextureFn(GL_TEXTURE_2D, texture_->service_id());
-  if (!image_->BindTexImage(GL_TEXTURE_2D)) {
+  DCHECK_EQ(gl_texture_->target(), static_cast<unsigned>(GL_TEXTURE_2D));
+  ScopedRestoreTexture scoped_restore(api, GL_TEXTURE_2D);
+
+  api->glBindTextureFn(GL_TEXTURE_2D, gl_texture_->service_id());
+  if (!GetGLImage()->BindTexImage(GL_TEXTURE_2D)) {
     DLOG(ERROR) << "GLImage::BindTexImage failed";
     return false;
   }
@@ -263,7 +559,7 @@
                                                    MemoryTypeTracker* tracker) {
   TRACE_EVENT0("gpu", "SharedImageBackingD3D::ProduceGLTexturePassthrough");
   return std::make_unique<SharedImageRepresentationGLTexturePassthroughD3D>(
-      manager, this, tracker, texture_);
+      manager, this, tracker, gl_texture_);
 }
 
 std::unique_ptr<SharedImageRepresentationSkia>
diff --git a/gpu/command_buffer/service/shared_image_backing_d3d.h b/gpu/command_buffer/service/shared_image_backing_d3d.h
index b6bdb07..5af24d9 100644
--- a/gpu/command_buffer/service/shared_image_backing_d3d.h
+++ b/gpu/command_buffer/service/shared_image_backing_d3d.h
@@ -42,7 +42,7 @@
 class GPU_GLES2_EXPORT SharedImageBackingD3D
     : public ClearTrackingSharedImageBacking {
  public:
-  SharedImageBackingD3D(
+  static std::unique_ptr<SharedImageBackingD3D> CreateFromSwapChainBuffer(
       const Mailbox& mailbox,
       viz::ResourceFormat format,
       const gfx::Size& size,
@@ -50,13 +50,42 @@
       GrSurfaceOrigin surface_origin,
       SkAlphaType alpha_type,
       uint32_t usage,
-      Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain,
-      scoped_refptr<gles2::TexturePassthrough> texture,
-      scoped_refptr<gl::GLImage> image,
-      size_t buffer_index,
       Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
-      base::win::ScopedHandle shared_handle,
-      Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex);
+      Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain,
+      size_t buffer_index);
+
+  static std::unique_ptr<SharedImageBackingD3D> CreateFromSharedHandle(
+      const Mailbox& mailbox,
+      viz::ResourceFormat format,
+      const gfx::Size& size,
+      const gfx::ColorSpace& color_space,
+      GrSurfaceOrigin surface_origin,
+      SkAlphaType alpha_type,
+      uint32_t usage,
+      Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
+      base::win::ScopedHandle shared_handle);
+
+  // TODO(sunnyps): Remove this after migrating DXVA decoder to EGLImage.
+  static std::unique_ptr<SharedImageBackingD3D> CreateFromGLTexture(
+      const Mailbox& mailbox,
+      viz::ResourceFormat format,
+      const gfx::Size& size,
+      const gfx::ColorSpace& color_space,
+      GrSurfaceOrigin surface_origin,
+      SkAlphaType alpha_type,
+      uint32_t usage,
+      Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
+      scoped_refptr<gles2::TexturePassthrough> gl_texture);
+
+  static std::vector<std::unique_ptr<SharedImageBackingD3D>>
+  CreateFromVideoTexture(
+      base::span<const Mailbox> mailboxes,
+      DXGI_FORMAT dxgi_format,
+      const gfx::Size& size,
+      uint32_t usage,
+      Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
+      unsigned array_slice,
+      base::win::ScopedHandle shared_handle = base::win::ScopedHandle());
 
   ~SharedImageBackingD3D() override;
 
@@ -100,29 +129,64 @@
       scoped_refptr<SharedContextState> context_state) override;
 
  private:
-  uint32_t GetAllowedDawnUsages() const;
+  class SharedState : public base::RefCountedThreadSafe<SharedState> {
+   public:
+    explicit SharedState(
+        base::win::ScopedHandle shared_handle = base::win::ScopedHandle(),
+        Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex = nullptr);
 
-  Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain_;
-  scoped_refptr<gles2::TexturePassthrough> texture_;
-  scoped_refptr<gl::GLImage> image_;
-  const size_t buffer_index_;
+    bool BeginAccessD3D11();
+    void EndAccessD3D11();
+
+    bool BeginAccessD3D12(uint64_t* acquire_key);
+    void EndAccessD3D12();
+
+    HANDLE GetSharedHandle() const;
+
+   private:
+    friend class base::RefCountedThreadSafe<SharedState>;
+    ~SharedState();
+
+    // If |d3d11_texture_| has a keyed mutex, it will be stored in
+    // |dxgi_keyed_mutex_|. The keyed mutex is used to synchronize D3D11 and
+    // D3D12 Chromium components. |dxgi_keyed_mutex_| is the D3D11 side of the
+    // keyed mutex. To create the corresponding D3D12 interface, pass the handle
+    // stored in |shared_handle_| to ID3D12Device::OpenSharedHandle. Only one
+    // component is allowed to read/write to the texture at a time.
+    // |acquire_key_| is incremented on every Acquire/Release usage.
+    base::win::ScopedHandle shared_handle_;
+    Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex_;
+    uint64_t acquire_key_ = 0;
+    bool acquired_for_d3d12_ = false;
+    int acquired_for_d3d11_count_ = 0;
+  };
+
+  SharedImageBackingD3D(
+      const Mailbox& mailbox,
+      viz::ResourceFormat format,
+      const gfx::Size& size,
+      const gfx::ColorSpace& color_space,
+      GrSurfaceOrigin surface_origin,
+      SkAlphaType alpha_type,
+      uint32_t usage,
+      Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
+      scoped_refptr<gles2::TexturePassthrough> gl_texture,
+      Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain = nullptr,
+      size_t buffer_index = 0,
+      scoped_refptr<SharedState> shared_state =
+          base::MakeRefCounted<SharedState>());
+
+  uint32_t GetAllowedDawnUsages() const;
 
   // Texture could be nullptr if an empty backing is needed for testing.
   Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture_;
+  scoped_refptr<gles2::TexturePassthrough> gl_texture_;
 
-  // If d3d11_texture_ has a keyed mutex, it will be stored in
-  // dxgi_keyed_mutex. The keyed mutex is used to synchronize
-  // D3D11 and D3D12 Chromium components.
-  // dxgi_keyed_mutex_ is the D3D11 side of the keyed mutex.
-  // To create the corresponding D3D12 interface, pass the handle
-  // stored in shared_handle_ to ID3D12Device::OpenSharedHandle.
-  // Only one component is allowed to read/write to the texture
-  // at a time. keyed_mutex_acquire_key_ is incremented on every
-  // Acquire/Release usage.
-  base::win::ScopedHandle shared_handle_;
-  Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex_;
-  uint64_t keyed_mutex_acquire_key_ = 0;
-  bool keyed_mutex_acquired_ = false;
+  Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain_;
+
+  const size_t buffer_index_;
+
+  scoped_refptr<SharedState> shared_state_;
 
   // If external_image_ exists, it means Dawn produced the D3D12 side of the
   // D3D11 texture created by ID3D12Device::OpenSharedHandle.
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_d3d.cc b/gpu/command_buffer/service/shared_image_backing_factory_d3d.cc
index 02225c6..e2a8c22 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_d3d.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_d3d.cc
@@ -18,24 +18,6 @@
 
 namespace {
 
-class ScopedRestoreTexture2D {
- public:
-  explicit ScopedRestoreTexture2D(gl::GLApi* api) : api_(api) {
-    GLint binding = 0;
-    api->glGetIntegervFn(GL_TEXTURE_BINDING_2D, &binding);
-    prev_binding_ = binding;
-  }
-
-  ~ScopedRestoreTexture2D() {
-    api_->glBindTextureFn(GL_TEXTURE_2D, prev_binding_);
-  }
-
- private:
-  gl::GLApi* const api_;
-  GLuint prev_binding_ = 0;
-  DISALLOW_COPY_AND_ASSIGN(ScopedRestoreTexture2D);
-};
-
 bool ClearBackBuffer(Microsoft::WRL::ComPtr<IDXGISwapChain1>& swap_chain,
                      Microsoft::WRL::ComPtr<ID3D11Device>& d3d11_device) {
   Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
@@ -108,80 +90,6 @@
          gl::DirectCompositionSurfaceWin::IsSwapChainTearingSupported();
 }
 
-std::unique_ptr<SharedImageBacking> SharedImageBackingFactoryD3D::MakeBacking(
-    const Mailbox& mailbox,
-    viz::ResourceFormat format,
-    const gfx::Size& size,
-    const gfx::ColorSpace& color_space,
-    GrSurfaceOrigin surface_origin,
-    SkAlphaType alpha_type,
-    uint32_t usage,
-    Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain,
-    size_t buffer_index,
-    Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
-    base::win::ScopedHandle shared_handle) {
-  gl::GLApi* const api = gl::g_current_gl_context;
-  ScopedRestoreTexture2D scoped_restore(api);
-
-  const GLenum target = GL_TEXTURE_2D;
-  GLuint service_id = 0;
-  api->glGenTexturesFn(1, &service_id);
-  api->glBindTextureFn(target, service_id);
-  api->glTexParameteriFn(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-  api->glTexParameteriFn(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-  api->glTexParameteriFn(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-  api->glTexParameteriFn(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
-  Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex;
-
-  if (swap_chain) {
-    DCHECK(!d3d11_texture);
-    DCHECK(!shared_handle.IsValid());
-    const HRESULT hr =
-        swap_chain->GetBuffer(buffer_index, IID_PPV_ARGS(&d3d11_texture));
-    if (FAILED(hr)) {
-      DLOG(ERROR) << "GetBuffer failed with error " << std::hex;
-      return nullptr;
-    }
-  } else if (shared_handle.IsValid()) {
-    // Keyed mutexes are required for Dawn interop but are not used
-    // for XR composition where fences are used instead.
-    d3d11_texture.As(&dxgi_keyed_mutex);
-  }
-  DCHECK(d3d11_texture);
-
-  // The GL internal format can differ from the underlying swap chain format
-  // e.g. RGBA8 or RGB8 instead of BGRA8.
-  const GLenum internal_format = viz::GLInternalFormat(format);
-  const GLenum data_type = viz::GLDataType(format);
-  const GLenum data_format = viz::GLDataFormat(format);
-  auto image = base::MakeRefCounted<gl::GLImageD3D>(
-      size, internal_format, data_type, d3d11_texture, swap_chain);
-  DCHECK_EQ(image->GetDataFormat(), data_format);
-  if (!image->Initialize()) {
-    DLOG(ERROR) << "GLImageD3D::Initialize failed";
-    return nullptr;
-  }
-  if (!image->BindTexImage(target)) {
-    DLOG(ERROR) << "GLImageD3D::BindTexImage failed";
-    return nullptr;
-  }
-
-  scoped_refptr<gles2::TexturePassthrough> texture =
-      base::MakeRefCounted<gles2::TexturePassthrough>(service_id, target);
-  texture->SetLevelImage(target, 0, image.get());
-  GLint texture_memory_size = 0;
-  api->glGetTexParameterivFn(target, GL_MEMORY_SIZE_ANGLE,
-                             &texture_memory_size);
-  texture->SetEstimatedSize(texture_memory_size);
-
-  return std::make_unique<SharedImageBackingD3D>(
-      mailbox, format, size, color_space, surface_origin, alpha_type, usage,
-      std::move(swap_chain), std::move(texture), std::move(image), buffer_index,
-      std::move(d3d11_texture), std::move(shared_handle),
-      std::move(dxgi_keyed_mutex));
-}
-
 SharedImageBackingFactoryD3D::SwapChainBackings
 SharedImageBackingFactoryD3D::CreateSwapChain(
     const Mailbox& front_buffer_mailbox,
@@ -263,18 +171,30 @@
   if (!ClearBackBuffer(swap_chain, d3d11_device_))
     return {nullptr, nullptr};
 
-  auto back_buffer_backing = MakeBacking(
+  Microsoft::WRL::ComPtr<ID3D11Texture2D> back_buffer_texture;
+  hr = swap_chain->GetBuffer(0, IID_PPV_ARGS(&back_buffer_texture));
+  if (FAILED(hr)) {
+    DLOG(ERROR) << "GetBuffer failed with error " << std::hex;
+    return {nullptr, nullptr};
+  }
+  auto back_buffer_backing = SharedImageBackingD3D::CreateFromSwapChainBuffer(
       back_buffer_mailbox, format, size, color_space, surface_origin,
-      alpha_type, usage, swap_chain, 0 /* buffer_index */,
-      nullptr /* d3d11_texture */, base::win::ScopedHandle());
+      alpha_type, usage, std::move(back_buffer_texture), swap_chain,
+      /*buffer_index=*/0);
   if (!back_buffer_backing)
     return {nullptr, nullptr};
   back_buffer_backing->SetCleared();
 
-  auto front_buffer_backing = MakeBacking(
+  Microsoft::WRL::ComPtr<ID3D11Texture2D> front_buffer_texture;
+  hr = swap_chain->GetBuffer(1, IID_PPV_ARGS(&front_buffer_texture));
+  if (FAILED(hr)) {
+    DLOG(ERROR) << "GetBuffer failed with error " << std::hex;
+    return {nullptr, nullptr};
+  }
+  auto front_buffer_backing = SharedImageBackingD3D::CreateFromSwapChainBuffer(
       front_buffer_mailbox, format, size, color_space, surface_origin,
-      alpha_type, usage, swap_chain, 1 /* buffer_index */,
-      nullptr /* d3d11_texture */, base::win::ScopedHandle());
+      alpha_type, usage, std::move(front_buffer_texture), swap_chain,
+      /*buffer_index=*/1);
   if (!front_buffer_backing)
     return {nullptr, nullptr};
   front_buffer_backing->SetCleared();
@@ -345,14 +265,12 @@
                 << std::hex << hr;
     return nullptr;
   }
-
   // Put the shared handle into an RAII object as quickly as possible to
   // ensure we do not leak it.
   base::win::ScopedHandle scoped_shared_handle(shared_handle);
-
-  return MakeBacking(mailbox, format, size, color_space, surface_origin,
-                     alpha_type, usage, nullptr, 0, std::move(d3d11_texture),
-                     std::move(scoped_shared_handle));
+  return SharedImageBackingD3D::CreateFromSharedHandle(
+      mailbox, format, size, color_space, surface_origin, alpha_type, usage,
+      std::move(d3d11_texture), std::move(scoped_shared_handle));
 }
 
 std::unique_ptr<SharedImageBacking>
@@ -427,11 +345,10 @@
     return nullptr;
   }
 
-  auto backing =
-      MakeBacking(mailbox, viz::GetResourceFormat(format), size, color_space,
-                  surface_origin, alpha_type, usage, /*swap_chain=*/nullptr,
-                  /*buffer_index=*/0, std::move(d3d11_texture),
-                  std::move(handle.dxgi_handle));
+  auto backing = SharedImageBackingD3D::CreateFromSharedHandle(
+      mailbox, viz::GetResourceFormat(format), size, color_space,
+      surface_origin, alpha_type, usage, std::move(d3d11_texture),
+      std::move(handle.dxgi_handle));
   if (backing)
     backing->SetCleared();
   return backing;
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_d3d_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_d3d_unittest.cc
index 356d812..c84072b 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_d3d_unittest.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_d3d_unittest.cc
@@ -33,6 +33,15 @@
 #include <dawn_native/DawnNative.h>
 #endif  // BUILDFLAG(USE_DAWN)
 
+#define SCOPED_GL_CLEANUP_VAR(api, func, var)            \
+  base::ScopedClosureRunner delete_##var(base::BindOnce( \
+      [](gl::GLApi* api, GLuint var) { api->gl##func##Fn(var); }, api, var))
+
+#define SCOPED_GL_CLEANUP_PTR(api, func, n, var)                           \
+  base::ScopedClosureRunner delete_##var(base::BindOnce(                   \
+      [](gl::GLApi* api, GLuint var) { api->gl##func##Fn(n, &var); }, api, \
+      var))
+
 namespace gpu {
 namespace {
 
@@ -452,6 +461,8 @@
     }
   }
 
+  void RunVideoTest(bool use_shared_handle);
+
   scoped_refptr<SharedContextState> context_state_;
 };
 
@@ -1038,5 +1049,248 @@
 }
 #endif  // BUILDFLAG(USE_DAWN)
 
+void SharedImageBackingFactoryD3DTest::RunVideoTest(bool use_shared_handle) {
+  if (!IsD3DSharedImageSupported())
+    return;
+
+  Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
+      shared_image_factory_->GetDeviceForTesting();
+
+  const gfx::Size size(32, 32);
+
+  const unsigned kYFillValue = 0x12;
+  const unsigned kUFillValue = 0x23;
+  const unsigned kVFillValue = 0x34;
+
+  const size_t kYPlaneSize = size.width() * size.height();
+
+  std::vector<unsigned char> video_data;
+  video_data.resize(kYPlaneSize * 3 / 2);
+  memset(video_data.data(), kYFillValue, kYPlaneSize);
+  for (size_t i = 0; i < kYPlaneSize / 2; i += 2) {
+    video_data[kYPlaneSize + i] = kUFillValue;
+    video_data[kYPlaneSize + i + 1] = kVFillValue;
+  }
+
+  D3D11_SUBRESOURCE_DATA data = {};
+  data.pSysMem = static_cast<const void*>(video_data.data());
+  data.SysMemPitch = static_cast<UINT>(size.width());
+
+  CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_NV12, size.width(), size.height(), 1,
+                             1, D3D11_BIND_SHADER_RESOURCE);
+  if (use_shared_handle) {
+    desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE |
+                     D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+  }
+
+  Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
+  HRESULT hr = d3d11_device->CreateTexture2D(&desc, &data, &d3d11_texture);
+  ASSERT_TRUE(SUCCEEDED(hr));
+
+  uint32_t usage =
+      gpu::SHARED_IMAGE_USAGE_VIDEO_DECODE | gpu::SHARED_IMAGE_USAGE_GLES2 |
+      gpu::SHARED_IMAGE_USAGE_RASTER | gpu::SHARED_IMAGE_USAGE_DISPLAY |
+      gpu::SHARED_IMAGE_USAGE_SCANOUT;
+
+  base::win::ScopedHandle shared_handle;
+  if (use_shared_handle) {
+    Microsoft::WRL::ComPtr<IDXGIResource1> dxgi_resource;
+    hr = d3d11_texture.As(&dxgi_resource);
+    ASSERT_TRUE(SUCCEEDED(hr));
+
+    HANDLE handle;
+    hr = dxgi_resource->CreateSharedHandle(
+        nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE,
+        nullptr, &handle);
+    ASSERT_TRUE(SUCCEEDED(hr));
+
+    shared_handle.Set(handle);
+    ASSERT_TRUE(shared_handle.IsValid());
+
+    usage |= gpu::SHARED_IMAGE_USAGE_WEBGPU;
+  }
+
+  const gpu::Mailbox mailboxes[] = {gpu::Mailbox::GenerateForSharedImage(),
+                                    gpu::Mailbox::GenerateForSharedImage()};
+
+  auto shared_image_backings = SharedImageBackingD3D::CreateFromVideoTexture(
+      mailboxes, DXGI_FORMAT_NV12, size, usage, d3d11_texture,
+      /*array_slice=*/0, std::move(shared_handle));
+  ASSERT_EQ(shared_image_backings.size(), 2u);
+
+  const gfx::Size plane_sizes[] = {
+      size, gfx::Size(size.width() / 2, size.height() / 2)};
+  const viz::ResourceFormat plane_formats[] = {viz::RED_8, viz::RG_88};
+
+  std::vector<std::unique_ptr<SharedImageRepresentationFactoryRef>>
+      shared_image_refs;
+  for (size_t i = 0; i < shared_image_backings.size(); i++) {
+    auto& backing = shared_image_backings[i];
+
+    EXPECT_EQ(backing->mailbox(), mailboxes[i]);
+    EXPECT_EQ(backing->size(), plane_sizes[i]);
+    EXPECT_EQ(backing->format(), plane_formats[i]);
+    EXPECT_EQ(backing->color_space(), gfx::ColorSpace());
+    EXPECT_EQ(backing->surface_origin(), kTopLeft_GrSurfaceOrigin);
+    EXPECT_EQ(backing->alpha_type(), kPremul_SkAlphaType);
+    EXPECT_EQ(backing->usage(), usage);
+    EXPECT_TRUE(backing->IsCleared());
+
+    shared_image_refs.push_back(shared_image_manager_.Register(
+        std::move(backing), memory_type_tracker_.get()));
+  }
+
+  // Setup GL shaders, framebuffers, uniforms, etc.
+  static const char* kVideoFragmentShaderSrc =
+      "#extension GL_OES_EGL_image_external : require\n"
+      "precision mediump float;\n"
+      "uniform samplerExternalOES u_texture_y;\n"
+      "uniform samplerExternalOES u_texture_uv;\n"
+      "varying vec2 v_texCoord;\n"
+      "void main() {\n"
+      "  gl_FragColor.r = texture2D(u_texture_y, v_texCoord).r;\n"
+      "  gl_FragColor.gb = texture2D(u_texture_uv, v_texCoord).rg;\n"
+      "  gl_FragColor.a = 1.0;\n"
+      "}\n";
+
+  gl::GLApi* api = gl::g_current_gl_context;
+
+  GLint status = 0;
+  GLuint vertex_shader = api->glCreateShaderFn(GL_VERTEX_SHADER);
+  SCOPED_GL_CLEANUP_VAR(api, DeleteShader, vertex_shader);
+  ASSERT_NE(vertex_shader, 0u);
+  api->glShaderSourceFn(vertex_shader, 1, &kVertexShaderSrc, nullptr);
+  api->glCompileShaderFn(vertex_shader);
+  api->glGetShaderivFn(vertex_shader, GL_COMPILE_STATUS, &status);
+  ASSERT_NE(status, 0);
+
+  GLuint fragment_shader = api->glCreateShaderFn(GL_FRAGMENT_SHADER);
+  SCOPED_GL_CLEANUP_VAR(api, DeleteShader, fragment_shader);
+  ASSERT_NE(fragment_shader, 0u);
+  api->glShaderSourceFn(fragment_shader, 1, &kVideoFragmentShaderSrc, nullptr);
+  api->glCompileShaderFn(fragment_shader);
+  api->glGetShaderivFn(fragment_shader, GL_COMPILE_STATUS, &status);
+  ASSERT_NE(status, 0);
+
+  GLuint program = api->glCreateProgramFn();
+  ASSERT_NE(program, 0u);
+  SCOPED_GL_CLEANUP_VAR(api, DeleteProgram, program);
+  api->glAttachShaderFn(program, vertex_shader);
+  api->glAttachShaderFn(program, fragment_shader);
+  api->glLinkProgramFn(program);
+  api->glGetProgramivFn(program, GL_LINK_STATUS, &status);
+  ASSERT_NE(status, 0);
+
+  GLint vertex_location = api->glGetAttribLocationFn(program, "a_position");
+  ASSERT_NE(vertex_location, -1);
+
+  GLint y_texture_location =
+      api->glGetUniformLocationFn(program, "u_texture_y");
+  ASSERT_NE(y_texture_location, -1);
+
+  GLint uv_texture_location =
+      api->glGetUniformLocationFn(program, "u_texture_uv");
+  ASSERT_NE(uv_texture_location, -1);
+
+  GLuint fbo, renderbuffer = 0u;
+  api->glGenFramebuffersEXTFn(1, &fbo);
+  ASSERT_NE(fbo, 0u);
+  SCOPED_GL_CLEANUP_PTR(api, DeleteFramebuffersEXT, 1, fbo);
+  api->glBindFramebufferEXTFn(GL_FRAMEBUFFER, fbo);
+
+  api->glGenRenderbuffersEXTFn(1, &renderbuffer);
+  ASSERT_NE(renderbuffer, 0u);
+  SCOPED_GL_CLEANUP_PTR(api, DeleteRenderbuffersEXT, 1, renderbuffer);
+  api->glBindRenderbufferEXTFn(GL_RENDERBUFFER, renderbuffer);
+
+  api->glRenderbufferStorageEXTFn(GL_RENDERBUFFER, GL_RGBA8_OES, size.width(),
+                                  size.height());
+  api->glFramebufferRenderbufferEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                                      GL_RENDERBUFFER, renderbuffer);
+  ASSERT_EQ(api->glCheckFramebufferStatusEXTFn(GL_FRAMEBUFFER),
+            static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE));
+
+  // Set the clear color to green.
+  api->glViewportFn(0, 0, size.width(), size.height());
+  api->glClearColorFn(0.0f, 1.0f, 0.0f, 1.0f);
+  api->glClearFn(GL_COLOR_BUFFER_BIT);
+
+  GLuint vbo = 0u;
+  api->glGenBuffersARBFn(1, &vbo);
+  ASSERT_NE(vbo, 0u);
+  SCOPED_GL_CLEANUP_PTR(api, DeleteBuffersARB, 1, vbo);
+  api->glBindBufferFn(GL_ARRAY_BUFFER, vbo);
+  static const float vertices[] = {
+      1.0f, 1.0f, -1.0f, 1.0f,  -1.0f, -1.0f,
+      1.0f, 1.0f, -1.0f, -1.0f, 1.0f,  -1.0f,
+  };
+  api->glBufferDataFn(GL_ARRAY_BUFFER, sizeof(vertices), vertices,
+                      GL_STATIC_DRAW);
+
+  ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
+
+  // Create the representations for the planes, get the texture ids, bind to
+  // samplers, and draw.
+  {
+    auto y_texture =
+        shared_image_representation_factory_->ProduceGLTexturePassthrough(
+            mailboxes[0]);
+    ASSERT_NE(y_texture, nullptr);
+
+    auto uv_texture =
+        shared_image_representation_factory_->ProduceGLTexturePassthrough(
+            mailboxes[1]);
+    ASSERT_NE(uv_texture, nullptr);
+
+    auto y_texture_access = y_texture->BeginScopedAccess(
+        GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM,
+        SharedImageRepresentation::AllowUnclearedAccess::kNo);
+    ASSERT_NE(y_texture_access, nullptr);
+
+    auto uv_texture_access = uv_texture->BeginScopedAccess(
+        GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM,
+        SharedImageRepresentation::AllowUnclearedAccess::kNo);
+    ASSERT_NE(uv_texture_access, nullptr);
+
+    api->glActiveTextureFn(GL_TEXTURE0);
+    api->glBindTextureFn(GL_TEXTURE_EXTERNAL_OES,
+                         y_texture->GetTextureBase()->service_id());
+    ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
+
+    api->glActiveTextureFn(GL_TEXTURE1);
+    api->glBindTextureFn(GL_TEXTURE_EXTERNAL_OES,
+                         uv_texture->GetTextureBase()->service_id());
+    ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
+
+    api->glUseProgramFn(program);
+
+    api->glEnableVertexAttribArrayFn(vertex_location);
+    api->glVertexAttribPointerFn(vertex_location, 2, GL_FLOAT, GL_FALSE, 0, 0);
+
+    api->glUniform1iFn(y_texture_location, 0);
+    api->glUniform1iFn(uv_texture_location, 1);
+
+    api->glDrawArraysFn(GL_TRIANGLES, 0, 6);
+    ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
+
+    GLubyte pixel_color[4];
+    api->glReadPixelsFn(size.width() / 2, size.height() / 2, 1, 1, GL_RGBA,
+                        GL_UNSIGNED_BYTE, pixel_color);
+    EXPECT_EQ(kYFillValue, pixel_color[0]);
+    EXPECT_EQ(kUFillValue, pixel_color[1]);
+    EXPECT_EQ(kVFillValue, pixel_color[2]);
+    EXPECT_EQ(0xff, pixel_color[3]);
+  }
+  // TODO(dawn:551): Test Dawn access after multi-planar support lands in Dawn.
+}
+
+TEST_F(SharedImageBackingFactoryD3DTest, CreateFromVideoTexture) {
+  RunVideoTest(/*use_shared_handle=*/false);
+}
+
+TEST_F(SharedImageBackingFactoryD3DTest, CreateFromVideoTextureSharedHandle) {
+  RunVideoTest(/*use_shared_handle=*/true);
+}
+
 }  // anonymous namespace
 }  // namespace gpu
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 4e53e875..50b8c017 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -22283,7 +22283,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -38598,7 +38598,7 @@
       dimensions: "builder:chromeos-arm-generic-rel"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index d556007..d73f87ff 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -1742,6 +1742,7 @@
         category = "simple|release",
         short_name = "arm",
     ),
+    os = os.LINUX_BIONIC_REMOVE,
     cq_mirrors_console_view = "mirrors",
     main_console_view = "main",
 )
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index 5c7888b..79e17ee 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -773,6 +773,7 @@
     branch_selector = branches.ALL_BRANCHES,
     builderless = not settings.is_main,
     main_list_view = "try",
+    os = os.LINUX_BIONIC_REMOVE,
     tryjob = try_.job(),
 )
 
diff --git a/ios/chrome/app/application_delegate/app_state.mm b/ios/chrome/app/application_delegate/app_state.mm
index 01785a4..2c790265 100644
--- a/ios/chrome/app/application_delegate/app_state.mm
+++ b/ios/chrome/app/application_delegate/app_state.mm
@@ -671,8 +671,6 @@
     // Complete the transition out of safe mode if the app was really in safe
     // mode.
     [self.observers appStateDidExitSafeMode:self];
-    [_mainApplicationDelegate
-        applicationDidBecomeActive:[UIApplication sharedApplication]];
   }
 
   if (EnableSyntheticCrashReportsForUte()) {
diff --git a/ios/chrome/app/application_delegate/app_state_unittest.mm b/ios/chrome/app/application_delegate/app_state_unittest.mm
index 8902958e..8f7f667a 100644
--- a/ios/chrome/app/application_delegate/app_state_unittest.mm
+++ b/ios/chrome/app/application_delegate/app_state_unittest.mm
@@ -527,9 +527,6 @@
   [[appStateObserverMock expect] appStateDidExitSafeMode:appState];
   [[browserLauncherMock expect]
       startUpBrowserToStage:INITIALIZATION_STAGE_FOREGROUND];
-  id applicationDelegateMock = getApplicationDelegateMock();
-  [[applicationDelegateMock expect]
-      applicationDidBecomeActive:[UIApplication sharedApplication]];
 
   swizzleSafeModeShouldStart(YES);
 
@@ -553,7 +550,6 @@
   EXPECT_OCMOCK_VERIFY(windowMock);
   EXPECT_OCMOCK_VERIFY(browserLauncherMock);
   EXPECT_OCMOCK_VERIFY(appStateObserverMock);
-  EXPECT_OCMOCK_VERIFY(applicationDelegateMock);
 
   EXPECT_EQ(InitStageFinal, appState.initStage);
 }
@@ -580,10 +576,6 @@
 
   [appState addObserver:appStateObserverMock];
 
-  id applicationDelegateMock = getApplicationDelegateMock();
-  [[applicationDelegateMock expect]
-      applicationDidBecomeActive:[UIApplication sharedApplication]];
-
   id browserLauncherMock = getBrowserLauncherMock();
   BrowserInitializationStageType stageForeground =
       INITIALIZATION_STAGE_FOREGROUND;
diff --git a/ios/chrome/app/strings/ios_chromium_strings.grd b/ios/chrome/app/strings/ios_chromium_strings.grd
index 27df413..e5aabb02 100644
--- a/ios/chrome/app/strings/ios_chromium_strings.grd
+++ b/ios/chrome/app/strings/ios_chromium_strings.grd
@@ -244,6 +244,12 @@
       <message name="IDS_IOS_FACE_ID_USAGE_DESCRIPTION" desc="Specifies the reason for using the device's Face ID capabilities.">
         Chromium uses Face ID to ensure authorized access to your passwords.
       </message>
+      <message name="IDS_IOS_FIRST_RUN_SIGNIN_SUBTITLE" desc="Subtitle of the screen suggesting to the user to sign in">
+        To get the most out of Chromium, sign in with your Google Account
+      </message>
+      <message name="IDS_IOS_FIRST_RUN_SIGNIN_TITLE" desc="Title of the screen suggesting to the user to sign in">
+        Make Chromium Your Own
+      </message>
       <message name="IDS_IOS_FIRSTRUN_AGREE_TO_TERMS" desc="Label containing a link to the Terms of Service, displayed in the first run flow. [Length: 117em] [iOS only]">
         By using Chromium, you agree to the <ph name="BEGIN_LINK_TOS">BEGIN_LINK_TOS</ph>Terms of Service<ph name="END_LINK_TOS">END_LINK_TOS</ph>.
       </message>
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_SUBTITLE.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_SUBTITLE.png.sha1
new file mode 100644
index 0000000..616073f0
--- /dev/null
+++ b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_SUBTITLE.png.sha1
@@ -0,0 +1 @@
+6dcf952950641904977ce7a0f63709e2f0079503
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_TITLE.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_TITLE.png.sha1
new file mode 100644
index 0000000..616073f0
--- /dev/null
+++ b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_TITLE.png.sha1
@@ -0,0 +1 @@
+6dcf952950641904977ce7a0f63709e2f0079503
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings.grd b/ios/chrome/app/strings/ios_google_chrome_strings.grd
index a64887d..6c3df739 100644
--- a/ios/chrome/app/strings/ios_google_chrome_strings.grd
+++ b/ios/chrome/app/strings/ios_google_chrome_strings.grd
@@ -244,6 +244,12 @@
       <message name="IDS_IOS_FACE_ID_USAGE_DESCRIPTION" desc="Specifies the reason for using the device's Face ID capabilities.">
         Chrome uses Face ID to ensure authorized access to your passwords.
       </message>
+      <message name="IDS_IOS_FIRST_RUN_SIGNIN_SUBTITLE" desc="Subtitle of the screen suggesting to the user to sign in">
+        To get the most out of Chrome, sign in with your Google Account
+      </message>
+      <message name="IDS_IOS_FIRST_RUN_SIGNIN_TITLE" desc="Title of the screen suggesting to the user to sign in">
+        Make Chrome Your Own
+      </message>
       <message name="IDS_IOS_FIRSTRUN_AGREE_TO_TERMS" desc="Label containing a link to the Terms of Service, displayed in the first run flow. [Length: 117em] [iOS only]">
       By using Chrome, you agree to the <ph name="BEGIN_LINK_TOS">BEGIN_LINK_TOS</ph>Terms of Service<ph name="END_LINK_TOS">END_LINK_TOS</ph>.
       </message>
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_SUBTITLE.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_SUBTITLE.png.sha1
new file mode 100644
index 0000000..616073f0
--- /dev/null
+++ b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_SUBTITLE.png.sha1
@@ -0,0 +1 @@
+6dcf952950641904977ce7a0f63709e2f0079503
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_TITLE.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_TITLE.png.sha1
new file mode 100644
index 0000000..616073f0
--- /dev/null
+++ b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_TITLE.png.sha1
@@ -0,0 +1 @@
+6dcf952950641904977ce7a0f63709e2f0079503
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 92edee7..d34a3b6 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -858,6 +858,15 @@
       <message name="IDS_IOS_PHONE_CALL_BUTTON" desc="Text in the confirmation dialog button that will initiate a phone call for the presented number. [Length: 10em] [iOS only]">
         Call
       </message>
+      <message name="IDS_IOS_FIRST_RUN_SIGNIN_CONTINUE_AS" desc="Label of the button which the user can tap to continue the process using the selected account (referred by the first name of the account owner)">
+        Continue as <ph name="FIRST_RUN_ACCOUNT_NAME">$1<ex>Jessica</ex></ph>
+      </message>
+      <message name="IDS_IOS_FIRST_RUN_SIGNIN_DONT_SIGN_IN" desc="Label of the button which can be used to not sign in and go to the next screen">
+        Don't Sign In
+      </message>
+      <message name="IDS_IOS_FIRST_RUN_SIGNIN_SIGN_IN_ACTION" desc="Label of the button suggesting to the user to sign in if they don't have any account on the device">
+        Sign In…
+      </message>
       <message name="IDS_IOS_FIRST_RUN_SYNC_SCREEN_TITLE" desc="Title on the sync screen presented to the user on First Run [iOS only]">
         Sync Between Your Devices
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_CONTINUE_AS.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_CONTINUE_AS.png.sha1
new file mode 100644
index 0000000..616073f0
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_CONTINUE_AS.png.sha1
@@ -0,0 +1 @@
+6dcf952950641904977ce7a0f63709e2f0079503
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_DONT_SIGN_IN.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_DONT_SIGN_IN.png.sha1
new file mode 100644
index 0000000..616073f0
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_DONT_SIGN_IN.png.sha1
@@ -0,0 +1 @@
+6dcf952950641904977ce7a0f63709e2f0079503
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_SIGN_IN_ACTION.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_SIGN_IN_ACTION.png.sha1
new file mode 100644
index 0000000..fe069d0
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_SIGNIN_SIGN_IN_ACTION.png.sha1
@@ -0,0 +1 @@
+192bb12d5ddb38ac0d3fb70a950f8b9cf3de6f6b
\ No newline at end of file
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_default_account/BUILD.gn b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_default_account/BUILD.gn
index f3f7da2..866ecbf2 100644
--- a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_default_account/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_default_account/BUILD.gn
@@ -23,6 +23,7 @@
     "//ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/bottom_sheet",
     "//ios/chrome/browser/ui/authentication/views",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
+    "//ios/chrome/browser/ui/util",
     "//ios/chrome/common/ui/colors",
     "//ios/chrome/common/ui/util",
     "//ios/public/provider/chrome/browser",
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_default_account/consistency_default_account_view_controller.mm b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_default_account/consistency_default_account_view_controller.mm
index 60e4543..3e71e25 100644
--- a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_default_account/consistency_default_account_view_controller.mm
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_default_account/consistency_default_account_view_controller.mm
@@ -6,6 +6,7 @@
 
 #import "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/ui/authentication/views/identity_button_control.h"
+#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui/util/button_util.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
 #import "ios/chrome/common/ui/util/pointer_interaction_util.h"
@@ -183,6 +184,10 @@
   // the "continue as" button.
   self.identityButtonControl.layer.cornerRadius =
       self.continueAsButton.layer.cornerRadius;
+
+  // Ensure that keyboard is hidden.
+  UIResponder* firstResponder = GetFirstResponder();
+  [firstResponder resignFirstResponder];
 }
 
 #pragma mark - UI actions
diff --git a/ios/chrome/browser/ui/browser_view/BUILD.gn b/ios/chrome/browser/ui/browser_view/BUILD.gn
index 70d508e..02053d95 100644
--- a/ios/chrome/browser/ui/browser_view/BUILD.gn
+++ b/ios/chrome/browser/ui/browser_view/BUILD.gn
@@ -98,6 +98,7 @@
     "//ios/chrome/browser/ui/content_suggestions:content_suggestions_constant",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/default_promo",
+    "//ios/chrome/browser/ui/default_promo:default_promo_ui",
     "//ios/chrome/browser/ui/dialogs:dialogs_internal",
     "//ios/chrome/browser/ui/download",
     "//ios/chrome/browser/ui/elements:elements_internal",
diff --git a/ios/chrome/browser/ui/commands/application_commands.h b/ios/chrome/browser/ui/commands/application_commands.h
index 2d07f9d..97c51bb1 100644
--- a/ios/chrome/browser/ui/commands/application_commands.h
+++ b/ios/chrome/browser/ui/commands/application_commands.h
@@ -63,6 +63,11 @@
 - (void)showCreditCardSettingsFromViewController:
     (UIViewController*)baseViewController;
 
+// Shows the settings page informing the user how to set Chrome as the default
+// browser.
+- (void)showDefaultBrowserSettingsFromViewController:
+    (UIViewController*)baseViewController;
+
 @end
 
 // Protocol for commands that will generally be handled by the application,
diff --git a/ios/chrome/browser/ui/default_promo/BUILD.gn b/ios/chrome/browser/ui/default_promo/BUILD.gn
index 0e91485..c0bf323 100644
--- a/ios/chrome/browser/ui/default_promo/BUILD.gn
+++ b/ios/chrome/browser/ui/default_promo/BUILD.gn
@@ -22,8 +22,6 @@
     "default_browser_promo_coordinator.h",
     "default_browser_promo_coordinator.mm",
     "default_browser_promo_non_modal_commands.h",
-    "default_browser_promo_non_modal_coordinator.h",
-    "default_browser_promo_non_modal_coordinator.mm",
     "default_browser_promo_non_modal_scheduler.h",
     "default_browser_promo_non_modal_scheduler.mm",
     "default_browser_promo_view_controller.h",
@@ -50,8 +48,6 @@
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/default_promo/resources",
-    "//ios/chrome/browser/ui/infobars/banners",
-    "//ios/chrome/browser/ui/infobars/coordinators",
     "//ios/chrome/browser/ui/main:scene_state_header",
     "//ios/chrome/browser/ui/main:scene_state_observer",
     "//ios/chrome/browser/web_state_list",
@@ -65,6 +61,29 @@
   frameworks = [ "UIKit.framework" ]
 }
 
+source_set("default_promo_ui") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "default_browser_promo_non_modal_coordinator.h",
+    "default_browser_promo_non_modal_coordinator.mm",
+  ]
+  deps = [
+    ":default_promo",
+    "//base",
+    "//ios/chrome/app/strings:ios_google_chrome_strings_grit",
+    "//ios/chrome/app/strings:ios_strings_grit",
+    "//ios/chrome/browser/main:public",
+    "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/infobars/banners",
+    "//ios/chrome/browser/ui/infobars/coordinators",
+    "//ios/chrome/browser/ui/main:default_browser_scene_agent",
+    "//ios/chrome/browser/ui/main:scene_state_header",
+    "//ios/public/provider/chrome/browser",
+    "//ios/public/provider/chrome/browser/images",
+    "//ui/base",
+  ]
+}
+
 source_set("unit_tests") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
diff --git a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_coordinator.mm b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_coordinator.mm
index 8a4bdf7..ac1c81f1 100644
--- a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_coordinator.mm
+++ b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_coordinator.mm
@@ -7,9 +7,12 @@
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_commands.h"
+#import "ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h"
 #import "ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.h"
 #import "ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator+subclassing.h"
 #import "ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator_implementation.h"
+#import "ios/chrome/browser/ui/main/default_browser_scene_agent.h"
+#import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
 #include "ios/chrome/grit/ios_google_chrome_strings.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
@@ -70,6 +73,12 @@
   }
 }
 
+// Overrides the superclass's implementation because that requires an
+// infobarDelegate, which this class doesn't have.
+- (void)bannerInfobarButtonWasPressed:(id)sender {
+  [self performInfobarAction];
+}
+
 #pragma mark - InfobarCoordinatorImplementation
 
 - (BOOL)configureModalViewController {
@@ -103,6 +112,11 @@
 
 - (void)performInfobarAction {
   self.infobarAccepted = YES;
+  SceneState* sceneState =
+      SceneStateBrowserAgent::FromBrowser(self.browser)->GetSceneState();
+  DefaultBrowserSceneAgent* agent =
+      [DefaultBrowserSceneAgent agentFromScene:sceneState];
+  [agent.nonModalScheduler logUserPerformedPromoAction];
 }
 
 - (void)infobarBannerWillBeDismissed:(BOOL)userInitiated {
diff --git a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h
index 3f69e39..51a82bb 100644
--- a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h
+++ b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h
@@ -42,6 +42,9 @@
 // Handles presenting the popup menu, dismissing the promo.
 - (void)logPopupMenuEntered;
 
+// Handles the user performing the promo action.
+- (void)logUserPerformedPromoAction;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_DEFAULT_PROMO_DEFAULT_BROWSER_PROMO_NON_MODAL_SCHEDULER_H_
diff --git a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm
index 1eb23b6..8f7c50bf 100644
--- a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm
+++ b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm
@@ -6,6 +6,7 @@
 
 #import "ios/chrome/browser/overlays/public/overlay_presenter.h"
 #import "ios/chrome/browser/overlays/public/overlay_presenter_observer_bridge.h"
+#import "ios/chrome/browser/ui/commands/application_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_commands.h"
 #import "ios/chrome/browser/ui/default_promo/default_browser_utils.h"
@@ -93,6 +94,20 @@
   [self dismissPromoAnimated:YES];
 }
 
+- (void)logUserPerformedPromoAction {
+  if (NonModalPromosInstructionsEnabled()) {
+    id<ApplicationSettingsCommands> handler =
+        HandlerForProtocol(self.dispatcher, ApplicationSettingsCommands);
+    [handler showDefaultBrowserSettingsFromViewController:nil];
+  } else {
+    NSURL* settingsURL =
+        [NSURL URLWithString:UIApplicationOpenSettingsURLString];
+    [[UIApplication sharedApplication] openURL:settingsURL
+                                       options:{}
+                             completionHandler:nil];
+  }
+}
+
 - (void)dismissPromoAnimated:(BOOL)animated {
   [self cancelDismissPromoTimer];
   [self.handler dismissDefaultBrowserNonModalPromoAnimated:animated];
diff --git a/ios/chrome/browser/ui/first_run/first_run_screen_view_controller.h b/ios/chrome/browser/ui/first_run/first_run_screen_view_controller.h
index 7c1a8f3..169a90b 100644
--- a/ios/chrome/browser/ui/first_run/first_run_screen_view_controller.h
+++ b/ios/chrome/browser/ui/first_run/first_run_screen_view_controller.h
@@ -51,6 +51,10 @@
 // NO.
 @property(nonatomic, assign) BOOL scrollToEndMandatory;
 
+// When set to YES, the screen can be dismissed by the user by swiping the view
+// down. Defaults to NO. Has no effect on iOS 12.
+@property(nonatomic, assign) BOOL canDismissScreen;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_FIRST_RUN_FIRST_RUN_SCREEN_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/first_run/first_run_screen_view_controller.mm b/ios/chrome/browser/ui/first_run/first_run_screen_view_controller.mm
index a416112..cbf799b 100644
--- a/ios/chrome/browser/ui/first_run/first_run_screen_view_controller.mm
+++ b/ios/chrome/browser/ui/first_run/first_run_screen_view_controller.mm
@@ -62,6 +62,10 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
 
+  if (@available(iOS 13.4, *)) {
+    self.modalInPresentation = !self.canDismissScreen;
+  }
+
   self.view.backgroundColor = [UIColor colorNamed:kBackgroundColor];
 
   // Create a layout guide for the margin between the subtitle and the screen-
@@ -383,6 +387,7 @@
         kFirstRunPrimaryActionAccessibilityIdentifier;
     _primaryActionButton.titleEdgeInsets =
         UIEdgeInsetsMake(0, kMoreArrowMargin, 0, kMoreArrowMargin);
+    _primaryActionButton.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
     [_primaryActionButton addTarget:self
                              action:@selector(didTapPrimaryActionButton)
                    forControlEvents:UIControlEventTouchUpInside];
@@ -433,6 +438,7 @@
   button.translatesAutoresizingMaskIntoConstraints = NO;
   button.titleLabel.adjustsFontForContentSizeCategory = YES;
   button.accessibilityIdentifier = accessibilityIdentifier;
+  button.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
 
   if (@available(iOS 13.4, *)) {
     button.pointerInteractionEnabled = YES;
diff --git a/ios/chrome/browser/ui/first_run/signin/BUILD.gn b/ios/chrome/browser/ui/first_run/signin/BUILD.gn
index e729fc6..1d51910cb 100644
--- a/ios/chrome/browser/ui/first_run/signin/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/signin/BUILD.gn
@@ -37,11 +37,13 @@
     "signin_screen_view_controller.mm",
   ]
   deps = [
+    "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui/authentication",
     "//ios/chrome/browser/ui/authentication/views",
     "//ios/chrome/browser/ui/elements:elements_internal",
     "//ios/chrome/browser/ui/first_run:first_run_ui",
     "//ios/chrome/common/ui/util",
+    "//ui/base",
   ]
 }
 
diff --git a/ios/chrome/browser/ui/first_run/signin/signin_screen_consumer.h b/ios/chrome/browser/ui/first_run/signin/signin_screen_consumer.h
index 63f09043..7149e36 100644
--- a/ios/chrome/browser/ui/first_run/signin/signin_screen_consumer.h
+++ b/ios/chrome/browser/ui/first_run/signin/signin_screen_consumer.h
@@ -14,11 +14,13 @@
 // default image.
 - (void)setUserImage:(UIImage*)userImage;
 
-// Sets the |userName| and its |email| of the selected identity.
+// Sets the |userName| and its |email| of the selected identity. Notifies the UI
+// that an identity is available.
 - (void)setSelectedIdentityUserName:(NSString*)userName email:(NSString*)email;
 
-// Hides the identity control.
-- (void)hideIdentityButtonControl;
+// Notifies the consumer that no identity is available and that the UI should be
+// updated accordingly.
+- (void)noIdentityAvailable;
 
 // Sets the UI as interactable or not.
 - (void)setUIEnabled:(BOOL)UIEnabled;
diff --git a/ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.mm b/ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.mm
index 0291d23a..d386f10 100644
--- a/ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.mm
+++ b/ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.mm
@@ -4,6 +4,7 @@
 
 #import "ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.h"
 
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/first_run/first_run_metrics.h"
 #include "ios/chrome/browser/main/browser.h"
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
@@ -77,6 +78,15 @@
   self.viewController = [[SigninScreenViewController alloc] init];
   self.viewController.delegate = self;
   self.mediator = [[SigninScreenMediator alloc] init];
+  NSArray* identities = ios::GetChromeBrowserProvider()
+                            ->GetChromeIdentityService()
+                            ->GetAllIdentitiesSortedForDisplay(
+                                self.browser->GetBrowserState()->GetPrefs());
+  ChromeIdentity* newIdentity = nil;
+  if (identities.count != 0) {
+    newIdentity = identities[0];
+  }
+  self.mediator.selectedIdentity = newIdentity;
   self.mediator.consumer = self.viewController;
   self.mediator.delegate = self;
   BOOL animated = self.baseNavigationController.topViewController != nil;
diff --git a/ios/chrome/browser/ui/first_run/signin/signin_screen_mediator.mm b/ios/chrome/browser/ui/first_run/signin/signin_screen_mediator.mm
index 65f98d2..958ee76 100644
--- a/ios/chrome/browser/ui/first_run/signin/signin_screen_mediator.mm
+++ b/ios/chrome/browser/ui/first_run/signin/signin_screen_mediator.mm
@@ -151,7 +151,7 @@
           [weakSelf.consumer setUserImage:identityAvatar];
         });
   } else {
-    [self.consumer hideIdentityButtonControl];
+    [self.consumer noIdentityAvailable];
   }
 
   // TODO(crbug.com/1189836): Update the buttons.
diff --git a/ios/chrome/browser/ui/first_run/signin/signin_screen_mediator_unittest.mm b/ios/chrome/browser/ui/first_run/signin/signin_screen_mediator_unittest.mm
index dba67c0..3ee6e81 100644
--- a/ios/chrome/browser/ui/first_run/signin/signin_screen_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/first_run/signin/signin_screen_mediator_unittest.mm
@@ -38,7 +38,7 @@
 
 @implementation FakeSigninScreenConsumer
 
-- (void)hideIdentityButtonControl {
+- (void)noIdentityAvailable {
   self.hidden = YES;
 }
 
diff --git a/ios/chrome/browser/ui/first_run/signin/signin_screen_view_controller.mm b/ios/chrome/browser/ui/first_run/signin/signin_screen_view_controller.mm
index a288905b..602e497 100644
--- a/ios/chrome/browser/ui/first_run/signin/signin_screen_view_controller.mm
+++ b/ios/chrome/browser/ui/first_run/signin/signin_screen_view_controller.mm
@@ -4,14 +4,23 @@
 
 #import "ios/chrome/browser/ui/first_run/signin/signin_screen_view_controller.h"
 
+#include "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/ui/authentication/views/identity_button_control.h"
 #import "ios/chrome/browser/ui/elements/activity_overlay_view.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
+#include "ios/chrome/grit/ios_google_chrome_strings.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+namespace {
+// Width of the identity control if nothing is contraining it.
+const CGFloat kIdentityControlMaxWidth = 327;
+}  // namespace
+
 @interface SigninScreenViewController ()
 
 // Button controlling the display of the selected identity.
@@ -28,15 +37,34 @@
 #pragma mark - Public
 
 - (void)viewDidLoad {
-  self.titleText = @"Test Sign-in Screen";
-  self.primaryActionString = @"Test Continue Button";
+  self.titleText = l10n_util::GetNSString(IDS_IOS_FIRST_RUN_SIGNIN_TITLE);
+  self.subtitleText = l10n_util::GetNSString(IDS_IOS_FIRST_RUN_SIGNIN_SUBTITLE);
+  if (!self.primaryActionString) {
+    // |primaryActionString| could already be set using the consumer methods.
+    self.primaryActionString =
+        l10n_util::GetNSString(IDS_IOS_FIRST_RUN_SIGNIN_SIGN_IN_ACTION);
+  }
+  self.secondaryActionString =
+      l10n_util::GetNSString(IDS_IOS_FIRST_RUN_SIGNIN_DONT_SIGN_IN);
 
-  self.identityControl =
-      [[IdentityButtonControl alloc] initWithFrame:CGRectZero];
-  [self.identityControl addTarget:self
-                           action:@selector(identityButtonControlTapped:
-                                                               forEvent:)
-                 forControlEvents:UIControlEventTouchUpInside];
+  [self.specificContentView addSubview:self.identityControl];
+
+  NSLayoutConstraint* widthConstraint = [self.identityControl.widthAnchor
+      constraintEqualToConstant:kIdentityControlMaxWidth];
+  widthConstraint.priority = UILayoutPriorityDefaultHigh;
+
+  [NSLayoutConstraint activateConstraints:@[
+    [self.identityControl.topAnchor
+        constraintEqualToAnchor:self.specificContentView.topAnchor],
+    [self.identityControl.centerXAnchor
+        constraintEqualToAnchor:self.specificContentView.centerXAnchor],
+    [self.identityControl.widthAnchor
+        constraintLessThanOrEqualToAnchor:self.specificContentView.widthAnchor],
+    widthConstraint,
+    [self.identityControl.bottomAnchor
+        constraintLessThanOrEqualToAnchor:self.specificContentView
+                                              .bottomAnchor],
+  ]];
 
   // TODO(crbug.com/1189836): Add the identity control to the wrapper view.
 
@@ -55,6 +83,25 @@
   return _overlay;
 }
 
+- (IdentityButtonControl*)identityControl {
+  if (!_identityControl) {
+    _identityControl = [[IdentityButtonControl alloc] initWithFrame:CGRectZero];
+    _identityControl.translatesAutoresizingMaskIntoConstraints = NO;
+    [_identityControl addTarget:self
+                         action:@selector(identityButtonControlTapped:forEvent:)
+               forControlEvents:UIControlEventTouchUpInside];
+
+    // Setting the content hugging priority isn't working, so creating a
+    // low-priority constraint to make sure that the view is as small as
+    // possible.
+    NSLayoutConstraint* heightConstraint =
+        [_identityControl.heightAnchor constraintEqualToConstant:0];
+    heightConstraint.priority = UILayoutPriorityDefaultLow - 1;
+    heightConstraint.active = YES;
+  }
+  return _identityControl;
+}
+
 #pragma mark - SignInScreenConsumer
 
 - (void)setUserImage:(UIImage*)userImage {
@@ -66,12 +113,12 @@
 }
 
 - (void)setSelectedIdentityUserName:(NSString*)userName email:(NSString*)email {
-  self.identityControl.hidden = NO;
+  [self updateUIForIdentityAvailable:YES];
   [self.identityControl setIdentityName:userName email:email];
 }
 
-- (void)hideIdentityButtonControl {
-  self.identityControl.hidden = YES;
+- (void)noIdentityAvailable {
+  [self updateUIForIdentityAvailable:NO];
 }
 
 - (void)setUIEnabled:(BOOL)UIEnabled {
@@ -96,9 +143,25 @@
 
 #pragma mark - Private
 
+// Callback for |identityControl|.
 - (void)identityButtonControlTapped:(id)sender forEvent:(UIEvent*)event {
   UITouch* touch = event.allTouches.anyObject;
   [self.delegate showAccountPickerFromPoint:[touch locationInView:nil]];
 }
 
+// Updates the UI to adapt for |identityAvailable| or not.
+- (void)updateUIForIdentityAvailable:(BOOL)identityAvailable {
+  self.identityControl.hidden = !identityAvailable;
+  if (identityAvailable) {
+    // TODO(crbug.com/1189836): Use the real name.
+    self.primaryActionString = l10n_util::GetNSStringF(
+        IDS_IOS_FIRST_RUN_SIGNIN_CONTINUE_AS,
+        base::SysNSStringToUTF16(@"Account Name (Test)"));
+    ;
+  } else {
+    self.primaryActionString =
+        l10n_util::GetNSString(IDS_IOS_FIRST_RUN_SIGNIN_SIGN_IN_ACTION);
+  }
+}
+
 @end
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index e2f7c295..6817b95 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -1858,6 +1858,26 @@
                                  completion:nil];
 }
 
+- (void)showDefaultBrowserSettingsFromViewController:
+    (UIViewController*)baseViewController {
+  if (!baseViewController) {
+    baseViewController = self.currentInterface.viewController;
+  }
+  if (self.settingsNavigationController) {
+    [self.settingsNavigationController
+        showDefaultBrowserSettingsFromViewController:baseViewController];
+    return;
+  }
+  Browser* browser = self.mainInterface.browser;
+
+  self.settingsNavigationController =
+      [SettingsNavigationController defaultBrowserControllerForBrowser:browser
+                                                              delegate:self];
+  [baseViewController presentViewController:self.settingsNavigationController
+                                   animated:YES
+                                 completion:nil];
+}
+
 #pragma mark - UserFeedbackDataSource
 
 - (BOOL)currentPageIsIncognito {
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.h b/ios/chrome/browser/ui/settings/settings_navigation_controller.h
index 1eb15408..a12e0556 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller.h
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.h
@@ -137,6 +137,15 @@
                                       (id<SettingsNavigationControllerDelegate>)
                                           delegate;
 
+// Creates a new DefaultBrowserSettingsTableViewController and the chrome
+// around it. |browser| is the browser where settings are being displayed and
+// should not be nil. |delegate| may be nil.
++ (instancetype)
+    defaultBrowserControllerForBrowser:(Browser*)browser
+                              delegate:
+                                  (id<SettingsNavigationControllerDelegate>)
+                                      delegate;
+
 // Initializes the UINavigationController with |rootViewController|.
 - (instancetype)initWithRootViewController:(UIViewController*)rootViewController
                                    browser:(Browser*)browser
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
index 6be9ab3d..631e5fe11 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
@@ -14,6 +14,7 @@
 #import "ios/chrome/browser/ui/keyboard/UIKeyCommand+Chrome.h"
 #import "ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/default_browser/default_browser_settings_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/google_services/google_services_settings_coordinator.h"
 #import "ios/chrome/browser/ui/settings/google_services/google_services_settings_view_controller.h"
@@ -281,6 +282,22 @@
   return nc;
 }
 
++ (instancetype)
+    defaultBrowserControllerForBrowser:(Browser*)browser
+                              delegate:
+                                  (id<SettingsNavigationControllerDelegate>)
+                                      delegate {
+  DCHECK(browser);
+  DefaultBrowserSettingsTableViewController* controller =
+      [[DefaultBrowserSettingsTableViewController alloc] init];
+  SettingsNavigationController* nc = [[SettingsNavigationController alloc]
+      initWithRootViewController:controller
+                         browser:browser
+                        delegate:delegate];
+  [controller navigationItem].leftBarButtonItem = [nc cancelButton];
+  return nc;
+}
+
 #pragma mark - Lifecycle
 
 - (instancetype)initWithRootViewController:(UIViewController*)rootViewController
@@ -647,6 +664,14 @@
   [self pushViewController:controller animated:YES];
 }
 
+- (void)showDefaultBrowserSettingsFromViewController:
+    (UIViewController*)baseViewController {
+  DefaultBrowserSettingsTableViewController* controller =
+      [[DefaultBrowserSettingsTableViewController alloc] init];
+  controller.dispatcher = [self.settingsNavigationDelegate handlerForSettings];
+  [self pushViewController:controller animated:YES];
+}
+
 #pragma mark - UIResponder
 
 - (BOOL)canBecomeFirstResponder {
diff --git a/ios/chrome/browser/ui/webui/policy/policy_ui.mm b/ios/chrome/browser/ui/webui/policy/policy_ui.mm
index 2138722e..65ce478a 100644
--- a/ios/chrome/browser/ui/webui/policy/policy_ui.mm
+++ b/ios/chrome/browser/ui/webui/policy/policy_ui.mm
@@ -39,8 +39,6 @@
       {"labelAssetId", IDS_POLICY_LABEL_ASSET_ID},
       {"labelClientId", IDS_POLICY_LABEL_CLIENT_ID},
       {"labelDirectoryApiId", IDS_POLICY_LABEL_DIRECTORY_API_ID},
-      {"labelEnterpriseEnrollmentDomain",
-       IDS_POLICY_LABEL_ENTERPRISE_ENROLLMENT_DOMAIN},
       {"labelGaiaId", IDS_POLICY_LABEL_GAIA_ID},
       {"labelIsAffiliated", IDS_POLICY_LABEL_IS_AFFILIATED},
       {"labelLocation", IDS_POLICY_LABEL_LOCATION},
diff --git a/media/base/status_codes.h b/media/base/status_codes.h
index 45eee646..70078b0 100644
--- a/media/base/status_codes.h
+++ b/media/base/status_codes.h
@@ -94,6 +94,7 @@
   kGetQuantBufferFailed = 0x00000328,
   kReleaseQuantBufferFailed = 0x00000329,
   kBitstreamBufferSliceTooBig = 0x00000330,
+  kCreateSharedImageFailed = 0x00000331,
 
   // MojoDecoder Errors: 0x04
   kMojoDecoderNoWrappedDecoder = 0x00000401,
diff --git a/media/gpu/command_buffer_helper.cc b/media/gpu/command_buffer_helper.cc
index a15549c0..9288149 100644
--- a/media/gpu/command_buffer_helper.cc
+++ b/media/gpu/command_buffer_helper.cc
@@ -48,8 +48,10 @@
 #endif  // defined(OS_MAC)
     );
     decoder_helper_ = GLES2DecoderHelper::Create(stub_->decoder_context());
-    tracker_ =
-        std::make_unique<gpu::MemoryTypeTracker>(stub_->GetMemoryTracker());
+    // Use the shared image stub tracker instead of command buffer stub tracker
+    // since the created shared images could outlive the command buffer.
+    gpu::MemoryTracker* tracker = stub_->channel()->shared_image_stub();
+    tracker_ = std::make_unique<gpu::MemoryTypeTracker>(tracker);
   }
 
   gl::GLContext* GetGLContext() override {
diff --git a/media/gpu/windows/d3d11_texture_selector.cc b/media/gpu/windows/d3d11_texture_selector.cc
index 7a92a8f..4a84e58 100644
--- a/media/gpu/windows/d3d11_texture_selector.cc
+++ b/media/gpu/windows/d3d11_texture_selector.cc
@@ -66,7 +66,6 @@
     }
     case DXGI_FORMAT_P010: {
       MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder producing P010";
-      output_pixel_format = PIXEL_FORMAT_ARGB;
 
       // TODO(liberato): handle case where we bind P010 directly (see dxva).
 
@@ -94,6 +93,7 @@
           // TODO(liberato): use the format checker, else bind P010.
           MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder: 8 bit sRGB";
           output_dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM;
+          output_pixel_format = PIXEL_FORMAT_ARGB;
           output_color_space = gfx::ColorSpace::CreateSRGB();
         } else {
           // Bind P010 directly, since we can't copy.
@@ -116,9 +116,10 @@
           output_color_space = gfx::ColorSpace::CreateSCRGBLinear();
         } else if (format_checker->CheckOutputFormatSupport(
                        DXGI_FORMAT_R10G10B10A2_UNORM)) {
-          MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder: BGRA10 scRGBLinear";
+          MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder: RGB10A2 HDR10/PQ";
           output_dxgi_format = DXGI_FORMAT_R10G10B10A2_UNORM;
-          output_color_space = gfx::ColorSpace::CreateSCRGBLinear();
+          output_pixel_format = PIXEL_FORMAT_XB30;
+          output_color_space = gfx::ColorSpace::CreateHDR10();
         } else {
           // No support at all.  Just bind P010, and hope for the best.
           MEDIA_LOG(INFO, media_log)
@@ -149,9 +150,10 @@
   // If we're trying to produce an output texture that's different from what
   // the decoder is providing, then we need to copy it.  If sharing decoder
   // textures is not allowed, then copy either way.
-  bool needs_texture_copy = !SupportsZeroCopy(gpu_preferences, workarounds) ||
-                            (decoder_output_format != output_dxgi_format) ||
-               base::FeatureList::IsEnabled(kD3D11VideoDecoderAlwaysCopy);
+  bool needs_texture_copy =
+      !SupportsZeroCopy(gpu_preferences, workarounds) ||
+      (decoder_output_format != output_dxgi_format) ||
+      base::FeatureList::IsEnabled(kD3D11VideoDecoderAlwaysCopy);
 
   MEDIA_LOG(INFO, media_log)
       << "D3D11VideoDecoder output color space: "
@@ -178,8 +180,7 @@
     ComD3D11Device device,
     gfx::Size size) {
   // TODO(liberato): If the output format is rgb, then create a pbuffer wrapper.
-  return std::make_unique<DefaultTexture2DWrapper>(size, OutputDXGIFormat(),
-                                                   PixelFormat());
+  return std::make_unique<DefaultTexture2DWrapper>(size, OutputDXGIFormat());
 }
 
 bool TextureSelector::WillCopyForTesting() const {
@@ -228,9 +229,7 @@
     return nullptr;
 
   return std::make_unique<CopyingTexture2DWrapper>(
-      size,
-      std::make_unique<DefaultTexture2DWrapper>(size, OutputDXGIFormat(),
-                                                PixelFormat()),
+      size, std::make_unique<DefaultTexture2DWrapper>(size, OutputDXGIFormat()),
       video_processor_proxy_, out_texture, output_color_space_);
 }
 
diff --git a/media/gpu/windows/d3d11_texture_selector_unittest.cc b/media/gpu/windows/d3d11_texture_selector_unittest.cc
index 12bd030..9ebb9c34 100644
--- a/media/gpu/windows/d3d11_texture_selector_unittest.cc
+++ b/media/gpu/windows/d3d11_texture_selector_unittest.cc
@@ -129,7 +129,7 @@
       CreateWithDefaultGPUInfo(DXGI_FORMAT_P010, ZeroCopyEnabled::kTrue,
                                TextureSelector::HDRMode::kSDROrHDR);
 
-  EXPECT_EQ(tex_sel->PixelFormat(), PIXEL_FORMAT_ARGB);
+  EXPECT_EQ(tex_sel->PixelFormat(), PIXEL_FORMAT_XB30);
   EXPECT_EQ(tex_sel->OutputDXGIFormat(), DXGI_FORMAT_R10G10B10A2_UNORM);
   EXPECT_TRUE(tex_sel->WillCopyForTesting());
 }
diff --git a/media/gpu/windows/d3d11_texture_wrapper.cc b/media/gpu/windows/d3d11_texture_wrapper.cc
index ed2ede8d..0d1da96 100644
--- a/media/gpu/windows/d3d11_texture_wrapper.cc
+++ b/media/gpu/windows/d3d11_texture_wrapper.cc
@@ -22,90 +22,43 @@
 
 namespace {
 
-// Populates Viz |texture_formats| that map to the corresponding DXGI format and
-// VideoPixelFormat. Returns true if they can be successfully mapped.
-bool DXGIFormatToVizFormat(
-    DXGI_FORMAT dxgi_format,
-    VideoPixelFormat pixel_format,
-    size_t textures_per_picture,
-    std::array<viz::ResourceFormat, VideoFrame::kMaxPlanes>& texture_formats) {
+bool SupportsFormat(DXGI_FORMAT dxgi_format) {
   switch (dxgi_format) {
     case DXGI_FORMAT_NV12:
-      DCHECK_EQ(textures_per_picture, 2u);
-      texture_formats[0] = viz::RED_8;  // Y
-      texture_formats[1] = viz::RG_88;  // UV
-      return true;
     case DXGI_FORMAT_P010:
-      // TODO(crbug.com/1011555): P010 formats are not fully supported.
-      // Treat them to be the same as NV12 for the time being.
-      DCHECK_EQ(textures_per_picture, 2u);
-      texture_formats[0] = viz::RED_8;
-      texture_formats[1] = viz::RG_88;
-      return true;
     case DXGI_FORMAT_B8G8R8A8_UNORM:
-      DCHECK_EQ(textures_per_picture, 1u);
-      if (pixel_format != PIXEL_FORMAT_ARGB) {
-        return false;
-      }
-      texture_formats[0] = viz::BGRA_8888;
-      return true;
+    case DXGI_FORMAT_R10G10B10A2_UNORM:
     case DXGI_FORMAT_R16G16B16A16_FLOAT:
-      DCHECK_EQ(textures_per_picture, 1u);
-      if (pixel_format != PIXEL_FORMAT_RGBAF16)
-        return false;
-      texture_formats[0] = viz::RGBA_F16;
       return true;
-    default:  // Unsupported
+    default:
       return false;
   }
 }
 
+size_t NumPlanes(DXGI_FORMAT dxgi_format) {
+  switch (dxgi_format) {
+    case DXGI_FORMAT_NV12:
+    case DXGI_FORMAT_P010:
+      return 2;
+    case DXGI_FORMAT_B8G8R8A8_UNORM:
+    case DXGI_FORMAT_R10G10B10A2_UNORM:
+    case DXGI_FORMAT_R16G16B16A16_FLOAT:
+      return 1;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
 }  // anonymous namespace
 
-// Handy structure so that we can activate / bind one or two textures.
-struct ScopedTextureEverything {
-  ScopedTextureEverything(GLenum unit, GLuint service_id)
-      : active_(unit), binder_(GL_TEXTURE_EXTERNAL_OES, service_id) {}
-  ~ScopedTextureEverything() = default;
-
-  // Order is important; we need |active_| to be constructed first
-  // and destructed last.
-  gl::ScopedActiveTexture active_;
-  gl::ScopedTextureBinder binder_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedTextureEverything);
-};
-
-// Another handy helper class to guarantee that ScopedTextureEverythings
-// are deleted in reverse order.  This is required so that the scoped
-// active texture unit doesn't change.  Surprisingly, none of the stl
-// containers, or the chromium ones, seem to guarantee anything about
-// the order of destruction.
-struct OrderedDestructionList {
-  OrderedDestructionList() = default;
-  ~OrderedDestructionList() {
-    // Erase last-to-first.
-    while (!list_.empty())
-      list_.pop_back();
-  }
-
-  template <typename... Args>
-  void emplace_back(Args&&... args) {
-    list_.emplace_back(std::forward<Args>(args)...);
-  }
-
-  std::list<ScopedTextureEverything> list_;
-  DISALLOW_COPY_AND_ASSIGN(OrderedDestructionList);
-};
-
 Texture2DWrapper::Texture2DWrapper() = default;
 
 Texture2DWrapper::~Texture2DWrapper() = default;
 
 DefaultTexture2DWrapper::DefaultTexture2DWrapper(const gfx::Size& size,
-                                                 DXGI_FORMAT dxgi_format,
-                                                 VideoPixelFormat pixel_format)
-    : size_(size), dxgi_format_(dxgi_format), pixel_format_(pixel_format) {}
+                                                 DXGI_FORMAT dxgi_format)
+    : size_(size), dxgi_format_(dxgi_format) {}
 
 DefaultTexture2DWrapper::~DefaultTexture2DWrapper() = default;
 
@@ -135,28 +88,17 @@
     GetCommandBufferHelperCB get_helper_cb,
     ComD3D11Texture2D texture,
     size_t array_slice) {
-  gpu_resources_ = base::SequenceBound<GpuResources>(
-      std::move(gpu_task_runner),
-      BindToCurrentLoop(base::BindOnce(&DefaultTexture2DWrapper::OnError,
-                                       weak_factory_.GetWeakPtr())));
-
-  const size_t textures_per_picture = VideoFrame::NumPlanes(pixel_format_);
-
-  std::array<viz::ResourceFormat, VideoFrame::kMaxPlanes> texture_formats;
-  if (!DXGIFormatToVizFormat(dxgi_format_, pixel_format_, textures_per_picture,
-                             texture_formats)) {
+  if (!SupportsFormat(dxgi_format_))
     return Status(StatusCode::kUnsupportedTextureFormatForBind);
-  }
 
   // Generate mailboxes and holders.
   // TODO(liberato): Verify that this is really okay off the GPU main thread.
   // The current implementation is.
   std::vector<gpu::Mailbox> mailboxes;
-  for (size_t texture_idx = 0; texture_idx < textures_per_picture;
-       texture_idx++) {
+  for (size_t plane = 0; plane < NumPlanes(dxgi_format_); plane++) {
     mailboxes.push_back(gpu::Mailbox::GenerateForSharedImage());
-    mailbox_holders_[texture_idx] = gpu::MailboxHolder(
-        mailboxes[texture_idx], gpu::SyncToken(), GL_TEXTURE_EXTERNAL_OES);
+    mailbox_holders_[plane] = gpu::MailboxHolder(
+        mailboxes[plane], gpu::SyncToken(), GL_TEXTURE_EXTERNAL_OES);
   }
 
   // Start construction of the GpuResources.
@@ -164,10 +106,12 @@
   // device for decoding.  Sharing seems not to work very well.  Otherwise, we
   // would create the texture with KEYED_MUTEX and NTHANDLE, then send along
   // a handle that we get from |texture| as an IDXGIResource1.
-  gpu_resources_.AsyncCall(&GpuResources::Init)
-      .WithArgs(std::move(get_helper_cb), std::move(mailboxes),
-                GL_TEXTURE_EXTERNAL_OES, size_, textures_per_picture,
-                texture_formats, pixel_format_, texture, array_slice);
+  auto on_error_cb = BindToCurrentLoop(base::BindOnce(
+      &DefaultTexture2DWrapper::OnError, weak_factory_.GetWeakPtr()));
+  gpu_resources_ = base::SequenceBound<GpuResources>(
+      std::move(gpu_task_runner), std::move(on_error_cb),
+      std::move(get_helper_cb), std::move(mailboxes), size_, dxgi_format_,
+      texture, array_slice);
   return OkStatus();
 }
 
@@ -182,217 +126,47 @@
 void DefaultTexture2DWrapper::SetDisplayHDRMetadata(
     const DXGI_HDR_METADATA_HDR10& dxgi_display_metadata) {}
 
-DefaultTexture2DWrapper::GpuResources::GpuResources(OnErrorCB on_error_cb)
-    : on_error_cb_(std::move(on_error_cb)) {}
-
-DefaultTexture2DWrapper::GpuResources::~GpuResources() {
-  if (helper_ && helper_->MakeContextCurrent()) {
-    for (uint32_t service_id : service_ids_)
-      helper_->DestroyTexture(service_id);
-  }
-}
-
-void DefaultTexture2DWrapper::GpuResources::Init(
+DefaultTexture2DWrapper::GpuResources::GpuResources(
+    OnErrorCB on_error_cb,
     GetCommandBufferHelperCB get_helper_cb,
-    const std::vector<gpu::Mailbox> mailboxes,
-    GLenum target,
-    gfx::Size size,
-    size_t textures_per_picture,
-    std::array<viz::ResourceFormat, VideoFrame::kMaxPlanes> texture_formats,
-    VideoPixelFormat pixel_format,
+    const std::vector<gpu::Mailbox>& mailboxes,
+    const gfx::Size& size,
+    DXGI_FORMAT dxgi_format,
     ComD3D11Texture2D texture,
     size_t array_slice) {
   helper_ = get_helper_cb.Run();
 
   if (!helper_ || !helper_->MakeContextCurrent()) {
-    NotifyError(StatusCode::kMakeContextCurrentFailed);
+    std::move(on_error_cb)
+        .Run(std::move(StatusCode::kMakeContextCurrentFailed));
     return;
   }
 
-  // Create the stream for zero-copy use by gl.
-  EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay();
-  const EGLint stream_attributes[] = {
-      // clang-format off
-      EGL_CONSUMER_LATENCY_USEC_KHR,         0,
-      EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR, 0,
-      EGL_NONE,
-      // clang-format on
-  };
-  EGLStreamKHR stream = eglCreateStreamKHR(egl_display, stream_attributes);
-  if (!stream) {
-    NotifyError(StatusCode::kCreateEglStreamFailed);
+  // Usage flags to allow the display compositor to draw from it, video to
+  // decode, and allow webgl/canvas access.
+  constexpr uint32_t usage =
+      gpu::SHARED_IMAGE_USAGE_VIDEO_DECODE | gpu::SHARED_IMAGE_USAGE_GLES2 |
+      gpu::SHARED_IMAGE_USAGE_RASTER | gpu::SHARED_IMAGE_USAGE_DISPLAY |
+      gpu::SHARED_IMAGE_USAGE_SCANOUT;
+
+  auto shared_image_backings =
+      gpu::SharedImageBackingD3D::CreateFromVideoTexture(
+          mailboxes, dxgi_format, size, usage, texture, array_slice);
+  if (shared_image_backings.empty()) {
+    std::move(on_error_cb).Run(std::move(StatusCode::kCreateSharedImageFailed));
     return;
   }
+  DCHECK_EQ(shared_image_backings.size(), NumPlanes(dxgi_format));
 
-  // |stream| will be destroyed when the GLImage is.
-  // TODO(liberato): for tests, it will be destroyed pretty much at the end of
-  // this function unless |helper_| retains it.  Also, this won't work if we
-  // have a FakeCommandBufferHelper since the service IDs aren't meaningful.
-  gl_image_ = base::MakeRefCounted<gl::GLImageDXGI>(size, stream);
-
-  // Create the textures and attach them to the mailboxes.
-  // TODO(liberato): Should we use GL_FLOAT for an fp16 texture?  It doesn't
-  // really seem to matter so far as I can tell.
-  for (size_t texture_idx = 0; texture_idx < textures_per_picture;
-       texture_idx++) {
-    const viz::ResourceFormat format = texture_formats[texture_idx];
-    const GLenum internal_format = viz::GLInternalFormat(format);
-    const GLenum data_type = viz::GLDataType(format);
-    const GLenum data_format = viz::GLDataFormat(format);
-
-    // Adjust the size by the subsampling factor.
-    const size_t width =
-        VideoFrame::Columns(texture_idx, pixel_format, size.width());
-    const size_t height =
-        VideoFrame::Rows(texture_idx, pixel_format, size.height());
-    const gfx::Size plane_size(width, height);
-
-    // TODO(crbug.com/1011555): CreateTexture allocates a GL texture, figure out
-    // if this can be removed.
-    const uint32_t service_id =
-        helper_->CreateTexture(target, internal_format, plane_size.width(),
-                               plane_size.height(), data_format, data_type);
-
-    const auto& mailbox = mailboxes[texture_idx];
-
-    // Shared image does not need to store the colorspace since it is already
-    // stored on the VideoFrame which is provided upon presenting the overlay.
-    // To prevent the developer from mistakenly using it, provide the invalid
-    // value from default-construction.
-    const gfx::ColorSpace kInvalidColorSpace;
-
-    // Usage flags to allow the display compositor to draw from it, video to
-    // decode, and allow webgl/canvas access.
-    const uint32_t shared_image_usage =
-        gpu::SHARED_IMAGE_USAGE_VIDEO_DECODE | gpu::SHARED_IMAGE_USAGE_GLES2 |
-        gpu::SHARED_IMAGE_USAGE_RASTER | gpu::SHARED_IMAGE_USAGE_DISPLAY |
-        gpu::SHARED_IMAGE_USAGE_SCANOUT;
-
-    // Create a shared image
-    // TODO(crbug.com/1011555): Need key shared mutex if shared image is ever
-    // used by another device.
-    scoped_refptr<gpu::gles2::TexturePassthrough> gl_texture =
-        gpu::gles2::TexturePassthrough::CheckedCast(
-            helper_->GetTexture(service_id));
-
-    auto shared_image = std::make_unique<gpu::SharedImageBackingD3D>(
-        mailbox, format, plane_size, kInvalidColorSpace,
-        kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, shared_image_usage,
-        /*swap_chain=*/nullptr, std::move(gl_texture), gl_image_,
-        /*buffer_index=*/0, texture, base::win::ScopedHandle(),
-        /*dxgi_key_mutex=*/nullptr);
-
-    // Caller is assumed to provide cleared d3d textures.
-    shared_image->SetCleared();
-
-    // Shared images will be destroyed when this wrapper goes away.
-    // Only GpuResource can be used to safely destroy the shared images on the
-    // gpu main thread.
-    shared_images_.push_back(helper_->Register(std::move(shared_image)));
-
-    service_ids_.push_back(service_id);
-  }
-
-  // Bind all the textures so that the stream can find them.
-  OrderedDestructionList texture_everythings;
-  for (size_t i = 0; i < textures_per_picture; i++)
-    texture_everythings.emplace_back(GL_TEXTURE0 + i, service_ids_[i]);
-
-  std::vector<EGLAttrib> consumer_attributes;
-  if (textures_per_picture == 2) {
-    // Assume NV12.
-    consumer_attributes = {
-        // clang-format off
-        EGL_COLOR_BUFFER_TYPE,               EGL_YUV_BUFFER_EXT,
-        EGL_YUV_NUMBER_OF_PLANES_EXT,        2,
-        EGL_YUV_PLANE0_TEXTURE_UNIT_NV,      0,
-        EGL_YUV_PLANE1_TEXTURE_UNIT_NV,      1,
-        EGL_NONE,
-        // clang-format on
-    };
-  } else {
-    // Assume some rgb format.
-    consumer_attributes = {
-        // clang-format off
-        EGL_COLOR_BUFFER_TYPE,               EGL_RGB_BUFFER,
-        EGL_NONE,
-        // clang-format on
-    };
-  }
-  EGLBoolean result = eglStreamConsumerGLTextureExternalAttribsNV(
-      egl_display, stream, consumer_attributes.data());
-  if (!result) {
-    NotifyError(StatusCode::kCreateEglStreamConsumerFailed);
-    return;
-  }
-
-  EGLAttrib producer_attributes[] = {
-      EGL_NONE,
-  };
-
-  result = eglCreateStreamProducerD3DTextureANGLE(egl_display, stream,
-                                                  producer_attributes);
-  if (!result) {
-    NotifyError(StatusCode::kCreateEglStreamProducerFailed);
-    return;
-  }
-
-  // Note that this is valid as long as |gl_image_| is valid; it is
-  // what deletes the stream.
-  stream_ = stream;
-
-  // Bind the image to each texture.
-  for (size_t texture_idx = 0; texture_idx < service_ids_.size();
-       texture_idx++) {
-    helper_->BindImage(service_ids_[texture_idx], gl_image_.get(),
-                       false /* client_managed */);
-  }
-
-  // Specify the texture so ProcessTexture knows how to process it using a GL
-  // image.
-  gl_image_->SetTexture(texture, array_slice);
-
-  PushNewTexture();
+  for (auto& backing : shared_image_backings)
+    shared_images_.push_back(helper_->Register(std::move(backing)));
 }
 
-void DefaultTexture2DWrapper::GpuResources::PushNewTexture() {
-  // If init didn't complete, then signal (another) error that will probably be
-  // ignored in favor of whatever we signalled earlier.
-  if (!gl_image_ || !stream_) {
-    NotifyError(StatusCode::kDecoderInitializeNeverCompleted);
+DefaultTexture2DWrapper::GpuResources::~GpuResources() {
+  // Destroy shared images with a current context.
+  if (!helper_ || !helper_->MakeContextCurrent())
     return;
-  }
-
-  if (!helper_ || !helper_->MakeContextCurrent()) {
-    NotifyError(StatusCode::kMakeContextCurrentFailed);
-    return;
-  }
-
-  // Notify angle that it has a new texture.
-  EGLAttrib frame_attributes[] = {
-      EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE,
-      gl_image_->level(),
-      EGL_NONE,
-  };
-
-  EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay();
-  if (!eglStreamPostD3DTextureANGLE(
-          egl_display, stream_, static_cast<void*>(gl_image_->texture().Get()),
-          frame_attributes)) {
-    NotifyError(StatusCode::kPostTextureFailed);
-    return;
-  }
-
-  if (!eglStreamConsumerAcquireKHR(egl_display, stream_)) {
-    NotifyError(StatusCode::kPostAcquireStreamFailed);
-    return;
-  }
-}
-
-void DefaultTexture2DWrapper::GpuResources::NotifyError(Status status) {
-  if (on_error_cb_)
-    std::move(on_error_cb_).Run(std::move(status));
-  // else this isn't the first error, so skip it.
+  shared_images_.clear();
 }
 
 }  // namespace media
diff --git a/media/gpu/windows/d3d11_texture_wrapper.h b/media/gpu/windows/d3d11_texture_wrapper.h
index 5c097ec9..9604e88 100644
--- a/media/gpu/windows/d3d11_texture_wrapper.h
+++ b/media/gpu/windows/d3d11_texture_wrapper.h
@@ -74,9 +74,7 @@
 
   // While the specific texture instance can change on every call to
   // ProcessTexture, the dxgi format must be the same for all of them.
-  DefaultTexture2DWrapper(const gfx::Size& size,
-                          DXGI_FORMAT dxgi_format,
-                          VideoPixelFormat pixel_format);
+  DefaultTexture2DWrapper(const gfx::Size& size, DXGI_FORMAT dxgi_format);
   ~DefaultTexture2DWrapper() override;
 
   Status Init(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
@@ -99,36 +97,17 @@
   // can use the mailbox.
   class GpuResources {
    public:
-    GpuResources(OnErrorCB on_error_cb);
+    GpuResources(OnErrorCB on_error_cb,
+                 GetCommandBufferHelperCB get_helper_cb,
+                 const std::vector<gpu::Mailbox>& mailboxes,
+                 const gfx::Size& size,
+                 DXGI_FORMAT dxgi_format,
+                 ComD3D11Texture2D texture,
+                 size_t array_slice);
     ~GpuResources();
 
-    void Init(
-        GetCommandBufferHelperCB get_helper_cb,
-        const std::vector<gpu::Mailbox> mailboxes,
-        GLenum target,
-        gfx::Size size,
-        size_t textures_per_picture,
-        std::array<viz::ResourceFormat, VideoFrame::kMaxPlanes> texture_formats,
-        VideoPixelFormat pixel_format,
-        ComD3D11Texture2D texture,
-        size_t array_slice);
-
-    std::vector<uint32_t> service_ids_;
-
    private:
-    // Push a new |texture|, |array_slice| to |gl_image_|.
-    // Both |texture| and |array_slice| were set by Init.
-    void PushNewTexture();
-
-    // Notify our wrapper about |status|, if we haven't before.
-    void NotifyError(Status status);
-
-    // May be empty if we've already sent an error.
-    OnErrorCB on_error_cb_;
-
     scoped_refptr<CommandBufferHelper> helper_;
-    scoped_refptr<gl::GLImageDXGI> gl_image_;
-    EGLStreamKHR stream_;
 
     std::vector<std::unique_ptr<gpu::SharedImageRepresentationFactoryRef>>
         shared_images_;
@@ -146,7 +125,6 @@
   base::SequenceBound<GpuResources> gpu_resources_;
   MailboxHolderArray mailbox_holders_;
   DXGI_FORMAT dxgi_format_;
-  VideoPixelFormat pixel_format_;
 
   base::WeakPtrFactory<DefaultTexture2DWrapper> weak_factory_{this};
 };
diff --git a/media/gpu/windows/d3d11_texture_wrapper_unittest.cc b/media/gpu/windows/d3d11_texture_wrapper_unittest.cc
index 3142782..cfcd5fa 100644
--- a/media/gpu/windows/d3d11_texture_wrapper_unittest.cc
+++ b/media/gpu/windows/d3d11_texture_wrapper_unittest.cc
@@ -88,10 +88,8 @@
 TEST_F(D3D11TextureWrapperUnittest, NV12InitSucceeds) {
   STOP_IF_WIN7();
   const DXGI_FORMAT dxgi_format = DXGI_FORMAT_NV12;
-  const VideoPixelFormat pixel_format = PIXEL_FORMAT_NV12;
 
-  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format,
-                                                           pixel_format);
+  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format);
   const Status init_result = wrapper->Init(
       task_runner_, get_helper_cb_, /*texture_d3d=*/nullptr, /*array_slice=*/0);
   EXPECT_TRUE(init_result.is_ok());
@@ -102,10 +100,8 @@
 TEST_F(D3D11TextureWrapperUnittest, BGRA8InitSucceeds) {
   STOP_IF_WIN7();
   const DXGI_FORMAT dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM;
-  const VideoPixelFormat pixel_format = PIXEL_FORMAT_ARGB;
 
-  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format,
-                                                           pixel_format);
+  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format);
   const Status init_result = wrapper->Init(
       task_runner_, get_helper_cb_, /*texture_d3d=*/nullptr, /*array_slice=*/0);
   EXPECT_TRUE(init_result.is_ok());
@@ -114,10 +110,8 @@
 TEST_F(D3D11TextureWrapperUnittest, FP16InitSucceeds) {
   STOP_IF_WIN7();
   const DXGI_FORMAT dxgi_format = DXGI_FORMAT_R16G16B16A16_FLOAT;
-  const VideoPixelFormat pixel_format = PIXEL_FORMAT_RGBAF16;
 
-  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format,
-                                                           pixel_format);
+  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format);
   const Status init_result = wrapper->Init(
       task_runner_, get_helper_cb_, /*texture_d3d=*/nullptr, /*array_slice=*/0);
   EXPECT_TRUE(init_result.is_ok());
@@ -126,10 +120,8 @@
 TEST_F(D3D11TextureWrapperUnittest, P010InitSucceeds) {
   STOP_IF_WIN7();
   const DXGI_FORMAT dxgi_format = DXGI_FORMAT_P010;
-  const VideoPixelFormat pixel_format = PIXEL_FORMAT_NV12;
 
-  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format,
-                                                           pixel_format);
+  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format);
   const Status init_result = wrapper->Init(
       task_runner_, get_helper_cb_, /*texture_d3d=*/nullptr, /*array_slice=*/0);
   EXPECT_TRUE(init_result.is_ok());
@@ -138,13 +130,11 @@
 TEST_F(D3D11TextureWrapperUnittest, UnknownInitFails) {
   STOP_IF_WIN7();
   const DXGI_FORMAT dxgi_format = DXGI_FORMAT_UNKNOWN;
-  const VideoPixelFormat pixel_format = PIXEL_FORMAT_UNKNOWN;
 
-  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format,
-                                                           pixel_format);
+  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format);
   const Status init_result = wrapper->Init(
       task_runner_, get_helper_cb_, /*texture_d3d=*/nullptr, /*array_slice=*/0);
   EXPECT_FALSE(init_result.is_ok());
 }
 
-}  // namespace media
\ No newline at end of file
+}  // namespace media
diff --git a/media/gpu/windows/dxva_video_decode_accelerator_win.cc b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
index 02f496c..e7a5084 100644
--- a/media/gpu/windows/dxva_video_decode_accelerator_win.cc
+++ b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
@@ -2732,16 +2732,12 @@
       // this |picture_buffer| will be updated when the video frame is created.
       const auto& mailbox = gpu::Mailbox::GenerateForSharedImage();
 
-      auto shared_image = std::make_unique<gpu::SharedImageBackingD3D>(
+      auto shared_image = gpu::SharedImageBackingD3D::CreateFromGLTexture(
           mailbox, viz_formats[texture_idx],
           picture_buffer->texture_size(texture_idx),
           picture_buffer->color_space(), kTopLeft_GrSurfaceOrigin,
-          kPremul_SkAlphaType, shared_image_usage,
-          /*swap_chain=*/nullptr, std::move(gl_texture),
-          picture_buffer->gl_image(),
-          /*buffer_index=*/0, gl_image_dxgi->texture(),
-          base::win::ScopedHandle(),
-          /*dxgi_keyed_mutex=*/nullptr);
+          kPremul_SkAlphaType, shared_image_usage, gl_image_dxgi->texture(),
+          std::move(gl_texture));
 
       // Caller is assumed to provide cleared d3d textures.
       shared_image->SetCleared();
diff --git a/media/remoting/metrics.cc b/media/remoting/metrics.cc
index fd3252c..6d2f1f4 100644
--- a/media/remoting/metrics.cc
+++ b/media/remoting/metrics.cc
@@ -182,6 +182,10 @@
 
 void SessionMetricsRecorder::RecordCompatibility(
     RemotingCompatibility compatibility) {
+  if (did_record_compatibility_) {
+    return;
+  }
+  did_record_compatibility_ = true;
   base::UmaHistogramEnumeration("Media.Remoting.Compatibility", compatibility);
 }
 
diff --git a/media/remoting/metrics.h b/media/remoting/metrics.h
index 017ad9de..2a0bc084 100644
--- a/media/remoting/metrics.h
+++ b/media/remoting/metrics.h
@@ -70,7 +70,8 @@
   // for the recorder instance.
   void RecordVideoPixelRateSupport(PixelRateSupport support);
 
-  // Records the compatibility of a media content with remoting.
+  // Records the compatibility of a media content with remoting. Records only on
+  // the first call for the recorder instance.
   void RecordCompatibility(RemotingCompatibility compatibility);
 
  private:
@@ -114,6 +115,7 @@
   bool remote_playback_is_disabled_ = false;
 
   bool did_record_pixel_rate_support_ = false;
+  bool did_record_compatibility_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(SessionMetricsRecorder);
 };
diff --git a/media/remoting/metrics_unittest.cc b/media/remoting/metrics_unittest.cc
index 8ac9b0b6..cd6f8b6f 100644
--- a/media/remoting/metrics_unittest.cc
+++ b/media/remoting/metrics_unittest.cc
@@ -51,13 +51,13 @@
   recorder_.RecordCompatibility(RemotingCompatibility::kCompatible);
   recorder_.RecordCompatibility(RemotingCompatibility::kIncompatibleVideoCodec);
 
+  // We record only for the first RecordCompatibility() call for the
+  // given SessionMetricsRecorder instance.
   EXPECT_THAT(
       tester.GetAllSamples(kCompatibilityHistogramName),
-      ElementsAre(
-          Bucket(static_cast<int>(RemotingCompatibility::kCompatible), 1),
-          Bucket(
-              static_cast<int>(RemotingCompatibility::kIncompatibleVideoCodec),
-              2)));
+      ElementsAre(Bucket(
+          static_cast<int>(RemotingCompatibility::kIncompatibleVideoCodec),
+          1)));
 }
 
 }  // namespace remoting
diff --git a/remoting/protocol/video_channel_state_observer.h b/remoting/protocol/video_channel_state_observer.h
index 86beae1..a17d3d3 100644
--- a/remoting/protocol/video_channel_state_observer.h
+++ b/remoting/protocol/video_channel_state_observer.h
@@ -22,6 +22,13 @@
   virtual void OnTargetBitrateChanged(int bitrate_kbps) = 0;
   virtual void OnRttUpdate(base::TimeDelta rtt) = 0;
 
+  // Notifies the scheduler that the encoder wants to continue receiving
+  // captured frames even if nothing has changed (so it can re-encode the frame
+  // with increasing quality). If this is false, the scheduler need not send
+  // "empty" frames (no update region) for encoding/sending. The initial
+  // state is false, and the notification is raised whenever the state changes.
+  virtual void OnTopOffActive(bool active) = 0;
+
   // Called when the encoder has finished encoding a frame, and before it is
   // passed to WebRTC's registered callback. |frame| is non-const so that
   // WebrtcVideoStream can add timestamps to it before sending.
diff --git a/remoting/protocol/webrtc_frame_scheduler_simple.cc b/remoting/protocol/webrtc_frame_scheduler_simple.cc
index f1f2d3a..c1666b7d 100644
--- a/remoting/protocol/webrtc_frame_scheduler_simple.cc
+++ b/remoting/protocol/webrtc_frame_scheduler_simple.cc
@@ -110,6 +110,14 @@
   rtt_estimate_ = rtt;
 }
 
+void WebrtcFrameSchedulerSimple::OnTopOffActive(bool active) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  top_off_is_active_ = active;
+  if (active) {
+    ScheduleNextFrame();
+  }
+}
+
 void WebrtcFrameSchedulerSimple::Start(
     const base::RepeatingClosure& capture_callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/remoting/protocol/webrtc_frame_scheduler_simple.h b/remoting/protocol/webrtc_frame_scheduler_simple.h
index b9d8140..c96ac10 100644
--- a/remoting/protocol/webrtc_frame_scheduler_simple.h
+++ b/remoting/protocol/webrtc_frame_scheduler_simple.h
@@ -37,6 +37,7 @@
   void OnKeyFrameRequested() override;
   void OnTargetBitrateChanged(int bitrate_kbps) override;
   void OnRttUpdate(base::TimeDelta rtt) override;
+  void OnTopOffActive(bool active) override;
   void OnFrameEncoded(WebrtcVideoEncoder::EncodeResult encode_result,
                       WebrtcVideoEncoder::EncodedFrame* encoded_frame) override;
   void OnEncodedFrameSent(
diff --git a/remoting/protocol/webrtc_video_stream.cc b/remoting/protocol/webrtc_video_stream.cc
index d2d90d4..c820733 100644
--- a/remoting/protocol/webrtc_video_stream.cc
+++ b/remoting/protocol/webrtc_video_stream.cc
@@ -201,6 +201,11 @@
   scheduler_->OnRttUpdate(rtt);
 }
 
+void WebrtcVideoStream::OnTopOffActive(bool active) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  scheduler_->OnTopOffActive(active);
+}
+
 void WebrtcVideoStream::OnCaptureResult(
     webrtc::DesktopCapturer::Result result,
     std::unique_ptr<webrtc::DesktopFrame> frame) {
diff --git a/remoting/protocol/webrtc_video_stream.h b/remoting/protocol/webrtc_video_stream.h
index 9a61fd2..9d185202 100644
--- a/remoting/protocol/webrtc_video_stream.h
+++ b/remoting/protocol/webrtc_video_stream.h
@@ -63,6 +63,7 @@
   void OnKeyFrameRequested() override;
   void OnTargetBitrateChanged(int bitrate_kbps) override;
   void OnRttUpdate(base::TimeDelta rtt) override;
+  void OnTopOffActive(bool active) override;
   void OnFrameEncoded(WebrtcVideoEncoder::EncodeResult encode_result,
                       WebrtcVideoEncoder::EncodedFrame* frame) override;
   void OnEncodedFrameSent(
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index f1417ad..bfc95bd 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -73,7 +73,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -106,7 +106,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -143,7 +143,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -176,7 +176,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -212,7 +212,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -247,7 +247,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -282,7 +282,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -316,7 +316,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -349,7 +349,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -387,7 +387,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -420,7 +420,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -453,7 +453,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -486,7 +486,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -519,7 +519,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -555,7 +555,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -588,7 +588,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -621,7 +621,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -669,7 +669,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -706,7 +706,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -739,7 +739,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -772,7 +772,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -805,7 +805,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -842,7 +842,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -875,7 +875,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -908,7 +908,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -941,7 +941,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -990,7 +990,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -1037,7 +1037,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -1083,7 +1083,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -1134,7 +1134,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 6c9a01a..24a7c38 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -12238,7 +12238,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12271,7 +12271,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12308,7 +12308,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12341,7 +12341,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12377,7 +12377,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12412,7 +12412,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12447,7 +12447,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12481,7 +12481,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12514,7 +12514,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12552,7 +12552,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12585,7 +12585,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12618,7 +12618,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12651,7 +12651,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12684,7 +12684,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12720,7 +12720,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12753,7 +12753,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12786,7 +12786,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12834,7 +12834,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12871,7 +12871,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12904,7 +12904,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12937,7 +12937,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -12970,7 +12970,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -13007,7 +13007,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -13040,7 +13040,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -13073,7 +13073,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -13106,7 +13106,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -13155,7 +13155,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -13202,7 +13202,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -13248,7 +13248,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -13299,7 +13299,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 630fa76..20a3b95b 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -6493,7 +6493,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -6543,7 +6543,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -6593,7 +6593,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -6643,7 +6643,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -6697,7 +6697,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -6756,7 +6756,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -6815,7 +6815,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -6866,7 +6866,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -6916,7 +6916,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -6963,7 +6963,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
@@ -7014,7 +7014,7 @@
             {
               "cpu": "x86",
               "kvm": "1",
-              "os": "Ubuntu-16.04",
+              "os": "Ubuntu-18.04",
               "pool": "chromium.tests"
             }
           ],
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 12b6fc5..9735e5f 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -9713,7 +9713,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -9731,7 +9731,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -9752,7 +9752,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -9771,7 +9771,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -9789,7 +9789,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -9807,7 +9807,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -9825,7 +9825,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -9843,7 +9843,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -9861,7 +9861,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -9879,7 +9879,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -9897,7 +9897,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -9916,7 +9916,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -9934,7 +9934,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -9952,7 +9952,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -9992,7 +9992,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10010,7 +10010,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10028,7 +10028,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10046,7 +10046,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10064,7 +10064,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10082,7 +10082,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10100,7 +10100,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10118,7 +10118,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10136,7 +10136,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10173,7 +10173,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10191,7 +10191,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10209,7 +10209,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10227,7 +10227,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10245,7 +10245,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10263,7 +10263,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10281,7 +10281,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10299,7 +10299,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10317,7 +10317,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10335,7 +10335,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10353,7 +10353,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10371,7 +10371,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10389,7 +10389,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10407,7 +10407,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10425,7 +10425,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10443,7 +10443,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10461,7 +10461,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10479,7 +10479,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10497,7 +10497,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10515,7 +10515,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10533,7 +10533,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10570,7 +10570,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10588,7 +10588,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10606,7 +10606,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10624,7 +10624,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10642,7 +10642,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10660,7 +10660,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10678,7 +10678,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10696,7 +10696,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10714,7 +10714,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10732,7 +10732,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10750,7 +10750,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10768,7 +10768,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10786,7 +10786,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10804,7 +10804,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10822,7 +10822,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10840,7 +10840,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10858,7 +10858,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10876,7 +10876,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10894,7 +10894,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10912,7 +10912,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10930,7 +10930,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10948,7 +10948,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10966,7 +10966,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -10984,7 +10984,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11002,7 +11002,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11020,7 +11020,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11038,7 +11038,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11056,7 +11056,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11074,7 +11074,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11092,7 +11092,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11110,7 +11110,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11128,7 +11128,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11146,7 +11146,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11164,7 +11164,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11182,7 +11182,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11200,7 +11200,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11218,7 +11218,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11236,7 +11236,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11254,7 +11254,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11272,7 +11272,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11290,7 +11290,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11308,7 +11308,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11326,7 +11326,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11344,7 +11344,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11363,7 +11363,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11381,7 +11381,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11406,7 +11406,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11461,7 +11461,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11480,7 +11480,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11503,7 +11503,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11526,7 +11526,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11548,7 +11548,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11570,7 +11570,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11592,7 +11592,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11624,7 +11624,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11679,7 +11679,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "idempotent": false,
@@ -11761,7 +11761,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11800,7 +11800,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11852,7 +11852,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11869,7 +11869,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11889,7 +11889,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11907,7 +11907,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11924,7 +11924,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11941,7 +11941,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11958,7 +11958,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11975,7 +11975,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -11992,7 +11992,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12009,7 +12009,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12026,7 +12026,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12044,7 +12044,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12061,7 +12061,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12078,7 +12078,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12116,7 +12116,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12133,7 +12133,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12150,7 +12150,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12167,7 +12167,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12184,7 +12184,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12201,7 +12201,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12218,7 +12218,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12235,7 +12235,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12252,7 +12252,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12287,7 +12287,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12304,7 +12304,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12321,7 +12321,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12338,7 +12338,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12355,7 +12355,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12372,7 +12372,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12389,7 +12389,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12406,7 +12406,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12423,7 +12423,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12440,7 +12440,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12457,7 +12457,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12474,7 +12474,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12491,7 +12491,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12508,7 +12508,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12525,7 +12525,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12542,7 +12542,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12559,7 +12559,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12576,7 +12576,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12593,7 +12593,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12610,7 +12610,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12627,7 +12627,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12662,7 +12662,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12679,7 +12679,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12696,7 +12696,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12713,7 +12713,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12730,7 +12730,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12747,7 +12747,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12764,7 +12764,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12781,7 +12781,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12798,7 +12798,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12815,7 +12815,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12832,7 +12832,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12849,7 +12849,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12866,7 +12866,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12883,7 +12883,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12900,7 +12900,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -12918,7 +12918,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12935,7 +12935,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12952,7 +12952,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12969,7 +12969,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -12986,7 +12986,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13003,7 +13003,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13020,7 +13020,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13037,7 +13037,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13054,7 +13054,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13071,7 +13071,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13088,7 +13088,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13105,7 +13105,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13122,7 +13122,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13139,7 +13139,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13156,7 +13156,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13173,7 +13173,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13190,7 +13190,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13207,7 +13207,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13224,7 +13224,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13241,7 +13241,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13258,7 +13258,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13275,7 +13275,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13292,7 +13292,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13309,7 +13309,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13326,7 +13326,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13343,7 +13343,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13360,7 +13360,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13377,7 +13377,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13394,7 +13394,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13412,7 +13412,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13429,7 +13429,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13453,7 +13453,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13504,7 +13504,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13526,7 +13526,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13547,7 +13547,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13568,7 +13568,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13589,7 +13589,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "idempotent": false,
@@ -13668,7 +13668,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13688,7 +13688,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 58bddb8..6dbd07bf 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -5020,7 +5020,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5040,7 +5040,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5061,7 +5061,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5082,7 +5082,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5102,7 +5102,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5122,7 +5122,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5142,7 +5142,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5162,7 +5162,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5182,7 +5182,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5202,7 +5202,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5222,7 +5222,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5243,7 +5243,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -5264,7 +5264,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5284,7 +5284,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5326,7 +5326,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5346,7 +5346,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5366,7 +5366,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5386,7 +5386,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5406,7 +5406,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5426,7 +5426,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5446,7 +5446,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5466,7 +5466,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -5487,7 +5487,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5549,7 +5549,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5569,7 +5569,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5589,7 +5589,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5609,7 +5609,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5629,7 +5629,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5649,7 +5649,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5669,7 +5669,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5689,7 +5689,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5709,7 +5709,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5729,7 +5729,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5749,7 +5749,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5769,7 +5769,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5789,7 +5789,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5836,7 +5836,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5856,7 +5856,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5876,7 +5876,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5896,7 +5896,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5916,7 +5916,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5936,7 +5936,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5956,7 +5956,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -5976,7 +5976,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -5997,7 +5997,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6017,7 +6017,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6037,7 +6037,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6057,7 +6057,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6077,7 +6077,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6097,7 +6097,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6117,7 +6117,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6137,7 +6137,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6157,7 +6157,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6177,7 +6177,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6197,7 +6197,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6217,7 +6217,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6237,7 +6237,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6257,7 +6257,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6298,7 +6298,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6318,7 +6318,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6338,7 +6338,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6358,7 +6358,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6378,7 +6378,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6398,7 +6398,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6418,7 +6418,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6438,7 +6438,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6458,7 +6458,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6478,7 +6478,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6498,7 +6498,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6518,7 +6518,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6538,7 +6538,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6558,7 +6558,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6599,7 +6599,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6619,7 +6619,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6639,7 +6639,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6659,7 +6659,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -6680,7 +6680,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6700,7 +6700,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6720,7 +6720,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6740,7 +6740,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6760,7 +6760,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6780,7 +6780,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6800,7 +6800,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6820,7 +6820,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6840,7 +6840,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6860,7 +6860,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6881,7 +6881,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -6901,7 +6901,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index 6e4a47a..b0155c26 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -299,6 +299,9 @@
         "override_compile_targets": [
           "performance_test_suite"
         ],
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index b3a3e4e4..b1e70e2 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -238,7 +238,7 @@
         {
           'cpu': 'x86',
           'kvm': '1',
-          'os': 'Ubuntu-16.04',
+          'os': 'Ubuntu-18.04',
           'pool': 'chromium.tests',
         }
       ],
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index e5089d9..0062eb2 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -234,12 +234,26 @@
         "args": [
           "--additional-env-var=LLVM_PROFILE_FILE=${ISOLATED_OUTDIR}/profraw/default-%2m.profraw",
         ],
+        # TODO(crbug/1200134): Remove it once blink tests is fixed on
+        # bionic bots.
+        'swarming': {
+          'dimension_sets': [
+            {
+              'os': 'Ubuntu-16.04',
+            }
+          ],
+        },
       },
       'Linux Tests (dbg)(1)': {
         'args': [
           '--debug',
         ],
         'swarming': {
+          'dimension_sets': [
+            {
+              'os': 'Ubuntu-16.04',
+            }
+          ],
           'shards': 25,
         },
       },
@@ -573,6 +587,12 @@
         # These are very slow on the ASAN trybot for some reason.
         # crbug.com/794372
         'swarming': {
+          'dimension_sets': [
+            {
+              # TODO(crbug.com/1200574): Remove this exception.
+              'os': 'Ubuntu-16.04',
+            }
+          ],
           'shards': 40,
         },
       },
@@ -598,9 +618,25 @@
           '--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter',
         ],
       },
+      # TODO(crbug/1200334): Remove it once interactive_ui_tests is fixed on
+      # bionic bots.
+      'Linux Tests': {
+        'swarming': {
+          'dimension_sets': [
+            {
+              'os': 'Ubuntu-16.04',
+            }
+          ],
+        },
+      },
       'Linux Tests (dbg)(1)': {
         # crbug.com/1066161
         'swarming': {
+          'dimension_sets': [
+            {
+              'os': 'Ubuntu-16.04',
+            }
+          ],
           'shards': 20,
         },
       },
@@ -1169,11 +1205,6 @@
       },
       'Linux ASan LSan Tests (1)': {
         'swarming': {
-          'dimension_sets': [
-            {
-              'os': 'Ubuntu-18.04',
-            }
-          ],
           'shards': 12,
         },
       },
@@ -1193,25 +1224,6 @@
           'shards': 15,
         },
       },
-      # TODO(crbug/1199425): Remove it once bionic is the default option.
-      'Linux Tests': {
-        'swarming': {
-          'dimension_sets': [
-            {
-              'os': 'Ubuntu-18.04',
-            }
-          ],
-        },
-      },
-      'Linux Tests (dbg)(1)': {
-        'swarming': {
-          'dimension_sets': [
-            {
-              'os': 'Ubuntu-18.04',
-            }
-          ],
-        },
-      },
       'Lollipop Phone Tester': {
         'swarming': {
           'shards': 10,
@@ -1394,11 +1406,6 @@
         # These are slow on the ASAN trybot for some reason.
         # crbug.com/794372
         'swarming': {
-          'dimension_sets': [
-            {
-              'os': 'Ubuntu-18.04',
-            }
-          ],
           'shards': 2,
         },
       },
@@ -1796,6 +1803,26 @@
           'shards': 32, # Adjusted for testing, see https://crbug.com/1179567
         },
       },
+      # TODO(crbug/1199425): Remove it once interactive_ui_tests is fixed on
+      # bionic bots.
+      'Linux Tests': {
+        'swarming': {
+          'dimension_sets': [
+            {
+              'os': 'Ubuntu-16.04',
+            }
+          ],
+        },
+      },
+      'Linux Tests (dbg)(1)': {
+        'swarming': {
+          'dimension_sets': [
+            {
+              'os': 'Ubuntu-16.04',
+            }
+          ],
+        },
+      },
       'ToTLinuxTSan': {
         # These are slow on the TSan bots for some reason, crbug.com/794372
         'swarming': {
@@ -2162,11 +2189,6 @@
         # These are very slow on the ASAN trybot for some reason.
         # crbug.com/794372
         'swarming': {
-          'dimension_sets': [
-            {
-              'os': 'Ubuntu-18.04',
-            }
-          ],
           'shards': 16,
         },
       },
@@ -2262,6 +2284,24 @@
         "args": [
           "--additional-env-var=LLVM_PROFILE_FILE=${ISOLATED_OUTDIR}/profraw/default-%2m.profraw",
         ],
+        # TODO(crbug/1200134): Remove it once blink tests is fixed on
+        # bionic bots.
+        'swarming': {
+          'dimension_sets': [
+            {
+              'os': 'Ubuntu-16.04',
+            }
+          ],
+        },
+      },
+      'Linux Tests (dbg)(1)': {
+        'swarming': {
+          'dimension_sets': [
+            {
+              'os': 'Ubuntu-16.04',
+            }
+          ],
+        },
       },
       'linux-code-coverage': {
         'args': [
@@ -2621,11 +2661,6 @@
       },
       'Linux ASan LSan Tests (1)': {
         'swarming': {
-          'dimension_sets': [
-            {
-              'os': 'Ubuntu-18.04',
-            }
-          ],
           'shards': 4,
         },
       },
@@ -2741,28 +2776,12 @@
           '--xvfb',
           '--jobs=1',
         ],
-        # TODO(crbug/1199425): Remove it once bionic is the default option.
-        'swarming': {
-          'dimension_sets': [
-            {
-              'os': 'Ubuntu-18.04',
-            }
-          ],
-        },
       },
       'Linux Tests (dbg)(1)': {
         'args': [
           '--xvfb',
           '--jobs=1',
         ],
-        # TODO(crbug/1199425): Remove it once bionic is the default option.
-        'swarming': {
-          'dimension_sets': [
-            {
-              'os': 'Ubuntu-18.04',
-            }
-          ],
-        },
       },
       # TODO crbug.com/1143924: Remove when experimentation is complete
       'Linux Tests Robocrop': {
@@ -2845,25 +2864,6 @@
   },
   'telemetry_unittests': {
     'modifications': {
-      # TODO(crbug/1199425): Remove it once bionic is the default option.
-      'Linux Tests': {
-        'swarming': {
-          'dimension_sets': [
-            {
-              'os': 'Ubuntu-18.04',
-            }
-          ],
-        },
-      },
-      'Linux Tests (dbg)(1)': {
-        'swarming': {
-          'dimension_sets': [
-            {
-              'os': 'Ubuntu-18.04',
-            }
-          ],
-        },
-      },
       'Win10 Tests x64 (dbg)': {
         'experiment_percentage': 100,  # crbug.com/870673
       },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 9bf0f56..ef314b2 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -5018,7 +5018,7 @@
       'Linux Tests': {
         'mixins': [
           'isolate_profile_data',
-          'linux-xenial',
+          'linux-bionic',
         ],
         'test_suites': {
           'gtest_tests': 'chromium_linux_gtests',
@@ -5028,7 +5028,7 @@
       },
       'Linux Tests (dbg)(1)': {
         'mixins': [
-          'linux-xenial',
+          'linux-bionic',
         ],
         'test_suites': {
           'gtest_tests': 'chromium_linux_gtests',
@@ -5317,7 +5317,7 @@
       },
       'Linux ASan LSan Tests (1)': {
         'mixins': [
-          'linux-xenial',
+          'linux-bionic',
         ],
         'test_suites': {
           'gtest_tests': 'chromium_linux_and_gl_gtests',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 0acf5bfa..ffb3181 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3722,6 +3722,18 @@
                     },
                     "enable_features": [
                         "DiscoverFeedInNtp",
+                        "InterestFeedNoticeCardAutoDismiss",
+                        "RefactoredNTP"
+                    ]
+                },
+                {
+                    "name": "EnabledM2FullSetNoAutodismiss",
+                    "params": {
+                        "DiscoverFeedIsNativeUIEnabled": "true",
+                        "RefactoredNTPLoggingEnabled": "true"
+                    },
+                    "enable_features": [
+                        "DiscoverFeedInNtp",
                         "RefactoredNTP"
                     ]
                 }
@@ -4784,10 +4796,10 @@
             ],
             "experiments": [
                 {
-                    "name": "EnabledColorfulMic",
+                    "name": "Enabled",
                     "params": {
-                        "colorful_mic": "true",
-                        "min_agsa_version": "12.13.8"
+                        "colorful_mic": "false",
+                        "min_agsa_version": "12.15"
                     },
                     "enable_features": [
                         "OmniboxAssistantVoiceSearch"
@@ -4797,12 +4809,50 @@
                     ]
                 },
                 {
+                    "name": "EnabledColorfulMic",
+                    "params": {
+                        "colorful_mic": "true",
+                        "min_agsa_version": "12.15"
+                    },
+                    "enable_features": [
+                        "OmniboxAssistantVoiceSearch"
+                    ],
+                    "disable_features": [
+                        "LensCameraAssistedSearch"
+                    ]
+                },
+                {
+                    "name": "AlwaysMicWithAssistant",
+                    "params": {
+                        "colorful_mic": "false",
+                        "min_agsa_version": "12.15"
+                    },
+                    "enable_features": [
+                        "OmniboxAssistantVoiceSearch",
+                        "VoiceButtonInTopToolbar",
+                        "VoiceSearchAudioCapturePolicy"
+                    ]
+                },
+                {
+                    "name": "AlwaysMicWithoutAssistant",
+                    "params": {
+                        "min_agsa_version": "12.15"
+                    },
+                    "enable_features": [
+                        "VoiceButtonInTopToolbar",
+                        "VoiceSearchAudioCapturePolicy"
+                    ],
+                    "disable_features": [
+                        "OmniboxAssistantVoiceSearch"
+                    ]
+                },
+                {
                     "name": "EnabledLensThenDefaultMic",
                     "params": {
                         "disableOnIncognito": "true",
                         "enableCameraAssistedSearchOnLowEndDevice": "false",
                         "enableCameraAssistedSearchOnTablet": "false",
-                        "minAgsaVersionForLensCameraAssistedSearch": "12.13.8",
+                        "minAgsaVersionForLensCameraAssistedSearch": "12.15",
                         "searchBoxStartVariantForLensCameraAssistedSearch": "true"
                     },
                     "enable_features": [
@@ -4818,7 +4868,7 @@
                         "disableOnIncognito": "true",
                         "enableCameraAssistedSearchOnLowEndDevice": "false",
                         "enableCameraAssistedSearchOnTablet": "false",
-                        "minAgsaVersionForLensCameraAssistedSearch": "12.13.8",
+                        "minAgsaVersionForLensCameraAssistedSearch": "12.15",
                         "searchBoxStartVariantForLensCameraAssistedSearch": "false"
                     },
                     "enable_features": [
@@ -4834,7 +4884,7 @@
                         "disableOnIncognito": "true",
                         "enableCameraAssistedSearchOnLowEndDevice": "true",
                         "enableCameraAssistedSearchOnTablet": "false",
-                        "minAgsaVersionForLensCameraAssistedSearch": "12.13.8",
+                        "minAgsaVersionForLensCameraAssistedSearch": "12.15",
                         "searchBoxStartVariantForLensCameraAssistedSearch": "false"
                     },
                     "enable_features": [
@@ -5185,9 +5235,11 @@
                     "name": "NonAdaptiveMic_Actions_20210423",
                     "params": {
                         "colorful_mic": "true",
+                        "experiment_id": "47453836",
                         "min_agsa_version": "12.15"
                     },
                     "enable_features": [
+                        "AssistantIntentExperimentId",
                         "AssistantIntentPageUrl",
                         "AssistantIntentTranslateInfo",
                         "OmniboxAssistantVoiceSearch",
diff --git a/third_party/blink/renderer/core/paint/cull_rect_updater.cc b/third_party/blink/renderer/core/paint/cull_rect_updater.cc
index 9170c1cf..a1c572a4 100644
--- a/third_party/blink/renderer/core/paint/cull_rect_updater.cc
+++ b/third_party/blink/renderer/core/paint/cull_rect_updater.cc
@@ -73,12 +73,18 @@
   if (root_layer_.GetLayoutObject().GetFrameView()->ShouldThrottleRendering())
     return;
 
+  bool should_use_infinite =
+      PaintLayerPainter(root_layer_).ShouldUseInfiniteCullRect();
   auto& fragment =
       root_layer_.GetLayoutObject().GetMutableForPainting().FirstFragment();
-  SetFragmentCullRect(root_layer_, fragment, input_cull_rect);
+  SetFragmentCullRect(
+      root_layer_, fragment,
+      should_use_infinite ? CullRect::Infinite() : input_cull_rect);
   bool force_update_children = SetFragmentContentsCullRect(
       root_layer_, fragment,
-      ComputeFragmentContentsCullRect(root_layer_, fragment, input_cull_rect));
+      should_use_infinite ? CullRect::Infinite()
+                          : ComputeFragmentContentsCullRect(
+                                root_layer_, fragment, input_cull_rect));
   UpdateForDescendants(root_layer_, force_update_children);
 }
 
diff --git a/third_party/blink/renderer/platform/heap/test/heap_test.cc b/third_party/blink/renderer/platform/heap/test/heap_test.cc
index 565b0f2d..f2dfa360 100644
--- a/third_party/blink/renderer/platform/heap/test/heap_test.cc
+++ b/third_party/blink/renderer/platform/heap/test/heap_test.cc
@@ -2368,7 +2368,7 @@
     Persistent<InlinedVectorObjectWrapper> vector_wrapper =
         MakeGarbageCollected<InlinedVectorObjectWrapper>();
     ConservativelyCollectGarbage();
-    EXPECT_EQ(2, InlinedVectorObject::destructor_calls_);
+    EXPECT_LE(2, InlinedVectorObject::destructor_calls_);
   }
   PreciselyCollectGarbage();
   EXPECT_LE(8, InlinedVectorObject::destructor_calls_);
@@ -2427,7 +2427,7 @@
     vector.push_back(i2);
   }
   PreciselyCollectGarbage();
-  EXPECT_EQ(4, InlinedVectorObjectWithVtable::destructor_calls_);
+  EXPECT_LE(4, InlinedVectorObjectWithVtable::destructor_calls_);
 
   InlinedVectorObjectWithVtable::destructor_calls_ = 0;
   {
@@ -2437,7 +2437,7 @@
     vector.push_back(i2);  // This allocates an out-of-line buffer.
   }
   PreciselyCollectGarbage();
-  EXPECT_EQ(5, InlinedVectorObjectWithVtable::destructor_calls_);
+  EXPECT_LE(5, InlinedVectorObjectWithVtable::destructor_calls_);
 
   InlinedVectorObjectWithVtable::destructor_calls_ = 0;
   {
@@ -2447,17 +2447,17 @@
     vector.push_back(i2);
   }
   PreciselyCollectGarbage();
-  EXPECT_EQ(4, InlinedVectorObjectWithVtable::destructor_calls_);
+  EXPECT_LE(4, InlinedVectorObjectWithVtable::destructor_calls_);
 
   InlinedVectorObjectWithVtable::destructor_calls_ = 0;
   {
     Persistent<InlinedVectorObjectWithVtableWrapper> vector_wrapper =
         MakeGarbageCollected<InlinedVectorObjectWithVtableWrapper>();
     ConservativelyCollectGarbage();
-    EXPECT_EQ(3, InlinedVectorObjectWithVtable::destructor_calls_);
+    EXPECT_LE(3, InlinedVectorObjectWithVtable::destructor_calls_);
   }
   PreciselyCollectGarbage();
-  EXPECT_EQ(9, InlinedVectorObjectWithVtable::destructor_calls_);
+  EXPECT_LE(9, InlinedVectorObjectWithVtable::destructor_calls_);
 }
 #endif
 
diff --git a/third_party/blink/renderer/platform/p2p/network_list_manager.h b/third_party/blink/renderer/platform/p2p/network_list_manager.h
index ba9e48e3..9a197eb8 100644
--- a/third_party/blink/renderer/platform/p2p/network_list_manager.h
+++ b/third_party/blink/renderer/platform/p2p/network_list_manager.h
@@ -9,7 +9,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_P2P_NETWORK_LIST_MANAGER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_P2P_NETWORK_LIST_MANAGER_H_
 
-#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 
 namespace blink {
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 337b5bb..e91d7629 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -7079,6 +7079,14 @@
 crbug.com/1204498 [ Linux ] virtual/scroll-unification/fast/events/hit-test-cache-iframes.html [ Pass Failure ]
 
 # Sheriff 2021-05-03
+crbug.com/1205133 [ Linux ] virtual/gpu/fast/canvas/canvas-blending-gradient-over-color.html [ Pass Timeout ]
+crbug.com/1205133 [ Mac ] virtual/gpu/fast/canvas/canvas-blending-gradient-over-color.html [ Pass Timeout ]
+crbug.com/1205020 [ Win ] virtual/oopr-canvas2d/fast/canvas/canvas-blend-image.html [ Pass Failure ]
+crbug.com/1205020 [ Linux ] virtual/oopr-canvas2d/fast/canvas/canvas-blend-image.html [ Pass Failure ]
 crbug.com/1161155 [ Mac ] paint/invalidation/image/canvas-composite-repaint-by-all-imagesource.html [ Failure ]
 crbug.com/1205012 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/reporting-observer.html [ Timeout Pass ]
 crbug.com/1197465 [ Linux ] virtual/scroll-unification/fast/events/mouse-cursor-no-mousemove.html [ Pass Failure ]
+crbug.com/1093041 fast/canvas/color-space/canvas-createImageBitmap-rec2020.html [ Pass Failure ]
+crbug.com/1093041 virtual/gpu/fast/canvas/color-space/canvas-createImageBitmap-rec2020.html [ Pass Failure Timeout ]
+crbug.com/1093041 virtual/oopr-canvas2d/fast/canvas/color-space/canvas-createImageBitmap-rec2020.html [ Pass Failure Timeout ]
+crbug.com/1146560 virtual/oopr-canvas2d/fast/canvas/color-space/canvas-createImageBitmap-e_srgb.html [ Pass Failure Timeout ]
diff --git a/third_party/blink/web_tests/WebGPUExpectations b/third_party/blink/web_tests/WebGPUExpectations
index bd4400f..85fa5df 100644
--- a/third_party/blink/web_tests/WebGPUExpectations
+++ b/third_party/blink/web_tests/WebGPUExpectations
@@ -34,9 +34,6 @@
 # Fails or crashes on numerous combinations of backends, hardware, and validation layers
 wpt_internal/webgpu/cts.html?q=webgpu:api,operation,render_pass,resolve:* [ Skip ]
 
-# Test uses incorrect WGSL
-crbug.com/tint/354 wpt_internal/webgpu/cts.html?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:readMethod="Sample";* [ Failure ]
-
 ## webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,*
 
 # Windows (Intel but not NVIDIA). Possibly nondeterministic.
diff --git a/third_party/leveldatabase/BUILD.gn b/third_party/leveldatabase/BUILD.gn
index 49d28018..744ce88 100644
--- a/third_party/leveldatabase/BUILD.gn
+++ b/third_party/leveldatabase/BUILD.gn
@@ -21,8 +21,6 @@
   "env_chromium.h",
   "leveldb_chrome.cc",
   "leveldb_chrome.h",
-  "leveldb_features.cc",
-  "leveldb_features.h",
   "port/port_chromium.cc",
   "port/port_chromium.h",
   "src/db/builder.cc",
diff --git a/third_party/leveldatabase/env_chromium.cc b/third_party/leveldatabase/env_chromium.cc
index 73d42e3a..dbf99fd8 100644
--- a/third_party/leveldatabase/env_chromium.cc
+++ b/third_party/leveldatabase/env_chromium.cc
@@ -37,7 +37,6 @@
 #include "components/services/storage/public/cpp/filesystem/filesystem_proxy.h"
 #include "third_party/leveldatabase/chromium_logger.h"
 #include "third_party/leveldatabase/leveldb_chrome.h"
-#include "third_party/leveldatabase/leveldb_features.h"
 #include "third_party/leveldatabase/src/include/leveldb/options.h"
 #include "third_party/re2/src/re2/re2.h"
 
@@ -1416,8 +1415,6 @@
                           const std::string& name,
                           std::unique_ptr<leveldb::DB>* dbptr) {
   DCHECK(options.create_if_missing);
-  if (!base::FeatureList::IsEnabled(leveldb::kLevelDBRewriteFeature))
-    return Status::OK();
   if (leveldb_chrome::IsMemEnv(options.env))
     return Status::OK();
   TRACE_EVENT1("leveldb", "ChromiumEnv::RewriteDB", "name", name);
diff --git a/third_party/leveldatabase/env_chromium.h b/third_party/leveldatabase/env_chromium.h
index e1466cb..20e4c87f 100644
--- a/third_party/leveldatabase/env_chromium.h
+++ b/third_party/leveldatabase/env_chromium.h
@@ -315,7 +315,6 @@
 // an identical copy. |dbptr| will be replaced with the new database on success.
 // If the rewrite fails e.g. because we can't write to the temporary location,
 // the old db is returned if possible, otherwise |*dbptr| can become NULL.
-// The rewrite will only be performed if |kLevelDBRewriteFeature| is enabled.
 LEVELDB_EXPORT leveldb::Status RewriteDB(const leveldb_env::Options& options,
                                          const std::string& name,
                                          std::unique_ptr<leveldb::DB>* dbptr);
diff --git a/third_party/leveldatabase/env_chromium_unittest.cc b/third_party/leveldatabase/env_chromium_unittest.cc
index 9a278c69..da87e8c 100644
--- a/third_party/leveldatabase/env_chromium_unittest.cc
+++ b/third_party/leveldatabase/env_chromium_unittest.cc
@@ -14,7 +14,6 @@
 #include "base/lazy_instance.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_suite.h"
 #include "base/trace_event/process_memory_dump.h"
@@ -22,7 +21,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/leveldb_chrome.h"
-#include "third_party/leveldatabase/leveldb_features.h"
 #include "third_party/leveldatabase/src/include/leveldb/cache.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
 
@@ -658,9 +656,7 @@
 
 class ChromiumLevelDBRebuildTest : public ::testing::Test {
  protected:
-  ChromiumLevelDBRebuildTest() {
-    feature_list_.InitAndEnableFeature(leveldb::kLevelDBRewriteFeature);
-  }
+  ChromiumLevelDBRebuildTest() = default;
 
   void SetUp() override {
     testing::Test::SetUp();
@@ -670,7 +666,6 @@
   const base::FilePath& temp_path() const { return scoped_temp_dir_.GetPath(); }
 
  private:
-  base::test::ScopedFeatureList feature_list_;
   base::ScopedAllowBlockingForTesting allow_blocking_;
   base::ScopedTempDir scoped_temp_dir_;
 };
diff --git a/third_party/leveldatabase/leveldb_features.cc b/third_party/leveldatabase/leveldb_features.cc
deleted file mode 100644
index 85e9b58..0000000
--- a/third_party/leveldatabase/leveldb_features.cc
+++ /dev/null
@@ -1,11 +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. See the AUTHORS file for names of contributors.
-
-#include "third_party/leveldatabase/leveldb_features.h"
-
-namespace leveldb {
-
-const base::Feature kLevelDBRewriteFeature{"LevelDBPerformRewrite",
-                                           base::FEATURE_ENABLED_BY_DEFAULT};
-}  // namespace leveldb
diff --git a/third_party/leveldatabase/leveldb_features.h b/third_party/leveldatabase/leveldb_features.h
deleted file mode 100644
index a18cfa0c..0000000
--- a/third_party/leveldatabase/leveldb_features.h
+++ /dev/null
@@ -1,15 +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. See the AUTHORS file for names of contributors.
-
-#ifndef THIRD_PARTY_LEVELDATABASE_LEVELDB_FEATURES_H_
-#define THIRD_PARTY_LEVELDATABASE_LEVELDB_FEATURES_H_
-
-#include "base/feature_list.h"
-#include "leveldb/export.h"
-
-namespace leveldb {
-LEVELDB_EXPORT extern const base::Feature kLevelDBRewriteFeature;
-}  // namespace leveldb
-
-#endif  // THIRD_PARTY_LEVELDATABASE_LEVELDB_FEATURES_H_
diff --git a/third_party/webxr_test_pages/webxr-samples/tests/index.html b/third_party/webxr_test_pages/webxr-samples/tests/index.html
index 0e01b9b..45268001 100644
--- a/third_party/webxr_test_pages/webxr-samples/tests/index.html
+++ b/third_party/webxr_test_pages/webxr-samples/tests/index.html
@@ -115,6 +115,9 @@
 
             { title: 'Permission Requests', category: 'User Agent Behavior',
               path: 'permission-request.html',
+              variants: [
+                  {name: 'Start Inline Session', query: '?inlineSession=true'}
+              ],
               description: 'Tests requesting permissions from within XR.' },
 
             { title: 'Offscreen Canvas', category: 'Alternate WebGL Modes',
@@ -169,6 +172,16 @@
             polyfillLink.innerHTML = 'Open with Polyfill';
             links.appendChild(polyfillLink);
 
+            if (page.variants) {
+              for (let i = 0; i < page.variants.length; ++i) {
+                let variant = page.variants[i];
+                let link = document.createElement('a');
+                link.href = page.path + variant.query;
+                link.innerHTML = variant.name;
+                links.appendChild(link);
+              }
+            }
+
             let sourceLink = document.createElement('a');
             sourceLink.href = 'https://cs.chromium.org/chromium/src/third_party/webxr_test_pages/webxr-samples/tests/' + page.path;
             sourceLink.innerHTML = 'Source';
diff --git a/third_party/webxr_test_pages/webxr-samples/tests/permission-request.html b/third_party/webxr_test_pages/webxr-samples/tests/permission-request.html
index f391460..15e750ce 100644
--- a/third_party/webxr_test_pages/webxr-samples/tests/permission-request.html
+++ b/third_party/webxr_test_pages/webxr-samples/tests/permission-request.html
@@ -50,8 +50,21 @@
           handles those requests, if at all.
           <a class="back" href="./index.html">Back</a>
         </p>
+        <input id="startArSession" type="checkbox">
+        <label for="startArSession">immersive-ar mode</label><br/>
+
+        <input id="useDomOverlay" type="checkbox" disabled>
+        <label for="useDomOverlay">Enable DOM Overlay</label><br/>
+
+        <input id="fullscreenDomOverlay" type="checkbox" disabled>
+        <label for="fullscreenDomOverlay">
+          Use entire page as DOM overlay (vs. a single div)
+        </label><br/>
       </details>
     </header>
+    <div id="text-overlay">
+      <div id="text-info"></div>
+    </div>
     <script type="module">
       import {Scene} from '../js/cottontail/src/scenes/scene.js';
       import {Renderer, createWebGLContext} from '../js/cottontail/src/core/renderer.js';
@@ -67,12 +80,23 @@
         var polyfill = new WebXRPolyfill();
       }
 
+      let startInlineSession = false;
+      if (QueryArgs.getBool('inlineSession', false)) {
+        startInlineSession = true;
+      }
+
       const BUTTON_PER_ROW = 5;
       const BUTTON_ROW_ARC = Math.PI * 0.2;
       const BUTTON_ROW_HEIGHT = 0.12;
       const BUTTON_GRID_HEIGHT = 1.3;
       const BUTTON_GRID_DISTANCE = 1.0;
 
+      const startArSession = document.getElementById('startArSession');
+      startArSession.addEventListener('click', onSessionModeChanged);
+      const useDomOverlay = document.getElementById('useDomOverlay');
+      useDomOverlay.addEventListener('click', onDomOverlayChanged);
+      const fullscreenDomOverlay = document.getElementById('fullscreenDomOverlay');
+
       // XR globals.
       let xrButton = null;
       let xrImmersiveRefSpace = null;
@@ -89,23 +113,41 @@
       let xTexture = new UrlTexture('../../media/textures/x-button.png');
       let waitTexture = new UrlTexture('../test-media/third-party/hourglass-button.png');
 
+      function onSessionModeChanged() {
+        if (startArSession.checked) {
+          useDomOverlay.disabled = false;
+          fullscreenDomOverlay.disabled = !useDomOverlay.checked;
+        } else {
+          useDomOverlay.disabled = true;
+          fullscreenDomOverlay.disabled = true;
+        }
+      }
+
+      function onDomOverlayChanged() {
+        fullscreenDomOverlay.disabled = !useDomOverlay.checked;
+      }
+
       function initXR() {
         xrButton = new XRDeviceButton({
           onRequestSession: onRequestSession,
           onEndSession: onEndSession,
-          supportedSessionTypes: ['immersive-vr']
+          supportedSessionTypes: ['immersive-ar','immersive-vr'],
+          textEnterXRTitle: "START XR",
+          textXRNotFoundTitle: "XR NOT FOUND",
+          textExitXRTitle: "EXIT XR",
         });
         document.querySelector('header').appendChild(xrButton.domElement);
-
         if (navigator.xr) {
-          requestInlineSession()
-          .then((session) => {
-            gl.canvas.addEventListener('touchend', () => {
-              session.end().then(() => {
-                requestInlineSession({ optionalFeatures: ['local-floor'] });
-              }, { once: true });
-            })
-          });
+          if (startInlineSession) {
+            requestInlineSession()
+            .then((session) => {
+              gl.canvas.addEventListener('touchend', () => {
+                session.end().then(() => {
+                  requestInlineSession({ optionalFeatures: ['local-floor'] });
+                }, { once: true });
+              })
+            });
+          }
         } else {
           initFallback();
         }
@@ -217,8 +259,14 @@
           {
             icon: '../test-media/third-party/screen-share-button.png',
             callback: (success, fail) => {
-              navigator.mediaDevices.getDisplayMedia()
-              .then(device => {
+              let promise = null;
+              if (navigator.mediaDevices.getDisplayMedia == null) {
+                promise = navigator.mediaDevices.getCurrentBrowsingContextMedia({video:true});
+              } else {
+                promise = navigator.mediaDevices.getDisplayMedia();
+              }
+
+              promise.then(device => {
                   // Assign the device to a temp variable to keep it from
                   // getting garbage collected immediately.
                   window.getDisplayMediaRetVal = device;
@@ -230,9 +278,23 @@
       }
 
       function onRequestSession() {
-        navigator.xr.requestSession('immersive-vr', {requiredFeatures: ['local-floor']})
-        .then((session) => {
-          session.mode = 'immersive-vr';
+        let sessionType = startArSession.checked ? 'immersive-ar' : 'immersive-vr';
+
+        let options = {
+          requiredFeatures: ['local-floor'],
+          optionalFeatures: [],
+        };
+        if (useDomOverlay.checked) {
+          options.optionalFeatures.push('dom-overlay');
+          if (fullscreenDomOverlay.checked) {
+            options.domOverlay = { root: document.body };
+          } else {
+            options.domOverlay = { root: document.querySelector("#text-overlay") };
+          }
+        }
+
+        navigator.xr.requestSession(sessionType, options).then((session) => {
+          session.mode = sessionType;
           xrButton.setSession(session);
           onSessionStarted(session);
         });
@@ -248,6 +310,11 @@
 
       function onSessionStarted(session) {
         session.addEventListener('end', onSessionEnded);
+        if (session.mode == 'immersive-ar') {
+          document.querySelector("#text-info").innerHTML = "This is some in session text";
+          useDomOverlay.disabled = true;
+          fullscreenDomOverlay.disabled = true;
+        }
 
         session.addEventListener('select', (ev) => {
           let refSpace = ev.frame.session.mode.startsWith('immersive') ?
@@ -297,6 +364,9 @@
       function onSessionEnded(event) {
         if (event.session.mode.startsWith('immersive')) {
           xrButton.setSession(null);
+          useDomOverlay.disabled = false;
+          fullscreenDomOverlay.disabled = !useDomOverlay.checked;
+          document.querySelector("#text-info").innerHTML = "";
         }
       }
 
diff --git a/tools/clang/scripts/build.py b/tools/clang/scripts/build.py
index 59f37853..a279ffe 100755
--- a/tools/clang/scripts/build.py
+++ b/tools/clang/scripts/build.py
@@ -1055,7 +1055,7 @@
       if not os.path.exists(build_dir):
         os.mkdir(os.path.join(build_dir))
       os.chdir(build_dir)
-      target_spec = target_arch + '-fuchsia'
+      target_spec = target_arch + '-unknown-fuchsia'
       if args.build_mac_arm:
         # Just-built clang can't run (it's an arm binary on an intel host), so
         # use the bootstrap compiler instead.
@@ -1071,9 +1071,9 @@
         '-DCMAKE_AR=' + os.path.join(host_path, 'bin/llvm-ar'),
         '-DLLVM_CONFIG_PATH=' + os.path.join(host_path, 'bin/llvm-config'),
         '-DCMAKE_SYSTEM_NAME=Fuchsia',
-        '-DCMAKE_CXX_COMPILER_TARGET=%s-fuchsia' % target_arch,
-        '-DCMAKE_C_COMPILER_TARGET=%s-fuchsia' % target_arch,
-        '-DCMAKE_ASM_COMPILER_TARGET=%s-fuchsia' % target_arch,
+        '-DCMAKE_CXX_COMPILER_TARGET=' + target_spec,
+        '-DCMAKE_C_COMPILER_TARGET=' + target_spec,
+        '-DCMAKE_ASM_COMPILER_TARGET=' + target_spec,
         '-DCOMPILER_RT_BUILD_BUILTINS=ON',
         '-DCOMPILER_RT_BUILD_CRT=OFF',
         '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF',
diff --git a/tools/clang/scripts/package.py b/tools/clang/scripts/package.py
index 854a92a..186148d 100755
--- a/tools/clang/scripts/package.py
+++ b/tools/clang/scripts/package.py
@@ -239,16 +239,16 @@
       'bin/clang',
 
       # Include libclang_rt.builtins.a for Fuchsia targets.
-      'lib/clang/$V/lib/aarch64-fuchsia/libclang_rt.builtins.a',
-      'lib/clang/$V/lib/x86_64-fuchsia/libclang_rt.builtins.a',
+      'lib/clang/$V/lib/aarch64-unknown-fuchsia/libclang_rt.builtins.a',
+      'lib/clang/$V/lib/x86_64-unknown-fuchsia/libclang_rt.builtins.a',
     ])
     if not args.build_mac_arm:
       # TODO(thakis): Figure out why this doesn't build in --build-mac-arm
       # builds.
-      want.append('lib/clang/$V/lib/x86_64-fuchsia/libclang_rt.profile.a')
+      want.append('lib/clang/$V/lib/x86_64-unknown-fuchsia/libclang_rt.profile.a')
     if sys.platform != 'darwin':
       # The Fuchsia asan runtime is only built on non-Mac platforms.
-      want.append('lib/clang/$V/lib/x86_64-fuchsia/libclang_rt.asan.a')
+      want.append('lib/clang/$V/lib/x86_64-unknown-fuchsia/libclang_rt.asan.a')
   if sys.platform == 'darwin':
     want.extend([
       # AddressSanitizer runtime.
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 603d9d1..a4ae8fa3 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -40,7 +40,7 @@
 # Reverting problematic clang rolls is safe, though.
 # This is the output of `git describe` and is usable as a commit-ish.
 CLANG_REVISION = 'llvmorg-13-init-7296-ga749bd76'
-CLANG_SUB_REVISION = 2
+CLANG_SUB_REVISION = 3
 
 PACKAGE_VERSION = '%s-%s' % (CLANG_REVISION, CLANG_SUB_REVISION)
 RELEASE_VERSION = '13.0.0'
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index d283e89..f4931f8 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -44541,6 +44541,8 @@
   <int value="-1899409297"
       label="ProcessSharingWithStrictSiteInstances:disabled"/>
   <int value="-1899342084" label="ArcGhostWindow:enabled"/>
+  <int value="-1898963163"
+      label="OmniboxKeywordSpaceTriggeringSetting:disabled"/>
   <int value="-1898386671" label="PasswordCheck:enabled"/>
   <int value="-1896871201" label="CrossOriginOpenerPolicyReporting:enabled"/>
   <int value="-1896733769" label="CheckOfflineCapability:disabled"/>
@@ -46085,6 +46087,7 @@
   <int value="-600726805" label="DeprecateMenagerieAPI:disabled"/>
   <int value="-599932554" label="DoodlesOnLocalNtp:disabled"/>
   <int value="-599005750" label="ScanningUI:disabled"/>
+  <int value="-598191408" label="OmniboxKeywordSpaceTriggeringSetting:enabled"/>
   <int value="-598050737" label="disable-es3-apis"/>
   <int value="-596822155" label="AppManagementIntentSettings:enabled"/>
   <int value="-596337171" label="disable-ash-sidebar"/>
diff --git a/tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS b/tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS
index ad20aa4..3435c86 100644
--- a/tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS
+++ b/tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS
@@ -10,6 +10,7 @@
 ender@chromium.org
 harrisonsean@chromium.org
 javierrobles@chromium.org
+lizeb@chromium.org
 lyf@chromium.org
 mcrouse@chromium.org
 mlippautz@chromium.org
diff --git a/tools/metrics/histograms/histograms_xml/event/histograms.xml b/tools/metrics/histograms/histograms_xml/event/histograms.xml
index d1400f47..deb4dac 100644
--- a/tools/metrics/histograms/histograms_xml/event/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/event/histograms.xml
@@ -380,13 +380,14 @@
   </summary>
 </histogram>
 
-<histogram name="Event.Latency.OS" units="microseconds"
-    expires_after="2021-12-01">
+<histogram name="Event.Latency.OS" units="microseconds" expires_after="M92">
   <owner>flackr@chromium.org</owner>
   <owner>input-dev@chromium.org</owner>
   <summary>
     Time between input event received by OS and sent to Chrome, recorded for
-    each event when it is received by Chrome.
+    each event when it is received by Chrome. This is obsoleted by
+    Event.Latency.OS2 and should be removed after they've been run side-by-side
+    for M92.
 
     Warning: This metric may include reports from clients with low-resolution
     clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
@@ -396,6 +397,21 @@
   </summary>
 </histogram>
 
+<histogram name="Event.Latency.OS2" units="ms" expires_after="2021-12-01">
+  <owner>flackr@chromium.org</owner>
+  <owner>input-dev@chromium.org</owner>
+  <summary>
+    Time between input event received by OS and sent to Chrome, recorded for
+    each event when it is received by Chrome.
+
+    Warning: On Windows, the timestamps of most events come from a clock with a
+    several msec resolution so will have an abnormal distribution even when
+    TimeTicks::IsHighResolution returns true. The exception is touch events
+    which usually come from the high-resolution clock when
+    TimeTicks::IsHighResolution returns true.
+  </summary>
+</histogram>
+
 <histogram name="Event.Latency.OS_NO_VALIDATION.POSITIVE" units="ms"
     expires_after="2021-08-09">
   <owner>sullivan@chromium.org</owner>
@@ -404,7 +420,7 @@
     For Mac, a version of Event.Latency.OS that has the positive values it would
     contain if ValidateEventTimeClock() were not called on the timestamps.
 
-    This metric is intended to debug http://crbug.com/1039833, where very high
+    This metric is intended to debug http://crbug.com/1039833 where very high
     numbers are seen for PageLoad.InputDelay3. The units of this metric are the
     same as PageLoad.InputDelay3 for consistency while debugging.
   </summary>
@@ -412,6 +428,9 @@
 
 <histogram name="Event.Latency.OS_WIN.HIGH_RES" units="ms"
     expires_after="2021-12-01">
+  <obsolete>
+    Removed 2021-04-22 - replaced by Event.Latency.OS2.
+  </obsolete>
   <owner>joenotcharles@chromium.org</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
@@ -434,6 +453,9 @@
 
 <histogram name="Event.Latency.OS_WIN.LOW_RES" units="ms"
     expires_after="2021-12-01">
+  <obsolete>
+    Removed 2021-04-22 - replaced by Event.Latency.OS2.
+  </obsolete>
   <owner>joenotcharles@chromium.org</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
@@ -456,6 +478,9 @@
 
 <histogram name="Event.Latency.OS_WIN_IS_VALID" enum="Boolean"
     expires_after="2021-12-01">
+  <obsolete>
+    Removed 2021-04-22 - replaced by Event.Latency.OS2.
+  </obsolete>
   <owner>joenotcharles@chromium.org</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
index 37b7bdc6..b97f7fc 100644
--- a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
@@ -6333,11 +6333,24 @@
   <suffix name="TOUCH_PRESSED" label=""/>
   <suffix name="TOUCH_RELEASED" label=""/>
   <affected-histogram name="Event.Latency.OS"/>
+  <affected-histogram name="Event.Latency.OS2"/>
   <affected-histogram name="Event.Latency.OS_NO_VALIDATION.NEGATIVE"/>
   <affected-histogram name="Event.Latency.OS_NO_VALIDATION.POSITIVE"/>
-  <affected-histogram name="Event.Latency.OS_WIN.HIGH_RES"/>
-  <affected-histogram name="Event.Latency.OS_WIN.LOW_RES"/>
-  <affected-histogram name="Event.Latency.OS_WIN_IS_VALID"/>
+  <affected-histogram name="Event.Latency.OS_WIN.HIGH_RES">
+    <obsolete>
+      Removed 2021-04-22 - replaced by Event.Latency.OS2.
+    </obsolete>
+  </affected-histogram>
+  <affected-histogram name="Event.Latency.OS_WIN.LOW_RES">
+    <obsolete>
+      Removed 2021-04-22 - replaced by Event.Latency.OS2.
+    </obsolete>
+  </affected-histogram>
+  <affected-histogram name="Event.Latency.OS_WIN_IS_VALID">
+    <obsolete>
+      Removed 2021-04-22 - replaced by Event.Latency.OS2.
+    </obsolete>
+  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="EventLatencyPinchTypes" separator=".">
diff --git a/tools/metrics/histograms/histograms_xml/memory/OWNERS b/tools/metrics/histograms/histograms_xml/memory/OWNERS
index 0b0a06ca..e6f7fde8 100644
--- a/tools/metrics/histograms/histograms_xml/memory/OWNERS
+++ b/tools/metrics/histograms/histograms_xml/memory/OWNERS
@@ -2,4 +2,5 @@
 
 # Prefer sending CLs to the owners listed below.
 # Use chromium-metrics-reviews@google.com as a backup.
+lizeb@chromium.org
 sebmarchand@chromium.org
diff --git a/tools/metrics/histograms/histograms_xml/memory/histograms.xml b/tools/metrics/histograms/histograms_xml/memory/histograms.xml
index d6e445cf..2e9dfa6 100644
--- a/tools/metrics/histograms/histograms_xml/memory/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/memory/histograms.xml
@@ -2340,6 +2340,21 @@
   </summary>
 </histogram>
 
+<histogram name="Memory.PressureLevel2" enum="MemoryPressureLevel"
+    expires_after="2021-11-30">
+  <owner>sebmarchand@chromium.org</owner>
+  <owner>catan-team@chromium.org</owner>
+  <summary>
+    The time spent in each memory pressure state, in seconds. Recorded when the
+    memory pressure state changes, at shutdown, or when it hasn't been recorded
+    in the last 5 minutes.
+
+    The values from this histogram shouldn't be used directly, users of this
+    metric should look at the percentage of samples in each bucket to understand
+    the average time that Chrome spend in each pressure state.
+  </summary>
+</histogram>
+
 <histogram name="Memory.PressureLevelChanges" enum="MemoryPressureLevelChanges"
     expires_after="M85">
   <owner>chrisha@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index 61ead914..9bd4a1e 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -1628,6 +1628,7 @@
     expires_after="2021-10-04">
   <owner>rhalavati@chromium.org</owner>
   <owner>chrome-privacy-core@google.com</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     This histogram records when a bookmark is added sliced on profile type.
 
@@ -1640,6 +1641,7 @@
     expires_after="2021-10-10">
   <owner>rhalavati@chromium.org</owner>
   <owner>chrome-privacy-core@google.com</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     This histogram records the count of tabs when a user bookmarks all open
     tabs, in incognito mode.
@@ -1650,6 +1652,7 @@
     expires_after="2021-10-04">
   <owner>rhalavati@chromium.org</owner>
   <owner>chrome-privacy-core@google.com</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     This histogram records the count of tabs when a user bookmarks all open
     tabs, in regular mode.
@@ -1660,6 +1663,7 @@
     enum="BookmarkBarPrefAndState" expires_after="2021-10-17">
   <owner>corising@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     This records the Boomarks bar pref state and status when the reading list is
     opened. This will help determine how often the reading list is accessed from
@@ -1671,6 +1675,7 @@
 <histogram name="Bookmarks.BookmarksInFolder" units="bookmarks"
     expires_after="M85">
   <owner>calamity@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     Logs the number of bookmark items inside a folder every time a folder is
     opened inside the bookmark manager.
@@ -1681,6 +1686,7 @@
     expires_after="M91">
   <owner>shaktisahu@chromium.org</owner>
   <owner>xingliu@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     Logs the destination directory chosen by the user when saving a bookmark via
     bottom sheet on Android.
@@ -1693,6 +1699,7 @@
   <owner>isherman@chromium.org</owner>
   <owner>aidanday@google.com</owner>
   <owner>mamir@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     The total number of bookmarks a user has saved, excluding folders. Recorded
     when a profile is opened - precisely, when bookmarks are loaded into storage
@@ -1709,6 +1716,7 @@
   </obsolete>
   <owner>mamir@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     The number of bookmarks a user has saved with a URL that is also present in
     at least one other bookmark. This excludes folders (which don't have a URL).
@@ -1721,6 +1729,7 @@
     units="bookmarks" expires_after="2021-10-10">
   <owner>mastiz@chromium.org</owner>
   <owner>sky@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     The number of bookmarks a user has saved with a {UniquenessCriterion} that
     is also present in at least one other bookmark. This excludes not only
@@ -1740,6 +1749,7 @@
     units="bookmarks" expires_after="2021-10-10">
   <owner>mastiz@chromium.org</owner>
   <owner>sky@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     The number of unique {UniquenessCriterion}s among bookmarks saved by the
     user. Recorded when bookmarks are loaded into storage from disk.
@@ -1755,6 +1765,7 @@
     expires_after="M82">
   <owner>twellington@google.com</owner>
   <owner>clank-app-team@google.com</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     Records the number of bookmarks which are opened using the &quot;Open in
     incognito tab&quot; button from selection mode in the Android bookmark
@@ -1766,6 +1777,7 @@
     expires_after="M82">
   <owner>twellington@google.com</owner>
   <owner>clank-app-team@google.com</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     Records the number of bookmarks which are opened using the &quot;Open in new
     tab&quot; button from selection mode in the Android bookmark manager.
@@ -1779,6 +1791,7 @@
   </obsolete>
   <owner>mastiz@chromium.org</owner>
   <owner>sky@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     Time taken to build the bookmark index. Logged upon profile load when the
     bookmarks JSON file is read.
@@ -1791,6 +1804,7 @@
   </obsolete>
   <owner>mastiz@chromium.org</owner>
   <owner>sky@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     Time taken to decode the bookmarks JSON file. Logged upon profile load when
     the bookmarks JSON file is read.
@@ -1804,6 +1818,7 @@
   </obsolete>
   <owner>mamir@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     Time to compute values to be logged for UMA metrics
     Bookmarks.Count.OnProfileLoad.*.
@@ -1813,18 +1828,21 @@
 <histogram name="Bookmarks.EntryPoint" enum="BookmarksEntryPoint"
     expires_after="2021-08-29">
   <owner>ianwen@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>How users add a new bookmark.</summary>
 </histogram>
 
 <histogram name="Bookmarks.LaunchLocation" enum="BookmarkLaunchLocation"
     expires_after="2021-10-25">
   <owner>ianwen@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>Logs a UI location from which a bookmark is launched.</summary>
 </histogram>
 
 <histogram name="Bookmarks.OnFaviconsChangedIconURL" enum="BooleanHit"
     expires_after="M77">
   <owner>pkotwicz@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     Logs how frequently BookmarkModel::OnFaviconsChanged() is called with a
     non-empty |icon_url| to determine whether BookmarkNodes should be cached
@@ -1839,6 +1857,7 @@
     enum="BrowserProfileType" expires_after="2022-03-01">
   <owner>roagarwal@chromium.org</owner>
   <owner>chrome-incognito@google.com</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     This records the number of times the bookmark manager is opened from regular
     or Incognito tab. This histogram only records the count from
@@ -1854,6 +1873,7 @@
   </obsolete>
   <owner>dtrainor@chromium.org</owner>
   <owner>xingliu@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     Logs the duration in milliseconds between bookmark creation time and
     bookmark open time based on device local time. Recorded when {BookmarkType}
@@ -1871,6 +1891,7 @@
     expires_after="2022-02-15">
   <owner>dtrainor@chromium.org</owner>
   <owner>xingliu@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     A new version of Bookmarks.OpenBookmarkTimeInterval.{BookmarkType}, to rule
     out corrupted data introduced by a timestamp bug.
@@ -1885,6 +1906,7 @@
 <histogram name="Bookmarks.OpenBookmarkType" enum="BookmarkType"
     expires_after="2021-10-04">
   <owner>wychen@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     Logs whether the bookmark entry is a user bookmark or a partner bookmark
     when it is opened.
@@ -1895,6 +1917,8 @@
     expires_after="2021-10-04">
   <owner>shaktisahu@chromium.org</owner>
   <owner>xingliu@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
+  <component>UI&gt;Browser&gt;Mobile&gt;ReadingList</component>
   <summary>
     Logs the total number of reading list items shown in the reading list on the
     Android Bookmarks page when it is opened.
@@ -1905,6 +1929,8 @@
     expires_after="2021-09-19">
   <owner>shaktisahu@chromium.org</owner>
   <owner>xingliu@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
+  <component>UI&gt;Browser&gt;Mobile&gt;ReadingList</component>
   <summary>
     Logs the number of read items shown in the reading list on the Android
     Bookmarks page when it is opened.
@@ -1915,6 +1941,8 @@
     expires_after="2021-10-10">
   <owner>shaktisahu@chromium.org</owner>
   <owner>xingliu@chromium.org</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
+  <component>UI&gt;Browser&gt;Mobile&gt;ReadingList</component>
   <summary>
     Logs the number of unread items shown in the reading list on the Android
     Bookmarks page when it is opened.
@@ -1925,6 +1953,7 @@
     enum="StarEntryPointAction" expires_after="2021-10-10">
   <owner>corising@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     Recorded when an action in the Bookmark icon menu is clicked.
   </summary>
@@ -1934,6 +1963,7 @@
     expires_after="2021-10-04">
   <owner>rhalavati@chromium.org</owner>
   <owner>chrome-privacy-core@google.com</owner>
+  <component>UI&gt;Browser&gt;Bookmarks</component>
   <summary>
     The number of times bookmarks are used in each profile type. Recorded when a
     bookmark is clicked.
diff --git a/tools/metrics/histograms/histograms_xml/password/histograms.xml b/tools/metrics/histograms/histograms_xml/password/histograms.xml
index 9c5ec76..9354f5f 100644
--- a/tools/metrics/histograms/histograms_xml/password/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/password/histograms.xml
@@ -970,8 +970,7 @@
   </summary>
 </histogram>
 
-<histogram
-    name="PasswordManager.CredentialEntryViewActions.{CredentialEntryType}"
+<histogram name="PasswordManager.CredentialEntryActions.{CredentialEntryType}"
     enum="CredentialEntryAction" expires_after="M97">
   <owner>ioanap@chromium.org</owner>
   <owner>fhorschig@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/v8/histograms.xml b/tools/metrics/histograms/histograms_xml/v8/histograms.xml
index e4e47ff..fe2397c1 100644
--- a/tools/metrics/histograms/histograms_xml/v8/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/v8/histograms.xml
@@ -1186,6 +1186,21 @@
   </summary>
 </histogram>
 
+<histogram name="V8.WasmMemoryProtectionKeysSupport" units="BooleanSupported"
+    expires_after="2021-10-04">
+  <owner>clemensb@chromium.org</owner>
+  <owner>jkummerow@chromium.org</owner>
+  <owner>ecmziegler@chromium.org</owner>
+  <summary>
+    Whether the system supports memory protection keys in userspace (MPK / PKU /
+    PKEY). This is determined once per process and recorded once per isolate
+    during isolate creation (startup).
+
+    As we use a Linux-specific API for PKUs, this value is only recorded on
+    64-bit Linux machines.
+  </summary>
+</histogram>
+
 <histogram name="V8.WasmMinMemPagesCount" units="pages"
     expires_after="2022-02-15">
   <owner>ecmziegler@chromium.org</owner>
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 291d108..950fbc7 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -1459,6 +1459,10 @@
     ]
   }
 
+  # TODO(crbug.com/1104244) Enable Result DB on all platforms when verified.
+  if builder_name == 'linux-perf-fyi':
+    result['resultdb'] = {'enable': True}
+
   # For now we either get shards from the number of devices specified
   # or a test entry needs to specify the num shards if it supports
   # soft device affinity.
diff --git a/tools/perf/page_sets/rendering/motionmark.py b/tools/perf/page_sets/rendering/motionmark.py
index e6364be2..c3108dc 100644
--- a/tools/perf/page_sets/rendering/motionmark.py
+++ b/tools/perf/page_sets/rendering/motionmark.py
@@ -242,6 +242,11 @@
             '&time-measurement=performance') % (suite_name, test_name)
 
 
+class MotionMarkRampMultiply(MotionMarkRampPage):
+  BASE_NAME = 'motionmark_ramp_multiply'
+  URL = MotionMarkRampPage.GetRampUrl('MotionMark', 'Multiply')
+
+
 class MotionMarkRampCanvasArcs(MotionMarkRampPage):
   BASE_NAME = 'motionmark_ramp_canvas_arcs'
   URL = MotionMarkRampPage.GetRampUrl('MotionMark', 'Canvas Arcs')
diff --git a/ui/accessibility/ax_event_generator.cc b/ui/accessibility/ax_event_generator.cc
index ebc55f7..6290c39 100644
--- a/ui/accessibility/ax_event_generator.cc
+++ b/ui/accessibility/ax_event_generator.cc
@@ -342,6 +342,10 @@
       AddEvent(node, Event::IGNORED_CHANGED);
       if (!new_value)
         AddEvent(node, Event::SUBTREE_CREATED);
+      if (node->data().role == ax::mojom::Role::kMenu) {
+        new_value ? AddEvent(node, Event::MENU_POPUP_END)
+                  : AddEvent(node, Event::MENU_POPUP_START);
+      }
       break;
     }
     case ax::mojom::State::kMultiline:
@@ -1195,6 +1199,10 @@
       return "loadStart";
     case AXEventGenerator::Event::MENU_ITEM_SELECTED:
       return "menuItemSelected";
+    case ui::AXEventGenerator::Event::MENU_POPUP_END:
+      return "menuPopupEnd";
+    case ui::AXEventGenerator::Event::MENU_POPUP_START:
+      return "menuPopupStart";
     case AXEventGenerator::Event::MULTILINE_STATE_CHANGED:
       return "multilineStateChanged";
     case AXEventGenerator::Event::MULTISELECTABLE_STATE_CHANGED:
diff --git a/ui/accessibility/ax_event_generator.h b/ui/accessibility/ax_event_generator.h
index 96dbf98..71d124e1 100644
--- a/ui/accessibility/ax_event_generator.h
+++ b/ui/accessibility/ax_event_generator.h
@@ -82,6 +82,8 @@
     LOAD_COMPLETE,
     LOAD_START,
     MENU_ITEM_SELECTED,
+    MENU_POPUP_END,
+    MENU_POPUP_START,
     MULTILINE_STATE_CHANGED,
     MULTISELECTABLE_STATE_CHANGED,
     NAME_CHANGED,
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index db3ae04a..ce33424 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -3444,6 +3444,14 @@
   atk_object_notify_state_change(obj, ATK_STATE_EXPANDED, is_expanded);
 }
 
+void AXPlatformNodeAuraLinux::OnShowingStateChanged(bool is_showing) {
+  AtkObject* obj = GetOrCreateAtkObject();
+  if (!obj)
+    return;
+
+  atk_object_notify_state_change(obj, ATK_STATE_SHOWING, is_showing);
+}
+
 void AXPlatformNodeAuraLinux::OnMenuPopupStart() {
   AtkObject* atk_object = GetOrCreateAtkObject();
   AtkObject* parent_frame = FindAtkObjectParentFrame(atk_object);
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.h b/ui/accessibility/platform/ax_platform_node_auralinux.h
index d98c27a..350cb32f 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.h
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.h
@@ -203,6 +203,7 @@
   void OnCheckedStateChanged();
   void OnEnabledChanged();
   void OnExpandedStateChanged(bool is_expanded);
+  void OnShowingStateChanged(bool is_showing);
   void OnFocused();
   void OnWindowActivated();
   void OnWindowDeactivated();
diff --git a/ui/accessibility/platform/ax_platform_node_mac.mm b/ui/accessibility/platform/ax_platform_node_mac.mm
index 003c3920..74b36fa 100644
--- a/ui/accessibility/platform/ax_platform_node_mac.mm
+++ b/ui/accessibility/platform/ax_platform_node_mac.mm
@@ -283,10 +283,10 @@
        NSAccessibilityFocusedUIElementChangedNotification},
       {ax::mojom::Event::kFocusContext,
        NSAccessibilityFocusedUIElementChangedNotification},
-      {ax::mojom::Event::kMenuStart, (id)kAXMenuOpenedNotification},
-      {ax::mojom::Event::kMenuEnd, (id)kAXMenuClosedNotification},
-      {ax::mojom::Event::kMenuPopupStart, (id)kAXMenuOpenedNotification},
-      {ax::mojom::Event::kMenuPopupEnd, (id)kAXMenuClosedNotification},
+      {ax::mojom::Event::kMenuStart, (NSString*)kAXMenuOpenedNotification},
+      {ax::mojom::Event::kMenuEnd, (NSString*)kAXMenuClosedNotification},
+      {ax::mojom::Event::kMenuPopupStart, (NSString*)kAXMenuOpenedNotification},
+      {ax::mojom::Event::kMenuPopupEnd, (NSString*)kAXMenuClosedNotification},
       {ax::mojom::Event::kTextChanged, NSAccessibilityTitleChangedNotification},
       {ax::mojom::Event::kValueChanged,
        NSAccessibilityValueChangedNotification},
diff --git a/ui/events/cocoa/events_mac.mm b/ui/events/cocoa/events_mac.mm
index 6f098352..b716ef8 100644
--- a/ui/events/cocoa/events_mac.mm
+++ b/ui/events/cocoa/events_mac.mm
@@ -84,6 +84,11 @@
   return timestamp;
 }
 
+base::TimeTicks EventLatencyTimeFromNative(const PlatformEvent& native_event,
+                                           base::TimeTicks current_time) {
+  return EventTimeFromNative(native_event);
+}
+
 gfx::PointF EventLocationFromNative(const PlatformEvent& native_event) {
   NSWindow* window = [native_event window];
   NSPoint location = [native_event locationInWindow];
diff --git a/ui/events/event_unittest.cc b/ui/events/event_unittest.cc
index e651e1d..0aede5b9 100644
--- a/ui/events/event_unittest.cc
+++ b/ui/events/event_unittest.cc
@@ -29,6 +29,10 @@
 #include "ui/events/test/test_event_target.h"
 #include "ui/gfx/transform.h"
 
+#if defined(OS_WIN)
+#include "ui/events/win/events_win_utils.h"
+#endif
+
 namespace ui {
 
 TEST(EventTest, NoNativeEvent) {
@@ -652,19 +656,14 @@
   EXPECT_FALSE(mouseev3.flags() & EF_UNADJUSTED_MOUSE);
 }
 
-// Checks that Event.Latency.OS.MOUSE_WHEEL histogram is computed properly.
+// Checks that Event.Latency.OS2.MOUSE_WHEEL histogram is computed properly.
 TEST(EventTest, EventLatencyOSMouseWheelHistogram) {
 #if defined(OS_WIN)
   base::HistogramTester histogram_tester;
   MSG event = {nullptr, WM_MOUSEWHEEL, 0, 0};
   MouseWheelEvent mouseWheelEvent(event);
   histogram_tester.ExpectTotalCount("Event.Latency.OS.MOUSE_WHEEL", 1);
-  histogram_tester.ExpectTotalCount("Event.Latency.OS_WIN.HIGH_RES.MOUSE_WHEEL",
-                                    0);
-  histogram_tester.ExpectTotalCount("Event.Latency.OS_WIN.LOW_RES.MOUSE_WHEEL",
-                                    0);
-  histogram_tester.ExpectTotalCount("Event.Latency.OS_WIN_IS_VALID.MOUSE_WHEEL",
-                                    0);
+  histogram_tester.ExpectTotalCount("Event.Latency.OS2.MOUSE_WHEEL", 1);
 #endif
 }
 
@@ -841,104 +840,13 @@
     ::testing::Combine(::testing::Values(WM_CHAR),
                        ::testing::ValuesIn(kAltGraphEventTestCases)));
 
-// Tests for ComputeEventLatencyOSWin
+// Tests for ComputeEventLatencyOS variants.
 
-constexpr struct EventLatencyTickCountTestCase {
-  EventType event_type;
-  const char* histogram_suffix;
-} kEventLatencyTickCountTestCases[] = {
-    {
-        ET_KEY_PRESSED,
-        "KEY_PRESSED",
-    },
-    {
-        ET_MOUSE_PRESSED,
-        "MOUSE_PRESSED",
-    },
-    {
-        ET_TOUCH_PRESSED,
-        "TOUCH_PRESSED",
-    },
-};
-
-class EventLatencyTestBase : public ::testing::Test {
- protected:
-  static constexpr char kHighResHistogram[] = "Event.Latency.OS_WIN.HIGH_RES";
-  static constexpr char kLowResHistogram[] = "Event.Latency.OS_WIN.LOW_RES";
-  static constexpr char kIsValidHistogram[] = "Event.Latency.OS_WIN_IS_VALID";
-
-  std::string HighResEventHistogram(base::StringPiece suffix) const {
-    return base::StrCat({kHighResHistogram, ".", suffix});
-  }
-
-  std::string LowResEventHistogram(base::StringPiece suffix) const {
-    return base::StrCat({kLowResHistogram, ".", suffix});
-  }
-
-  std::string IsValidEventHistogram(base::StringPiece suffix) const {
-    return base::StrCat({kIsValidHistogram, ".", suffix});
-  }
-
-  // Tests for all expected histograms for an event that has a valid timestamp.
-  void ExpectValidHistograms(const base::HistogramTester& histogram_tester,
-                             base::TimeDelta delta,
-                             base::StringPiece suffix) {
-    // Expect both general and per-event histograms to be set.
-    histogram_tester.ExpectUniqueSample(kIsValidHistogram, true, 1);
-    histogram_tester.ExpectUniqueSample(IsValidEventHistogram(suffix), true, 1);
-    if (base::TimeTicks::IsHighResolution()) {
-      histogram_tester.ExpectUniqueTimeSample(kHighResHistogram, delta, 1);
-      histogram_tester.ExpectUniqueTimeSample(HighResEventHistogram(suffix),
-                                              delta, 1);
-      histogram_tester.ExpectTotalCount(kLowResHistogram, 0);
-      histogram_tester.ExpectTotalCount(LowResEventHistogram(suffix), 0);
-    } else {
-      histogram_tester.ExpectUniqueTimeSample(kLowResHistogram, delta, 1);
-      histogram_tester.ExpectUniqueTimeSample(LowResEventHistogram(suffix),
-                                              delta, 1);
-      histogram_tester.ExpectTotalCount(kHighResHistogram, 0);
-      histogram_tester.ExpectTotalCount(HighResEventHistogram(suffix), 0);
-    }
-  }
-
-  // Tests for all expected histograms for an event that has an invalid
-  // timestamp.
-  void ExpectInvalidHistograms(const base::HistogramTester& histogram_tester,
-                               base::StringPiece suffix) {
-    histogram_tester.ExpectUniqueSample(kIsValidHistogram, false, 1);
-    histogram_tester.ExpectUniqueSample(IsValidEventHistogram(suffix), false,
-                                        1);
-    histogram_tester.ExpectTotalCount(kHighResHistogram, 0);
-    histogram_tester.ExpectTotalCount(HighResEventHistogram(suffix), 0);
-    histogram_tester.ExpectTotalCount(kLowResHistogram, 0);
-    histogram_tester.ExpectTotalCount(LowResEventHistogram(suffix), 0);
-  }
-
-  // Tests that no histograms were recorded for the given event. (For example
-  // if it has a type that should be excluded from the metric.)
-  void ExpectNoHistograms(const base::HistogramTester& histogram_tester,
-                          base::StringPiece suffix) {
-    histogram_tester.ExpectTotalCount(kIsValidHistogram, 0);
-    histogram_tester.ExpectTotalCount(IsValidEventHistogram(suffix), 0);
-    histogram_tester.ExpectTotalCount(kHighResHistogram, 0);
-    histogram_tester.ExpectTotalCount(HighResEventHistogram(suffix), 0);
-    histogram_tester.ExpectTotalCount(kLowResHistogram, 0);
-    histogram_tester.ExpectTotalCount(LowResEventHistogram(suffix), 0);
-  }
-
-  base::test::TaskEnvironment task_environment_{
-      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
-};
-
-class EventLatencyTickCountTest
-    : public EventLatencyTestBase,
-      public ::testing::WithParamInterface<EventLatencyTickCountTestCase> {
+class EventLatencyTest : public ::testing::Test {
  public:
-  EventLatencyTickCountTest() {
-    SetEventLatencyTickClockForTesting(&tick_clock_);
-  }
+  EventLatencyTest() { SetEventLatencyTickClockForTesting(&tick_clock_); }
 
-  ~EventLatencyTickCountTest() { SetEventLatencyTickClockForTesting(nullptr); }
+  ~EventLatencyTest() override { SetEventLatencyTickClockForTesting(nullptr); }
 
  protected:
   void UpdateTickClock(DWORD timestamp) {
@@ -946,17 +854,25 @@
                             base::TimeDelta::FromMilliseconds(timestamp));
   }
 
-  // The inherited |task_environment_| mocks the base::TimeTicks clock while
-  // |tick_clock_| mocks ::GetTickCount.
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+
+  // |task_environment_| mocks the base::TimeTicks clock while |tick_clock_|
+  // mocks ::GetTickCount.
   base::SimpleTestTickClock tick_clock_;
 };
 
-TEST_P(EventLatencyTickCountTest, ComputeEventLatencyOSWinFromTickCount) {
-  // Create an event whose timestamp is very close to the max range of
+TEST_F(EventLatencyTest, ComputeEventLatencyOSFromTickCount) {
+  // Create events whose timestamps are very close to the max range of
   // ::GetTickCount.
   constexpr DWORD timestamp_msec = std::numeric_limits<DWORD>::max() - 10;
-
-  const std::string suffix = GetParam().histogram_suffix;
+  constexpr TOUCHINPUT touch_input = {
+      .dwTime = timestamp_msec,
+  };
+  constexpr POINTER_INFO pointer_info = {
+      .dwTime = timestamp_msec,
+      .PerformanceCount = 0UL,
+  };
 
   // This test will create several events with the same timestamp, and change
   // the mocked result of ::GetTickCount for each measurement. This makes it
@@ -967,10 +883,16 @@
   UpdateTickClock(timestamp_msec + 5);
   {
     base::HistogramTester histogram_tester;
-    ComputeEventLatencyOSWinFromTickCount(GetParam().event_type, timestamp_msec,
+    ComputeEventLatencyOSFromTOUCHINPUT(ET_TOUCH_PRESSED, touch_input,
+                                        base::TimeTicks::Now());
+    ComputeEventLatencyOSFromPOINTER_INFO(ET_TOUCH_PRESSED, pointer_info,
                                           base::TimeTicks::Now());
-    ExpectValidHistograms(histogram_tester,
-                          base::TimeDelta::FromMilliseconds(5), suffix);
+    histogram_tester.ExpectUniqueSample(
+        "Event.Latency.OS.TOUCH_PRESSED",
+        base::TimeDelta::FromMilliseconds(5).InMicroseconds(), 2);
+    histogram_tester.ExpectUniqueTimeSample(
+        "Event.Latency.OS2.TOUCH_PRESSED", base::TimeDelta::FromMilliseconds(5),
+        2);
   }
 
   // Simulate ::GetTickCount advancing 15 msec, which wraps around past 0.
@@ -980,30 +902,34 @@
   UpdateTickClock(wrapped_timestamp_msec);
   {
     base::HistogramTester histogram_tester;
-    ComputeEventLatencyOSWinFromTickCount(GetParam().event_type, timestamp_msec,
+    ComputeEventLatencyOSFromTOUCHINPUT(ET_TOUCH_PRESSED, touch_input,
+                                        base::TimeTicks::Now());
+    ComputeEventLatencyOSFromPOINTER_INFO(ET_TOUCH_PRESSED, pointer_info,
                                           base::TimeTicks::Now());
-    ExpectValidHistograms(histogram_tester,
-                          base::TimeDelta::FromMilliseconds(15), suffix);
+    histogram_tester.ExpectUniqueSample(
+        "Event.Latency.OS.TOUCH_PRESSED",
+        base::TimeDelta::FromMilliseconds(15).InMicroseconds(), 2);
+    histogram_tester.ExpectUniqueTimeSample(
+        "Event.Latency.OS2.TOUCH_PRESSED",
+        base::TimeDelta::FromMilliseconds(15), 2);
   }
 
-  // Simulate an event with a bogus timestamp.
+  // Simulate an event with a bogus timestamp. The delta should be recorded as
+  // 0.
   UpdateTickClock(timestamp_msec - 1000);
   {
     base::HistogramTester histogram_tester;
-    ComputeEventLatencyOSWinFromTickCount(GetParam().event_type, timestamp_msec,
+    ComputeEventLatencyOSFromTOUCHINPUT(ET_TOUCH_PRESSED, touch_input,
+                                        base::TimeTicks::Now());
+    ComputeEventLatencyOSFromPOINTER_INFO(ET_TOUCH_PRESSED, pointer_info,
                                           base::TimeTicks::Now());
-    ExpectInvalidHistograms(histogram_tester, suffix);
+    histogram_tester.ExpectUniqueSample("Event.Latency.OS.TOUCH_PRESSED", 0, 2);
+    histogram_tester.ExpectUniqueTimeSample("Event.Latency.OS2.TOUCH_PRESSED",
+                                            base::TimeDelta(), 2);
   }
 }
 
-INSTANTIATE_TEST_SUITE_P(All,
-                         EventLatencyTickCountTest,
-                         ::testing::ValuesIn(kEventLatencyTickCountTestCases));
-
-using EventLatencyPerformanceCounterTest = EventLatencyTestBase;
-
-TEST_F(EventLatencyPerformanceCounterTest,
-       ComputeEventLatencyOSWinFromPerformanceCounter) {
+TEST_F(EventLatencyTest, ComputeEventLatencyOSFromPerformanceCounter) {
   // Make sure there's enough time before Now() to create an event that's
   // several minutes old.
   task_environment_.AdvanceClock(base::TimeDelta::FromMinutes(5));
@@ -1025,39 +951,82 @@
 
   // Event created shortly before now.
   {
+    const POINTER_INFO pointer_info = {
+        .dwTime = 0U,
+        .PerformanceCount = current_timestamp - ticks_per_second,
+    };
     base::HistogramTester histogram_tester;
-    ComputeEventLatencyOSWinFromPerformanceCounter(
-        ET_TOUCH_PRESSED, current_timestamp - ticks_per_second,
-        base::TimeTicks::Now());
-    ExpectValidHistograms(histogram_tester, base::TimeDelta::FromSeconds(1),
-                          "TOUCH_PRESSED");
+    ComputeEventLatencyOSFromPOINTER_INFO(ET_TOUCH_PRESSED, pointer_info,
+                                          base::TimeTicks::Now());
+    histogram_tester.ExpectUniqueSample(
+        "Event.Latency.OS.TOUCH_PRESSED",
+        base::TimeDelta::FromSeconds(1).InMicroseconds(), 1);
+    histogram_tester.ExpectUniqueTimeSample("Event.Latency.OS2.TOUCH_PRESSED",
+                                            base::TimeDelta::FromSeconds(1), 1);
   }
 
   // Event created several minutes before now (IsValidTimebase should return
-  // false).
+  // false). The delta should be recorded as 0.
   {
+    const POINTER_INFO pointer_info = {
+        .dwTime = 0U,
+        .PerformanceCount = current_timestamp - 5 * 60 * ticks_per_second,
+    };
     base::HistogramTester histogram_tester;
-    ComputeEventLatencyOSWinFromPerformanceCounter(
-        ET_TOUCH_PRESSED, current_timestamp - 5 * 60 * ticks_per_second,
-        base::TimeTicks::Now());
-    ExpectInvalidHistograms(histogram_tester, "TOUCH_PRESSED");
+    ComputeEventLatencyOSFromPOINTER_INFO(ET_TOUCH_PRESSED, pointer_info,
+                                          base::TimeTicks::Now());
+    histogram_tester.ExpectUniqueSample("Event.Latency.OS.TOUCH_PRESSED", 0, 1);
+    histogram_tester.ExpectUniqueTimeSample("Event.Latency.OS2.TOUCH_PRESSED",
+                                            base::TimeDelta(), 1);
   }
 
-  // Event created in the future (IsValidTimebase should return false).
+  // Event created in the future (IsValidTimebase should return false). The
+  // delta should be recorded as 0.
   {
+    const POINTER_INFO pointer_info = {
+        .dwTime = 0U,
+        .PerformanceCount = current_timestamp + ticks_per_second,
+    };
     base::HistogramTester histogram_tester;
-    ComputeEventLatencyOSWinFromPerformanceCounter(
-        ET_TOUCH_PRESSED, current_timestamp + ticks_per_second,
-        base::TimeTicks::Now());
-    ExpectInvalidHistograms(histogram_tester, "TOUCH_PRESSED");
+    ComputeEventLatencyOSFromPOINTER_INFO(ET_TOUCH_PRESSED, pointer_info,
+                                          base::TimeTicks::Now());
+    histogram_tester.ExpectUniqueSample("Event.Latency.OS.TOUCH_PRESSED", 0, 1);
+    histogram_tester.ExpectUniqueTimeSample("Event.Latency.OS2.TOUCH_PRESSED",
+                                            base::TimeDelta(), 1);
   }
 
-  // Event that should not be recorded.
+  // Invalid event with no timestamp.
   {
+    const POINTER_INFO pointer_info = {
+        .dwTime = 0U,
+        .PerformanceCount = 0UL,
+    };
     base::HistogramTester histogram_tester;
-    ComputeEventLatencyOSWinFromPerformanceCounter(
-        ET_TOUCH_MOVED, current_timestamp - 10, base::TimeTicks::Now());
-    ExpectNoHistograms(histogram_tester, "TOUCH_MOVED");
+    ComputeEventLatencyOSFromPOINTER_INFO(ET_TOUCH_PRESSED, pointer_info,
+                                          base::TimeTicks::Now());
+    histogram_tester.ExpectTotalCount("Event.Latency.OS.TOUCH_PRESSED", 0);
+    histogram_tester.ExpectTotalCount("Event.Latency.OS2.TOUCH_PRESSED", 0);
+  }
+
+  // Invalid event with 2 timestamps should take the higher-precision one.
+  {
+    const DWORD now_msec = 1000;
+    UpdateTickClock(now_msec);
+
+    const POINTER_INFO pointer_info = {
+        // 10 milliseconds ago.
+        .dwTime = now_msec - 10,
+        // 1 second ago.
+        .PerformanceCount = current_timestamp - ticks_per_second,
+    };
+    base::HistogramTester histogram_tester;
+    ComputeEventLatencyOSFromPOINTER_INFO(ET_TOUCH_PRESSED, pointer_info,
+                                          base::TimeTicks::Now());
+    histogram_tester.ExpectUniqueSample(
+        "Event.Latency.OS.TOUCH_PRESSED",
+        base::TimeDelta::FromSeconds(1).InMicroseconds(), 1);
+    histogram_tester.ExpectUniqueTimeSample("Event.Latency.OS2.TOUCH_PRESSED",
+                                            base::TimeDelta::FromSeconds(1), 1);
   }
 }
 
diff --git a/ui/events/event_utils.cc b/ui/events/event_utils.cc
index 93045d6..98a61ac4 100644
--- a/ui/events/event_utils.cc
+++ b/ui/events/event_utils.cc
@@ -10,120 +10,25 @@
 
 #include "base/check.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/no_destructor.h"
 #include "base/notreached.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/time/tick_clock.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/events/base_event_utils.h"
 
+#if defined(OS_WIN)
+#include "ui/events/win/events_win_utils.h"
+#endif
+
 namespace ui {
 
 namespace {
 
 int g_custom_event_types = ET_LAST;
 
-#if defined(OS_WIN)
-
-#define UMA_HISTOGRAM_WIN_EVENT_LATENCY_TIMES(name, sample)        \
+#define UMA_HISTOGRAM_EVENT_LATENCY_TIMES(name, sample)            \
   UMA_HISTOGRAM_CUSTOM_TIMES(name, sample,                         \
                              base::TimeDelta::FromMilliseconds(1), \
-                             base::TimeDelta::FromMilliseconds(100000), 100)
-
-class GetTickCountClock : public base::TickClock {
- public:
-  GetTickCountClock() = default;
-  ~GetTickCountClock() override = default;
-
-  base::TimeTicks NowTicks() const override {
-    return base::TimeTicks() +
-           base::TimeDelta::FromMilliseconds(::GetTickCount());
-  }
-};
-
-const base::TickClock* g_tick_count_clock = nullptr;
-
-// Logs experimental Event.Latency.OS_WIN.* metrics to parallel
-// Event.Latency.OS.*.
-// TODO(crbug.com/1189656): Check that these are accurate enough to replace
-// Event.Latency.OS.*.
-void ComputeEventLatencyOSWin(ui::EventType event_type,
-                              base::TimeTicks event_time,
-                              base::TimeTicks current_time) {
-  // Check that the event doesn't come from a device giving bogus timestamps.
-  // On most platforms this is done inside EventTimeFromNative.
-  const bool is_valid = IsValidTimebase(current_time, event_time);
-  UMA_HISTOGRAM_BOOLEAN("Event.Latency.OS_WIN_IS_VALID", is_valid);
-  switch (event_type) {
-    case ET_KEY_PRESSED:
-      UMA_HISTOGRAM_BOOLEAN("Event.Latency.OS_WIN_IS_VALID.KEY_PRESSED",
-                            is_valid);
-      break;
-    case ET_MOUSE_PRESSED:
-      UMA_HISTOGRAM_BOOLEAN("Event.Latency.OS_WIN_IS_VALID.MOUSE_PRESSED",
-                            is_valid);
-      break;
-    case ET_TOUCH_PRESSED:
-      UMA_HISTOGRAM_BOOLEAN("Event.Latency.OS_WIN_IS_VALID.TOUCH_PRESSED",
-                            is_valid);
-      break;
-    default:
-      // Caller should have filtered out unhandled events.
-      NOTREACHED();
-      break;
-  }
-  if (!is_valid)
-    return;
-
-  const base::TimeDelta delta = current_time - event_time;
-
-  if (base::TimeTicks::IsHighResolution()) {
-    UMA_HISTOGRAM_WIN_EVENT_LATENCY_TIMES("Event.Latency.OS_WIN.HIGH_RES",
-                                          delta);
-    switch (event_type) {
-      case ET_KEY_PRESSED:
-        UMA_HISTOGRAM_WIN_EVENT_LATENCY_TIMES(
-            "Event.Latency.OS_WIN.HIGH_RES.KEY_PRESSED", delta);
-        break;
-      case ET_MOUSE_PRESSED:
-        UMA_HISTOGRAM_WIN_EVENT_LATENCY_TIMES(
-            "Event.Latency.OS_WIN.HIGH_RES.MOUSE_PRESSED", delta);
-        break;
-      case ET_TOUCH_PRESSED:
-        UMA_HISTOGRAM_WIN_EVENT_LATENCY_TIMES(
-            "Event.Latency.OS_WIN.HIGH_RES.TOUCH_PRESSED", delta);
-        break;
-      default:
-        // Caller should have filtered out unhandled events.
-        NOTREACHED();
-        break;
-    }
-  } else {
-    UMA_HISTOGRAM_WIN_EVENT_LATENCY_TIMES("Event.Latency.OS_WIN.LOW_RES",
-                                          delta);
-    switch (event_type) {
-      case ET_KEY_PRESSED:
-        UMA_HISTOGRAM_WIN_EVENT_LATENCY_TIMES(
-            "Event.Latency.OS_WIN.LOW_RES.KEY_PRESSED", delta);
-        break;
-      case ET_MOUSE_PRESSED:
-        UMA_HISTOGRAM_WIN_EVENT_LATENCY_TIMES(
-            "Event.Latency.OS_WIN.LOW_RES.MOUSE_PRESSED", delta);
-        break;
-      case ET_TOUCH_PRESSED:
-        UMA_HISTOGRAM_WIN_EVENT_LATENCY_TIMES(
-            "Event.Latency.OS_WIN.LOW_RES.TOUCH_PRESSED", delta);
-        break;
-      default:
-        // Caller should have filtered out unhandled events.
-        NOTREACHED();
-        break;
-    }
-  }
-}
-
-#endif  // OS_WIN
+                             base::TimeDelta::FromMinutes(1), 50)
 
 }  // namespace
 
@@ -190,86 +95,22 @@
   return display::Display::TouchSupport::UNAVAILABLE;
 }
 
-#if defined(OS_WIN)
-void SetEventLatencyTickClockForTesting(const base::TickClock* clock) {
-  g_tick_count_clock = clock;
-}
-
-void ComputeEventLatencyOSWinFromTickCount(ui::EventType event_type,
-                                           DWORD event_time,
-                                           base::TimeTicks current_time) {
-  static const base::NoDestructor<GetTickCountClock> default_tick_count_clock;
-  if (!g_tick_count_clock)
-    g_tick_count_clock = default_tick_count_clock.get();
-
-  if (event_type != ET_KEY_PRESSED && event_type != ET_MOUSE_PRESSED &&
-      event_type != ET_TOUCH_PRESSED) {
-    // Only log this metric for events that indicate strong user intent (eg.
-    // clicks and keypresses), not every move event. This will detect jank that
-    // causes the most user pain.
-    return;
-  }
-
-  base::TimeTicks current_tick_count = g_tick_count_clock->NowTicks();
-  base::TimeTicks time_stamp =
-      base::TimeTicks() + base::TimeDelta::FromMilliseconds(event_time);
-  // Check if the 32-bit tick count wrapped around after the event.
-  if (current_tick_count < time_stamp) {
-    // ::GetTickCount returns an unsigned 32-bit value, which will fit into the
-    // signed 64-bit base::TimeTicks.
-    current_tick_count +=
-        base::TimeDelta::FromMilliseconds(std::numeric_limits<DWORD>::max());
-  }
-
-  // |event_time| is from the GetTickCount clock, which has a different 0-point
-  // from |current_time| (which uses either the high-resolution timer or
-  // timeGetTime). Adjust it to be compatible.
-  //
-  // This offset will vary by up to ~16 msec because it depends on when exactly
-  // we sample the high-resolution clock. It would be more consistent to
-  // calculate one offset at the start of the program and apply it every time,
-  // but that consistency isn't needed for jank investigations and then we
-  // would have to adjust for clock drift.
-  const base::TimeDelta time_source_offset = current_time - current_tick_count;
-  time_stamp += time_source_offset;
-
-  ComputeEventLatencyOSWin(event_type, time_stamp, current_time);
-}
-
-void ComputeEventLatencyOSWinFromPerformanceCounter(
-    ui::EventType event_type,
-    UINT64 event_time,
-    base::TimeTicks current_time) {
-  if (event_type != ET_TOUCH_PRESSED) {
-    // Only log this metric for events that indicate strong user intent (ie.
-    // touch press), not every touch move event. This will detect jank that
-    // causes the most user pain.
-    return;
-  }
-  if (!base::TimeTicks::IsHighResolution()) {
-    // The tick clock will be incompatible with |event_time|.
-    return;
-  }
-  ComputeEventLatencyOSWin(
-      event_type, base::TimeTicks::FromQPCValue(event_time), current_time);
-}
-#endif
-
 void ComputeEventLatencyOS(const PlatformEvent& native_event) {
   base::TimeTicks current_time = EventTimeForNow();
-  base::TimeTicks time_stamp = EventTimeFromNative(native_event);
+  base::TimeTicks time_stamp =
+      EventLatencyTimeFromNative(native_event, current_time);
+  EventType type = EventTypeFromNative(native_event);
+  ComputeEventLatencyOS(type, time_stamp, current_time);
+}
+
+void ComputeEventLatencyOS(EventType type,
+                           base::TimeTicks time_stamp,
+                           base::TimeTicks current_time) {
   base::TimeDelta delta = current_time - time_stamp;
 
-  EventType type = EventTypeFromNative(native_event);
-#if defined(OS_WIN)
-  // Also record windows-only metrics.
-  //
-  // On Windows EventTimeFromNative returns the current time instead of the
-  // timestamp from |native_event|, which makes it useless for latency metrics,
-  // so retrieve the time stamp directly.
-  ComputeEventLatencyOSWinFromTickCount(type, native_event.time, current_time);
-#endif
-
+  // TODO(crbug.com/1189656): Remove the legacy Event.Latency.OS.* histograms
+  // after M92 (which introduced Event.Latency.OS2.*) has been replaced in the
+  // stable channel.
   switch (type) {
 #if defined(OS_APPLE)
     // On Mac, ET_SCROLL and ET_MOUSEWHEEL represent the same class of events.
@@ -279,42 +120,86 @@
       UMA_HISTOGRAM_CUSTOM_COUNTS(
           "Event.Latency.OS.MOUSE_WHEEL",
           base::saturated_cast<int>(delta.InMicroseconds()), 1, 1000000, 50);
+      UMA_HISTOGRAM_EVENT_LATENCY_TIMES("Event.Latency.OS2.MOUSE_WHEEL", delta);
       return;
     case ET_TOUCH_MOVED:
       UMA_HISTOGRAM_CUSTOM_COUNTS(
           "Event.Latency.OS.TOUCH_MOVED",
           base::saturated_cast<int>(delta.InMicroseconds()), 1, 1000000, 50);
+      UMA_HISTOGRAM_EVENT_LATENCY_TIMES("Event.Latency.OS2.TOUCH_MOVED", delta);
       return;
     case ET_TOUCH_PRESSED:
       UMA_HISTOGRAM_CUSTOM_COUNTS(
           "Event.Latency.OS.TOUCH_PRESSED",
           base::saturated_cast<int>(delta.InMicroseconds()), 1, 1000000, 50);
+      UMA_HISTOGRAM_EVENT_LATENCY_TIMES("Event.Latency.OS2.TOUCH_PRESSED",
+                                        delta);
       return;
     case ET_TOUCH_RELEASED:
       UMA_HISTOGRAM_CUSTOM_COUNTS(
           "Event.Latency.OS.TOUCH_RELEASED",
           base::saturated_cast<int>(delta.InMicroseconds()), 1, 1000000, 50);
+      UMA_HISTOGRAM_EVENT_LATENCY_TIMES("Event.Latency.OS2.TOUCH_RELEASED",
+                                        delta);
       return;
     case ET_TOUCH_CANCELLED:
       UMA_HISTOGRAM_CUSTOM_COUNTS(
           "Event.Latency.OS.TOUCH_CANCELLED",
           base::saturated_cast<int>(delta.InMicroseconds()), 1, 1000000, 50);
+      UMA_HISTOGRAM_EVENT_LATENCY_TIMES("Event.Latency.OS2.TOUCH_CANCELLED",
+                                        delta);
       return;
     case ET_KEY_PRESSED:
       UMA_HISTOGRAM_CUSTOM_COUNTS(
           "Event.Latency.OS.KEY_PRESSED",
           base::saturated_cast<int>(delta.InMicroseconds()), 1, 1000000, 50);
+      UMA_HISTOGRAM_EVENT_LATENCY_TIMES("Event.Latency.OS2.KEY_PRESSED", delta);
       return;
     case ET_MOUSE_PRESSED:
       UMA_HISTOGRAM_CUSTOM_COUNTS(
           "Event.Latency.OS.MOUSE_PRESSED",
           base::saturated_cast<int>(delta.InMicroseconds()), 1, 1000000, 50);
+      UMA_HISTOGRAM_EVENT_LATENCY_TIMES("Event.Latency.OS2.MOUSE_PRESSED",
+                                        delta);
       return;
     default:
       return;
   }
 }
 
+#if defined(OS_WIN)
+
+void ComputeEventLatencyOSFromTOUCHINPUT(EventType event_type,
+                                         TOUCHINPUT touch_input,
+                                         base::TimeTicks current_time) {
+  base::TimeTicks time_stamp =
+      EventLatencyTimeFromTickClock(touch_input.dwTime, current_time);
+  ComputeEventLatencyOS(event_type, time_stamp, current_time);
+}
+
+void ComputeEventLatencyOSFromPOINTER_INFO(EventType event_type,
+                                           POINTER_INFO pointer_info,
+                                           base::TimeTicks current_time) {
+  base::TimeTicks time_stamp;
+  if (pointer_info.PerformanceCount) {
+    if (!base::TimeTicks::IsHighResolution()) {
+      // The tick clock will be incompatible with |event_time|.
+      return;
+    }
+    time_stamp =
+        EventLatencyTimeFromPerformanceCounter(pointer_info.PerformanceCount);
+  } else if (pointer_info.dwTime) {
+    time_stamp =
+        EventLatencyTimeFromTickClock(pointer_info.dwTime, current_time);
+  } else {
+    // Bad POINTER_INFO with no timestamp.
+    return;
+  }
+  ComputeEventLatencyOS(event_type, time_stamp, current_time);
+}
+
+#endif  // defined(OS_WIN)
+
 void ConvertEventLocationToTargetWindowLocation(
     const gfx::Point& target_window_origin,
     const gfx::Point& current_window_origin,
diff --git a/ui/events/event_utils.h b/ui/events/event_utils.h
index e60e7d58..10311a01 100644
--- a/ui/events/event_utils.h
+++ b/ui/events/event_utils.h
@@ -33,7 +33,6 @@
 }  // namespace gfx
 
 namespace base {
-class TickClock;
 class TimeTicks;
 }
 
@@ -87,6 +86,15 @@
 EVENTS_EXPORT base::TimeTicks EventTimeFromNative(
     const PlatformEvent& native_event);
 
+// Get the timestamp to use for latency metrics from |native_event|.
+// |current_time| is a timestamp returned by EventTimeForNow which will be
+// compared to the |native_event| timestamp to calculate latency. This is
+// different from EventTimeFromNative because on some platforms (eg. Windows)
+// EventTimeFromNative returns a synthesized timestamp.
+EVENTS_EXPORT base::TimeTicks EventLatencyTimeFromNative(
+    const PlatformEvent& native_event,
+    base::TimeTicks current_time);
+
 // Get the location from a native event.  The coordinate system of the resultant
 // |Point| has the origin at top-left of the "root window".  The nature of
 // this "root window" and how it maps to platform-specific drawing surfaces is
@@ -169,25 +177,23 @@
 
 EVENTS_EXPORT void ComputeEventLatencyOS(const PlatformEvent& native_event);
 
-#if defined(OS_WIN)
-// Makes ComputeEventLatencyOSWinFromTickCount call the given |clock| to find
-// the current time ticks to compare to an MSG timestamp. If |clock| is nullptr,
-// it will call ::GetTickCount, which is the default.
-EVENTS_EXPORT void SetEventLatencyTickClockForTesting(
-    const base::TickClock* clock);
+EVENTS_EXPORT void ComputeEventLatencyOS(ui::EventType type,
+                                         base::TimeTicks time_stamp,
+                                         base::TimeTicks current_time);
 
-// Records Event.Latency.OS_WIN.* metrics for events whose timestamp comes from
-// ::GetTickCount (such as an MSG).
-EVENTS_EXPORT void ComputeEventLatencyOSWinFromTickCount(
+#if defined(OS_WIN)
+// Like ComputeEventLatencyOS, but for events whose timestamp comes from a
+// TOUCHINPUT structure instead of PlatformEvent.
+EVENTS_EXPORT void ComputeEventLatencyOSFromTOUCHINPUT(
     ui::EventType event_type,
-    DWORD event_time,
+    TOUCHINPUT touch_input,
     base::TimeTicks current_time);
 
-// Records Event.Latency.OS_WIN.* metrics for events whose timestamp comes from
-// a Performance Counter (such as POINTER_INFO).
-EVENTS_EXPORT void ComputeEventLatencyOSWinFromPerformanceCounter(
+// Like ComputeEventLatencyOS, but for events whose timestamp comes from a
+// POINTER_INFO structure instead of PlatformEvent.
+EVENTS_EXPORT void ComputeEventLatencyOSFromPOINTER_INFO(
     ui::EventType event_type,
-    UINT64 event_time,
+    POINTER_INFO pointer_info,
     base::TimeTicks current_time);
 
 EVENTS_EXPORT int GetModifiersFromKeyState();
diff --git a/ui/events/events_default.cc b/ui/events/events_default.cc
index e48fc5ca..aed4891 100644
--- a/ui/events/events_default.cc
+++ b/ui/events/events_default.cc
@@ -15,6 +15,11 @@
   return event->time_stamp();
 }
 
+base::TimeTicks EventLatencyTimeFromNative(const PlatformEvent& native_event,
+                                           base::TimeTicks current_time) {
+  return EventTimeFromNative(native_event);
+}
+
 int EventFlagsFromNative(const PlatformEvent& native_event) {
   const ui::Event* event = static_cast<const ui::Event*>(native_event);
   return event->flags();
diff --git a/ui/events/events_stub.cc b/ui/events/events_stub.cc
index 710c384..badd355 100644
--- a/ui/events/events_stub.cc
+++ b/ui/events/events_stub.cc
@@ -32,6 +32,11 @@
   return base::TimeTicks();
 }
 
+base::TimeTicks EventLatencyTimeFromNative(const PlatformEvent& native_event,
+                                           base::TimeTicks current_time) {
+  return EventTimeFromNative(native_event);
+}
+
 gfx::PointF EventLocationFromNative(const PlatformEvent& native_event) {
   NOTIMPLEMENTED();
   return gfx::PointF();
diff --git a/ui/events/win/events_win.cc b/ui/events/win/events_win.cc
index 18a361f..e65cb88 100644
--- a/ui/events/win/events_win.cc
+++ b/ui/events/win/events_win.cc
@@ -17,9 +17,19 @@
 }
 
 base::TimeTicks EventTimeFromNative(const MSG& native_event) {
+  // Note EventTimeFromMSG actually returns a time based on the current clock
+  // tick, ignoring MSG. See the comments in that function (which is in
+  // events_win_utils.cc) for the reason.
   return EventTimeFromMSG(native_event);
 }
 
+base::TimeTicks EventLatencyTimeFromNative(const MSG& native_event,
+                                           base::TimeTicks current_time) {
+  // For latency calculations use the real timestamp, rather than the one
+  // returned from EventTimeFromMSG.
+  return EventLatencyTimeFromTickClock(native_event.time, current_time);
+}
+
 gfx::PointF EventLocationFromNative(const MSG& native_event) {
   return gfx::PointF(EventLocationFromMSG(native_event));
 }
diff --git a/ui/events/win/events_win_utils.cc b/ui/events/win/events_win_utils.cc
index 655e113..ce5b901 100644
--- a/ui/events/win/events_win_utils.cc
+++ b/ui/events/win/events_win_utils.cc
@@ -5,9 +5,12 @@
 #include <stdint.h>
 
 #include "base/check.h"
+#include "base/no_destructor.h"
 #include "base/notreached.h"
+#include "base/time/tick_clock.h"
 #include "base/time/time.h"
 #include "base/win/windowsx_shim.h"
+#include "ui/events/base_event_utils.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
@@ -153,6 +156,19 @@
   return flags;
 }
 
+class GetTickCountClock : public base::TickClock {
+ public:
+  GetTickCountClock() = default;
+  ~GetTickCountClock() override = default;
+
+  base::TimeTicks NowTicks() const override {
+    return base::TimeTicks() +
+           base::TimeDelta::FromMilliseconds(::GetTickCount());
+  }
+};
+
+const base::TickClock* g_tick_count_clock = nullptr;
+
 }  // namespace
 
 EventType EventTypeFromMSG(const MSG& native_event) {
@@ -242,6 +258,47 @@
   return EventTimeForNow();
 }
 
+base::TimeTicks EventLatencyTimeFromTickClock(DWORD event_time,
+                                              base::TimeTicks current_time) {
+  static const base::NoDestructor<GetTickCountClock> default_tick_count_clock;
+  if (!g_tick_count_clock)
+    g_tick_count_clock = default_tick_count_clock.get();
+
+  base::TimeTicks time_stamp =
+      base::TimeTicks() + base::TimeDelta::FromMilliseconds(event_time);
+
+  base::TimeTicks current_tick_count = g_tick_count_clock->NowTicks();
+  // Check if the 32-bit tick count wrapped around after the event.
+  if (current_tick_count < time_stamp) {
+    // ::GetTickCount returns an unsigned 32-bit value, which will fit into the
+    // signed 64-bit base::TimeTicks.
+    current_tick_count +=
+        base::TimeDelta::FromMilliseconds(std::numeric_limits<DWORD>::max());
+  }
+
+  // |time_stamp| is from the GetTickCount clock, which has a different 0-point
+  // from |current_time| (which uses either the high-resolution timer or
+  // timeGetTime). Adjust it to be compatible.
+  //
+  // This offset will vary by up to ~16 msec because it depends on when exactly
+  // we sample the high-resolution clock. It would be more consistent to
+  // calculate one offset at the start of the program and apply it every time,
+  // but that consistency isn't needed for jank investigations and then we
+  // would have to adjust for clock drift.
+  const base::TimeDelta time_source_offset = current_time - current_tick_count;
+  time_stamp += time_source_offset;
+
+  ValidateEventTimeClock(&time_stamp);
+  return time_stamp;
+}
+
+base::TimeTicks EventLatencyTimeFromPerformanceCounter(UINT64 event_time) {
+  DCHECK(base::TimeTicks::IsHighResolution());
+  base::TimeTicks time_stamp = base::TimeTicks::FromQPCValue(event_time);
+  ValidateEventTimeClock(&time_stamp);
+  return time_stamp;
+}
+
 gfx::Point EventLocationFromMSG(const MSG& native_event) {
   // This code may use GetCursorPos() to get a mouse location. This may
   // fail in certain situations (see
@@ -493,4 +550,8 @@
                          changed_button_flags);
 }
 
+void SetEventLatencyTickClockForTesting(const base::TickClock* clock) {
+  g_tick_count_clock = clock;
+}
+
 }  // namespace ui
diff --git a/ui/events/win/events_win_utils.h b/ui/events/win/events_win_utils.h
index 8aaa07f..4fb1550 100644
--- a/ui/events/win/events_win_utils.h
+++ b/ui/events/win/events_win_utils.h
@@ -25,6 +25,7 @@
 }  // namespace gfx
 
 namespace base {
+class TickClock;
 class TimeTicks;
 }
 
@@ -44,6 +45,22 @@
 // same native event may return different values.
 EVENTS_EXPORT base::TimeTicks EventTimeFromMSG(const MSG& native_event);
 
+// Convert |event_time|, a count of milliseconds from the clock used by
+// ::GetTickCount(), to a value comparable to the base::TimeTicks clock.
+// |current_time| is a value returned in the recent past by EventTimeForNow,
+// which will be compared to the current result of ::GetTickCount() for
+// calibration.
+EVENTS_EXPORT base::TimeTicks EventLatencyTimeFromTickClock(
+    DWORD event_time,
+    base::TimeTicks current_time);
+
+// Convert |event_time|, a timestamp from the clock used by
+// ::QueryPerformanceCounter(), to a value comparable to the base::TimeTicks
+// clock. Must not be called if base::TimeTicks::IsHighResolution() returns
+// false.
+EVENTS_EXPORT base::TimeTicks EventLatencyTimeFromPerformanceCounter(
+    UINT64 event_time);
+
 // Get the location from a native event.  The coordinate system of the resultant
 // |Point| has the origin at top-left of the "root window".  The nature of
 // this "root window" and how it maps to platform-specific drawing surfaces is
@@ -101,6 +118,12 @@
 // scrolled.
 EVENTS_EXPORT bool GetScrollOffsetsFromMSG(const MSG& native_event);
 
+// Makes EventLatencyTimeFromTickClock call the given |clock| to find the
+// current time ticks. If |clock| is nullptr, it will call ::GetTickCount(),
+// which is the default.
+EVENTS_EXPORT void SetEventLatencyTickClockForTesting(
+    const base::TickClock* clock);
+
 }  // namespace ui
 
 #endif  // UI_EVENTS_WIN_EVENTS_WIN_UTILS_H_
diff --git a/ui/gl/DEPS b/ui/gl/DEPS
index ca1a801..a135d3b 100644
--- a/ui/gl/DEPS
+++ b/ui/gl/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+cc/base",
+  "+components/viz/common/resources/resource_format.h",
   "+mojo/public/cpp/bindings",
   "+third_party/khronos",
   "+third_party/libsync",
diff --git a/ui/gl/direct_composition_surface_win_unittest.cc b/ui/gl/direct_composition_surface_win_unittest.cc
index 3a0201ed..84a089f3 100644
--- a/ui/gl/direct_composition_surface_win_unittest.cc
+++ b/ui/gl/direct_composition_surface_win_unittest.cc
@@ -1214,8 +1214,9 @@
   ASSERT_TRUE(front_buffer_texture);
 
   auto front_buffer_image = base::MakeRefCounted<GLImageD3D>(
-      swap_chain_size, GL_BGRA_EXT, GL_UNSIGNED_BYTE, front_buffer_texture,
-      swap_chain);
+      swap_chain_size, GL_BGRA_EXT, GL_UNSIGNED_BYTE,
+      gfx::ColorSpace::CreateSRGB(), front_buffer_texture,
+      /*array_slice=*/0, /*plane_index=*/0, swap_chain);
   ASSERT_TRUE(front_buffer_image->Initialize());
 
   Microsoft::WRL::ComPtr<ID3D11Texture2D> back_buffer_texture;
diff --git a/ui/gl/gl_image_d3d.cc b/ui/gl/gl_image_d3d.cc
index 976ab720..75fc087c 100644
--- a/ui/gl/gl_image_d3d.cc
+++ b/ui/gl/gl_image_d3d.cc
@@ -11,49 +11,30 @@
 #ifndef EGL_ANGLE_image_d3d11_texture
 #define EGL_D3D11_TEXTURE_ANGLE 0x3484
 #define EGL_TEXTURE_INTERNAL_FORMAT_ANGLE 0x345D
+#define EGL_D3D11_TEXTURE_PLANE_ANGLE 0x3492
+#define EGL_D3D11_TEXTURE_ARRAY_SLICE_ANGLE 0x3493
 #endif /* EGL_ANGLE_image_d3d11_texture */
 
 namespace gl {
 
-namespace {
-
-bool ValidGLInternalFormat(unsigned internal_format) {
-  switch (internal_format) {
-    case GL_RGB:
-    case GL_RGBA:
-    case GL_BGRA_EXT:
-      return true;
-    default:
-      return false;
-  }
-}
-
-bool ValidGLDataType(unsigned data_type) {
-  switch (data_type) {
-    case GL_UNSIGNED_BYTE:
-    case GL_HALF_FLOAT_OES:
-      return true;
-    default:
-      return false;
-  }
-}
-
-}  // namespace
-
 GLImageD3D::GLImageD3D(const gfx::Size& size,
                        unsigned internal_format,
                        unsigned data_type,
+                       const gfx::ColorSpace& color_space,
                        Microsoft::WRL::ComPtr<ID3D11Texture2D> texture,
+                       size_t array_slice,
+                       size_t plane_index,
                        Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain)
     : GLImage(),
       size_(size),
       internal_format_(internal_format),
       data_type_(data_type),
       texture_(std::move(texture)),
+      array_slice_(array_slice),
+      plane_index_(plane_index),
       swap_chain_(std::move(swap_chain)) {
+  GLImage::SetColorSpace(color_space);
   DCHECK(texture_);
-  DCHECK(ValidGLInternalFormat(internal_format_));
-  DCHECK(ValidGLDataType(data_type_));
 }
 
 GLImageD3D::~GLImageD3D() {
@@ -69,7 +50,12 @@
 bool GLImageD3D::Initialize() {
   DCHECK_EQ(egl_image_, EGL_NO_IMAGE_KHR);
   const EGLint attribs[] = {EGL_TEXTURE_INTERNAL_FORMAT_ANGLE,
-                            GetInternalFormat(), EGL_NONE};
+                            internal_format_,
+                            EGL_D3D11_TEXTURE_ARRAY_SLICE_ANGLE,
+                            array_slice_,
+                            EGL_D3D11_TEXTURE_PLANE_ANGLE,
+                            plane_index_,
+                            EGL_NONE};
   egl_image_ =
       eglCreateImageKHR(GLSurfaceEGL::GetHardwareDisplay(), EGL_NO_CONTEXT,
                         EGL_D3D11_TEXTURE_ANGLE,
diff --git a/ui/gl/gl_image_d3d.h b/ui/gl/gl_image_d3d.h
index 6c5b2166..fa62f42 100644
--- a/ui/gl/gl_image_d3d.h
+++ b/ui/gl/gl_image_d3d.h
@@ -22,13 +22,19 @@
   // |internal_format| and |data_type| are passed to ANGLE and used for GL
   // operations.  |internal_format| may be different from the internal format
   // associated with the DXGI_FORMAT of the texture (e.g. RGB instead of
-  // BGRA_EXT for DXGI_FORMAT_B8G8R8A8_UNORM).  |data_type| should match the
-  // data type accociated with the DXGI_FORMAT of the texture.
+  // BGRA_EXT for DXGI_FORMAT_B8G8R8A8_UNORM). |data_type| should match the data
+  // type accociated with the DXGI_FORMAT of the texture.  |array_slice| is used
+  // when the |texture| is a D3D11 texture array, and |plane_index| is used for
+  // specifying the plane to bind to for multi-planar YUV textures.
+  // See EGL_ANGLE_d3d_texture_client_buffer spec for format restrictions.
   GLImageD3D(const gfx::Size& size,
              unsigned internal_format,
              unsigned data_type,
+             const gfx::ColorSpace& color_space,
              Microsoft::WRL::ComPtr<ID3D11Texture2D> texture,
-             Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain);
+             size_t array_slice = 0,
+             size_t plane_index = 0,
+             Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain = nullptr);
 
   // Safe downcast. Returns nullptr on failure.
   static GLImageD3D* FromGLImage(GLImage* image);
@@ -62,20 +68,27 @@
   const Microsoft::WRL::ComPtr<ID3D11Texture2D>& texture() const {
     return texture_;
   }
-
   const Microsoft::WRL::ComPtr<IDXGISwapChain1>& swap_chain() const {
     return swap_chain_;
   }
+  size_t array_slice() const { return array_slice_; }
+  size_t plane_index() const { return plane_index_; }
+
+ protected:
+  const gfx::Size size_;
+  const unsigned internal_format_;  // GLenum
+  const unsigned data_type_;        // GLenum
+
+  Microsoft::WRL::ComPtr<ID3D11Texture2D> texture_;
+  const size_t array_slice_;
+  const size_t plane_index_;
+
+  Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain_;
 
  private:
   ~GLImageD3D() override;
 
-  const gfx::Size size_;
-  const unsigned internal_format_;  // GLenum
-  const unsigned data_type_;        // GLenum
-  void* egl_image_ = nullptr;       // EGLImageKHR
-  Microsoft::WRL::ComPtr<ID3D11Texture2D> texture_;
-  Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain_;
+  void* egl_image_ = nullptr;  // EGLImageKHR
 
   DISALLOW_COPY_AND_ASSIGN(GLImageD3D);
 };
diff --git a/ui/gl/gl_image_d3d_unittest.cc b/ui/gl/gl_image_d3d_unittest.cc
index febd42ee..4b047f4d 100644
--- a/ui/gl/gl_image_d3d_unittest.cc
+++ b/ui/gl/gl_image_d3d_unittest.cc
@@ -52,7 +52,8 @@
     EXPECT_TRUE(SUCCEEDED(hr));
 
     auto image = base::MakeRefCounted<GLImageD3D>(
-        size, internal_format, data_type, std::move(d3d11_texture), nullptr);
+        size, internal_format, data_type, gfx::ColorSpace::CreateSRGB(),
+        std::move(d3d11_texture));
     EXPECT_TRUE(image->Initialize());
     return image;
   }
diff --git a/ui/gl/gl_switches.cc b/ui/gl/gl_switches.cc
index 7341e37..f95cc05 100644
--- a/ui/gl/gl_switches.cc
+++ b/ui/gl/gl_switches.cc
@@ -211,7 +211,7 @@
 // is large enough and allows DWM to consider the main backbuffer as an
 // an overlay candidate.
 const base::Feature kDirectCompositionForceFullDamage{
-    "DirectCompositionForceFullDamage", base::FEATURE_ENABLED_BY_DEFAULT};
+    "DirectCompositionForceFullDamage", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Use presentation feedback event queries (must be enabled) to limit latency.
 const base::Feature kDirectCompositionLowLatencyPresentation{
diff --git a/ui/gl/swap_chain_presenter.cc b/ui/gl/swap_chain_presenter.cc
index cefbf6b..07088b9b 100644
--- a/ui/gl/swap_chain_presenter.cc
+++ b/ui/gl/swap_chain_presenter.cc
@@ -634,23 +634,28 @@
 }
 
 bool SwapChainPresenter::TryPresentToDecodeSwapChain(
-    GLImageDXGI* nv12_image,
+    Microsoft::WRL::ComPtr<ID3D11Texture2D> texture,
+    unsigned array_slice,
+    const gfx::ColorSpace& color_space,
     const gfx::Rect& content_rect,
-    const gfx::Size& swap_chain_size) {
+    const gfx::Size& swap_chain_size,
+    DXGI_FORMAT swap_chain_format) {
   if (ShouldUseVideoProcessorScaling())
     return false;
 
   auto not_used_reason = DecodeSwapChainNotUsedReason::kFailedToPresent;
 
   bool nv12_supported =
+      (swap_chain_format == DXGI_FORMAT_NV12) &&
       (DXGI_FORMAT_NV12 ==
        DirectCompositionSurfaceWin::GetOverlayFormatUsedForSDR());
   // TODO(sunnyps): Try using decode swap chain for uploaded video images.
-  if (nv12_image && nv12_supported && !failed_to_present_decode_swapchain_) {
+  if (texture && nv12_supported && !failed_to_present_decode_swapchain_) {
     D3D11_TEXTURE2D_DESC texture_desc = {};
-    nv12_image->texture()->GetDesc(&texture_desc);
+    texture->GetDesc(&texture_desc);
 
-    bool is_decoder_texture = texture_desc.BindFlags & D3D11_BIND_DECODER;
+    bool is_decoder_texture = (texture_desc.Format == DXGI_FORMAT_NV12) &&
+                              (texture_desc.BindFlags & D3D11_BIND_DECODER);
 
     // Decode swap chains do not support shared resources.
     // TODO(sunnyps): Find a workaround for when the decoder moves to its own
@@ -684,8 +689,10 @@
 
     if (is_decoder_texture && !is_shared_texture && !is_unitary_texture_array &&
         is_overlay_supported_transform) {
-      if (PresentToDecodeSwapChain(nv12_image, content_rect, swap_chain_size))
+      if (PresentToDecodeSwapChain(texture, array_slice, color_space,
+                                   content_rect, swap_chain_size)) {
         return true;
+      }
       ReleaseSwapChainResources();
       failed_to_present_decode_swapchain_ = true;
       not_used_reason = DecodeSwapChainNotUsedReason::kFailedToPresent;
@@ -700,7 +707,7 @@
     } else if (!is_overlay_supported_transform) {
       not_used_reason = DecodeSwapChainNotUsedReason::kIncompatibleTransform;
     }
-  } else if (!nv12_image) {
+  } else if (!texture) {
     not_used_reason = DecodeSwapChainNotUsedReason::kSoftwareFrame;
   } else if (!nv12_supported) {
     not_used_reason = DecodeSwapChainNotUsedReason::kNv12NotSupported;
@@ -714,7 +721,9 @@
 }
 
 bool SwapChainPresenter::PresentToDecodeSwapChain(
-    GLImageDXGI* nv12_image,
+    Microsoft::WRL::ComPtr<ID3D11Texture2D> texture,
+    unsigned array_slice,
+    const gfx::ColorSpace& color_space,
     const gfx::Rect& content_rect,
     const gfx::Size& swap_chain_size) {
   DCHECK(!swap_chain_size.IsEmpty());
@@ -724,7 +733,7 @@
                swap_chain_size.ToString());
 
   Microsoft::WRL::ComPtr<IDXGIResource> decode_resource;
-  nv12_image->texture().As(&decode_resource);
+  texture.As(&decode_resource);
   DCHECK(decode_resource);
 
   if (!decode_swap_chain_ || decode_resource_ != decode_resource) {
@@ -786,10 +795,6 @@
 
     content_visual_->SetContent(decode_surface_.Get());
     layer_tree_->SetNeedsRebuildVisualTree();
-  } else if (last_presented_images_[kNV12ImageIndex] == nv12_image &&
-             swap_chain_size_ == swap_chain_size) {
-    // Early out if we're presenting the same image again.
-    return true;
   }
 
   RECT source_rect = content_rect.ToRECT();
@@ -800,10 +805,6 @@
   RECT target_rect = gfx::Rect(swap_chain_size).ToRECT();
   decode_swap_chain_->SetTargetRect(&target_rect);
 
-  gfx::ColorSpace color_space = nv12_image->color_space();
-  if (!color_space.IsValid())
-    color_space = gfx::ColorSpace::CreateREC709();
-
   // TODO(sunnyps): Move this to gfx::ColorSpaceWin helper where we can access
   // internal color space state and do a better job.
   // Common color spaces have primaries and transfer function similar to BT 709
@@ -811,7 +812,8 @@
   int color_space_flags = DXGI_MULTIPLANE_OVERLAY_YCbCr_FLAG_BT709;
   // Proper Rec 709 and 601 have limited or nominal color range.
   if (color_space == gfx::ColorSpace::CreateREC709() ||
-      color_space == gfx::ColorSpace::CreateREC601()) {
+      color_space == gfx::ColorSpace::CreateREC601() ||
+      !color_space.IsValid()) {
     color_space_flags |= DXGI_MULTIPLANE_OVERLAY_YCbCr_FLAG_NOMINAL_RANGE;
   }
   // xvYCC allows colors outside nominal range to encode negative colors that
@@ -823,8 +825,7 @@
       static_cast<DXGI_MULTIPLANE_OVERLAY_YCbCr_FLAGS>(color_space_flags));
 
   UINT present_flags = DXGI_PRESENT_USE_DURATION;
-  HRESULT hr =
-      decode_swap_chain_->PresentBuffer(nv12_image->level(), 1, present_flags);
+  HRESULT hr = decode_swap_chain_->PresentBuffer(array_slice, 1, present_flags);
   // Ignore DXGI_STATUS_OCCLUDED since that's not an error but only indicates
   // that the window is occluded and we can stop rendering.
   if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
@@ -832,8 +833,6 @@
     return false;
   }
 
-  last_presented_images_ = ui::DCRendererLayerParams::OverlayImages();
-  last_presented_images_[kNV12ImageIndex] = nv12_image;
   swap_chain_size_ = swap_chain_size;
   if (swap_chain_format_ == DXGI_FORMAT_NV12) {
     frames_since_color_space_change_++;
@@ -852,14 +851,20 @@
     const ui::DCRendererLayerParams& params) {
   GLImageDXGI* image_dxgi =
       GLImageDXGI::FromGLImage(params.images[kNV12ImageIndex].get());
+  GLImageD3D* image_d3d =
+      GLImageD3D::FromGLImage(params.images[kNV12ImageIndex].get());
+
   GLImageMemory* y_image_memory =
       GLImageMemory::FromGLImage(params.images[kYPlaneImageIndex].get());
   GLImageMemory* uv_image_memory =
       GLImageMemory::FromGLImage(params.images[kUVPlaneImageIndex].get());
+
   GLImageD3D* swap_chain_image =
       GLImageD3D::FromGLImage(params.images[kSwapChainImageIndex].get());
+  if (swap_chain_image && !swap_chain_image->swap_chain())
+    swap_chain_image = nullptr;
 
-  if (!image_dxgi && (!y_image_memory || !uv_image_memory) &&
+  if (!image_dxgi && !image_d3d && (!y_image_memory || !uv_image_memory) &&
       !swap_chain_image) {
     DLOG(ERROR) << "Video GLImages are missing";
     ReleaseSwapChainResources();
@@ -869,7 +874,8 @@
     return true;
   }
 
-  if (image_dxgi && !image_dxgi->texture()) {
+  if ((image_dxgi && !image_dxgi->texture()) ||
+      (image_d3d && !image_d3d->texture())) {
     // We can't proceed if |image_dxgi| has no underlying d3d11 texture.  It's
     // unclear how we get into this state, but we do observe crashes due to it.
     // Just stop here instead, and render incorrectly.
@@ -879,11 +885,14 @@
     return true;
   }
 
-  std::string image_type = "software video frame";
-  if (image_dxgi)
-    image_type = "hardware video frame";
-  if (swap_chain_image)
+  std::string image_type;
+  if (swap_chain_image) {
     image_type = "swap chain";
+  } else if (image_d3d || image_dxgi) {
+    image_type = "hardware video frame";
+  } else {
+    image_type = "software video frame";
+  }
 
   gfx::Transform transform = params.transform;
   gfx::Rect clip_rect = params.clip_rect;
@@ -900,12 +909,29 @@
   TRACE_EVENT2("gpu", "SwapChainPresenter::PresentToSwapChain", "image_type",
                image_type, "swap_chain_size", swap_chain_size.ToString());
 
-  bool content_is_hdr = image_dxgi && image_dxgi->color_space().IsHDR();
+  Microsoft::WRL::ComPtr<ID3D11Texture2D> input_texture;
+  unsigned input_level = 0u;
+  Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex;
+  gfx::ColorSpace input_color_space;
+  if (image_dxgi) {
+    input_texture = image_dxgi->texture();
+    input_level = image_dxgi->level();
+    // Keyed mutex may not exist.
+    keyed_mutex = image_dxgi->keyed_mutex();
+    input_color_space = image_dxgi->color_space();
+  } else if (image_d3d) {
+    input_texture = image_d3d->texture();
+    input_level = image_d3d->array_slice();
+    input_color_space = image_d3d->color_space();
+  }
+  if (!input_color_space.IsValid())
+    input_color_space = gfx::ColorSpace::CreateREC709();
+
+  bool content_is_hdr = input_color_space.IsHDR();
   // Do not create a swap chain if swap chain size will be empty.
   if (swap_chain_size.IsEmpty()) {
     swap_chain_size_ = swap_chain_size;
     if (swap_chain_) {
-      last_presented_images_ = ui::DCRendererLayerParams::OverlayImages();
       ReleaseSwapChainResources();
       content_visual_->SetContent(nullptr);
       layer_tree_->SetNeedsRebuildVisualTree();
@@ -918,20 +944,16 @@
   // Swap chain image already has a swap chain that's presented by the client
   // e.g. for webgl/canvas low-latency/desynchronized mode.
   if (swap_chain_image) {
+    DCHECK(swap_chain_image->swap_chain());
     content_visual_->SetContent(swap_chain_image->swap_chain().Get());
-    if (last_presented_images_[kSwapChainImageIndex] != swap_chain_image) {
-      last_presented_images_ = params.images;
+    if (last_presented_images_ != params.images) {
       ReleaseSwapChainResources();
+      last_presented_images_ = params.images;
       layer_tree_->SetNeedsRebuildVisualTree();
     }
     return true;
   }
 
-  if (TryPresentToDecodeSwapChain(image_dxgi, params.content_rect,
-                                  swap_chain_size)) {
-    return true;
-  }
-
   bool swap_chain_resized = swap_chain_size_ != swap_chain_size;
 
   DXGI_FORMAT swap_chain_format =
@@ -940,6 +962,21 @@
   bool toggle_protected_video =
       protected_video_type_ != params.protected_video_type;
 
+  if (swap_chain_ && !swap_chain_resized && !swap_chain_format_changed &&
+      !toggle_protected_video && last_presented_images_ == params.images) {
+    // The swap chain is presenting the same images as last swap, which means
+    // that the images were never returned to the video decoder and should
+    // have the same contents as last time. It shouldn't need to be redrawn.
+    return true;
+  }
+
+  if (TryPresentToDecodeSwapChain(input_texture, input_level, input_color_space,
+                                  params.content_rect, swap_chain_size,
+                                  swap_chain_format)) {
+    last_presented_images_ = params.images;
+    return true;
+  }
+
   // Try reallocating swap chain if resizing fails.
   if (!swap_chain_ || swap_chain_resized || swap_chain_format_changed ||
       toggle_protected_video) {
@@ -950,22 +987,9 @@
     }
     content_visual_->SetContent(swap_chain_.Get());
     layer_tree_->SetNeedsRebuildVisualTree();
-  } else if (last_presented_images_ == params.images) {
-    // The swap chain is presenting the same images as last swap, which means
-    // that the images were never returned to the video decoder and should
-    // have the same contents as last time. It shouldn't need to be redrawn.
-    return true;
   }
-  last_presented_images_ = params.images;
 
-  Microsoft::WRL::ComPtr<ID3D11Texture2D> input_texture;
-  UINT input_level;
-  Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex;
-  if (image_dxgi) {
-    input_texture = image_dxgi->texture();
-    input_level = static_cast<UINT>(image_dxgi->level());
-    // Keyed mutex may not exist.
-    keyed_mutex = image_dxgi->keyed_mutex();
+  if (input_texture) {
     staging_texture_.Reset();
     copy_texture_.Reset();
   } else {
@@ -1056,6 +1080,7 @@
     DLOG(ERROR) << "Present failed with error 0x" << std::hex << hr;
     return false;
   }
+  last_presented_images_ = params.images;
   frames_since_color_space_change_++;
   RecordPresentationStatistics();
   return true;
@@ -1285,6 +1310,7 @@
 }
 
 void SwapChainPresenter::ReleaseSwapChainResources() {
+  last_presented_images_ = ui::DCRendererLayerParams::OverlayImages();
   output_view_.Reset();
   swap_chain_.Reset();
   decode_surface_.Reset();
diff --git a/ui/gl/swap_chain_presenter.h b/ui/gl/swap_chain_presenter.h
index afc17b0..e5531420 100644
--- a/ui/gl/swap_chain_presenter.h
+++ b/ui/gl/swap_chain_presenter.h
@@ -19,7 +19,6 @@
 
 namespace gl {
 class DCLayerTree;
-class GLImageDXGI;
 class GLImageMemory;
 
 // SwapChainPresenter holds a swap chain, direct composition visuals, and other
@@ -161,14 +160,20 @@
   // Try presenting to a decode swap chain based on various conditions such as
   // global state (e.g. finch, NV12 support), texture flags, and transform.
   // Returns true on success.  See PresentToDecodeSwapChain() for more info.
-  bool TryPresentToDecodeSwapChain(GLImageDXGI* nv12_image,
-                                   const gfx::Rect& content_rect,
-                                   const gfx::Size& swap_chain_size);
+  bool TryPresentToDecodeSwapChain(
+      Microsoft::WRL::ComPtr<ID3D11Texture2D> texture,
+      unsigned array_slice,
+      const gfx::ColorSpace& color_space,
+      const gfx::Rect& content_rect,
+      const gfx::Size& swap_chain_size,
+      DXGI_FORMAT swap_chain_format);
 
   // Present to a decode swap chain created from compatible video decoder
   // buffers using given |nv12_image| with destination size |swap_chain_size|.
   // Returns true on success.
-  bool PresentToDecodeSwapChain(GLImageDXGI* nv12_image,
+  bool PresentToDecodeSwapChain(Microsoft::WRL::ComPtr<ID3D11Texture2D> texture,
+                                unsigned array_slice,
+                                const gfx::ColorSpace& color_space,
                                 const gfx::Rect& content_rect,
                                 const gfx::Size& swap_chain_size);
 
diff --git a/ui/platform_window/x11/test/events_x_unittest.cc b/ui/platform_window/x11/test/events_x_unittest.cc
index 817c3e8..15b8869 100644
--- a/ui/platform_window/x11/test/events_x_unittest.cc
+++ b/ui/platform_window/x11/test/events_x_unittest.cc
@@ -763,7 +763,7 @@
   }
 }
 
-// Checks that Event.Latency.OS.TOUCH_PRESSED, TOUCH_MOVED,
+// Checks that Event.Latency.OS2.TOUCH_PRESSED, TOUCH_MOVED,
 // and TOUCH_RELEASED histograms are computed properly.
 TEST_F(EventsXTest, EventLatencyOSTouchHistograms) {
   base::HistogramTester histogram_tester;
@@ -780,14 +780,17 @@
                                gfx::Point(10, 10), {});
   auto touch_begin = ui::BuildTouchEventFromXEvent(*scoped_xevent);
   histogram_tester.ExpectTotalCount("Event.Latency.OS.TOUCH_PRESSED", 1);
+  histogram_tester.ExpectTotalCount("Event.Latency.OS2.TOUCH_PRESSED", 1);
   scoped_xevent.InitTouchEvent(0, x11::Input::DeviceEvent::TouchUpdate, 5,
                                gfx::Point(20, 20), {});
   auto touch_update = ui::BuildTouchEventFromXEvent(*scoped_xevent);
   histogram_tester.ExpectTotalCount("Event.Latency.OS.TOUCH_MOVED", 1);
+  histogram_tester.ExpectTotalCount("Event.Latency.OS2.TOUCH_MOVED", 1);
   scoped_xevent.InitTouchEvent(0, x11::Input::DeviceEvent::TouchEnd, 5,
                                gfx::Point(30, 30), {});
   auto touch_end = ui::BuildTouchEventFromXEvent(*scoped_xevent);
   histogram_tester.ExpectTotalCount("Event.Latency.OS.TOUCH_RELEASED", 1);
+  histogram_tester.ExpectTotalCount("Event.Latency.OS2.TOUCH_RELEASED", 1);
 }
 
 TEST_F(EventsXTest, EventLatencyOSMouseWheelHistogram) {
@@ -802,6 +805,7 @@
   });
   auto mouse_ev = ui::BuildMouseWheelEventFromXEvent(native_event);
   histogram_tester.ExpectTotalCount("Event.Latency.OS.MOUSE_WHEEL", 1);
+  histogram_tester.ExpectTotalCount("Event.Latency.OS2.MOUSE_WHEEL", 1);
 }
 
 }  // namespace ui
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 3769ddb2..b0404a8 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -972,6 +972,8 @@
     "test/views_test_base.h",
     "test/views_test_helper.cc",
     "test/views_test_helper.h",
+    "test/widget_animation_waiter.cc",
+    "test/widget_animation_waiter.h",
     "test/widget_test.cc",
     "test/widget_test.h",
     "test/widget_test_api.cc",
diff --git a/ash/shelf/test/widget_animation_waiter.cc b/ui/views/test/widget_animation_waiter.cc
similarity index 82%
rename from ash/shelf/test/widget_animation_waiter.cc
rename to ui/views/test/widget_animation_waiter.cc
index ec5cc3ba..33ad6dd 100644
--- a/ash/shelf/test/widget_animation_waiter.cc
+++ b/ui/views/test/widget_animation_waiter.cc
@@ -2,23 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/shelf/test/widget_animation_waiter.h"
+#include "ui/views/test/widget_animation_waiter.h"
 
-#include "ash/test/ash_test_base.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animation_delegate.h"
 #include "ui/compositor/layer_animator.h"
 #include "ui/views/widget/widget.h"
 
-namespace ash {
+namespace views {
 
-WidgetAnimationWaiter::WidgetAnimationWaiter(views::Widget* widget)
+WidgetAnimationWaiter::WidgetAnimationWaiter(Widget* widget)
     : target_bounds_(gfx::Rect()), widget_(widget) {
   widget->GetLayer()->GetAnimator()->AddObserver(this);
 }
 
-WidgetAnimationWaiter::WidgetAnimationWaiter(views::Widget* widget,
-                                             gfx::Rect target_bounds)
+WidgetAnimationWaiter::WidgetAnimationWaiter(Widget* widget,
+                                             const gfx::Rect& target_bounds)
     : target_bounds_(target_bounds), widget_(widget) {
   widget->GetLayer()->GetAnimator()->AddObserver(this);
 }
@@ -41,6 +41,7 @@
     run_loop_.Quit();
   }
 }
+
 void WidgetAnimationWaiter::OnLayerAnimationAborted(
     ui::LayerAnimationSequence* sequence) {}
 
@@ -59,4 +60,4 @@
   return animation_scheduled_ && is_valid_animation_;
 }
 
-}  // namespace ash
+}  // namespace views
diff --git a/ash/shelf/test/widget_animation_waiter.h b/ui/views/test/widget_animation_waiter.h
similarity index 76%
rename from ash/shelf/test/widget_animation_waiter.h
rename to ui/views/test/widget_animation_waiter.h
index a5e3f91e..dc59c8eb 100644
--- a/ash/shelf/test/widget_animation_waiter.h
+++ b/ui/views/test/widget_animation_waiter.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_SHELF_TEST_WIDGET_ANIMATION_WAITER_H_
-#define ASH_SHELF_TEST_WIDGET_ANIMATION_WAITER_H_
+#ifndef UI_VIEWS_TEST_WIDGET_ANIMATION_WAITER_H_
+#define UI_VIEWS_TEST_WIDGET_ANIMATION_WAITER_H_
 
 #include "base/run_loop.h"
 #include "ui/compositor/layer_animation_observer.h"
@@ -14,16 +14,13 @@
 
 namespace views {
 class Widget;
-}
-
-namespace ash {
 
 // Class which waits until for a widget to finish animating and verifies
 // that the layer transform animation was valid.
 class WidgetAnimationWaiter : ui::LayerAnimationObserver {
  public:
-  WidgetAnimationWaiter(views::Widget* widget);
-  WidgetAnimationWaiter(views::Widget* widget, gfx::Rect target_bounds);
+  explicit WidgetAnimationWaiter(Widget* widget);
+  WidgetAnimationWaiter(Widget* widget, const gfx::Rect& target_bounds);
   ~WidgetAnimationWaiter() override;
 
   // ui::LayerAnimationObserver:
@@ -40,13 +37,13 @@
   gfx::Rect target_bounds_;
 
   // Unowned
-  views::Widget* widget_;
+  Widget* const widget_;
 
   base::RunLoop run_loop_;
   bool is_valid_animation_ = false;
   bool animation_scheduled_ = false;
 };
 
-}  // namespace ash
+}  // namespace views
 
-#endif  // ASH_SHELF_TEST_WIDGET_ANIMATION_WAITER_H_
+#endif  // UI_VIEWS_TEST_WIDGET_ANIMATION_WAITER_H_
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 91dd04b1..2d64e8ab 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -2730,8 +2730,8 @@
         touch_ids_.insert(input[i].dwID);
         GenerateTouchEvent(ui::ET_TOUCH_PRESSED, touch_point, touch_id,
                            event_time, &touch_events);
-        ui::ComputeEventLatencyOSWinFromTickCount(ui::ET_TOUCH_PRESSED,
-                                                  input[i].dwTime, event_time);
+        ui::ComputeEventLatencyOSFromTOUCHINPUT(ui::ET_TOUCH_PRESSED, input[i],
+                                                event_time);
         touch_down_contexts_++;
         base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
             FROM_HERE,
@@ -2742,16 +2742,16 @@
         if (input[i].dwFlags & TOUCHEVENTF_MOVE) {
           GenerateTouchEvent(ui::ET_TOUCH_MOVED, touch_point, touch_id,
                              event_time, &touch_events);
-          ui::ComputeEventLatencyOSWinFromTickCount(
-              ui::ET_TOUCH_MOVED, input[i].dwTime, event_time);
+          ui::ComputeEventLatencyOSFromTOUCHINPUT(ui::ET_TOUCH_MOVED, input[i],
+                                                  event_time);
         }
 
         if (input[i].dwFlags & TOUCHEVENTF_UP) {
           touch_ids_.erase(input[i].dwID);
           GenerateTouchEvent(ui::ET_TOUCH_RELEASED, touch_point, touch_id,
                              event_time, &touch_events);
-          ui::ComputeEventLatencyOSWinFromTickCount(
-              ui::ET_TOUCH_RELEASED, input[i].dwTime, event_time);
+          ui::ComputeEventLatencyOSFromTOUCHINPUT(ui::ET_TOUCH_RELEASED,
+                                                  input[i], event_time);
           id_generator_.ReleaseNumber(input[i].dwID);
         }
       }
@@ -3245,14 +3245,8 @@
       ui::PointerDetails(ui::EventPointerType::kTouch, mapped_pointer_id,
                          radius_x, radius_y, pressure, rotation_angle),
       ui::GetModifiersFromKeyState());
-  if (pointer_info.PerformanceCount) {
-    ui::ComputeEventLatencyOSWinFromPerformanceCounter(
-        event_type, pointer_info.PerformanceCount, event_time);
-  } else {
-    ui::ComputeEventLatencyOSWinFromTickCount(event_type, pointer_info.dwTime,
-                                              event_time);
-  }
-
+  ui::ComputeEventLatencyOSFromPOINTER_INFO(event_type, pointer_info,
+                                            event_time);
   event.latency()->AddLatencyNumberWithTimestamp(
       ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, event_time);
 
diff --git a/ui/views/win/pen_event_processor.cc b/ui/views/win/pen_event_processor.cc
index 3f0bb802..7257458 100644
--- a/ui/views/win/pen_event_processor.cc
+++ b/ui/views/win/pen_event_processor.cc
@@ -201,13 +201,8 @@
   std::unique_ptr<ui::TouchEvent> event = std::make_unique<ui::TouchEvent>(
       event_type, point, event_time, pointer_details,
       flags | ui::GetModifiersFromKeyState());
-  if (pointer_info.PerformanceCount) {
-    ui::ComputeEventLatencyOSWinFromPerformanceCounter(
-        event_type, pointer_info.PerformanceCount, event_time);
-  } else {
-    ui::ComputeEventLatencyOSWinFromTickCount(event_type, pointer_info.dwTime,
-                                              event_time);
-  }
+  ui::ComputeEventLatencyOSFromPOINTER_INFO(event_type, pointer_info,
+                                            event_time);
   event->set_hovering(event_type == ui::ET_TOUCH_RELEASED);
   event->latency()->AddLatencyNumberWithTimestamp(
       ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, event_time);
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java
index f35183f6..abde911 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java
@@ -1544,5 +1544,11 @@
 
         navigateAndWaitForCompletion(URL3, () -> navigationController.reload());
         assertEquals(0, mCallback.onCompletedCallback.getNavigationEntryOffset());
+
+        navigateAndWaitForCompletion(URL1, () -> navigationController.goToIndex(0));
+        assertEquals(-2, mCallback.onCompletedCallback.getNavigationEntryOffset());
+
+        navigateAndWaitForCompletion(URL3, () -> navigationController.goToIndex(2));
+        assertEquals(2, mCallback.onCompletedCallback.getNavigationEntryOffset());
     }
 }
diff --git a/weblayer/public/java/org/chromium/weblayer/Navigation.java b/weblayer/public/java/org/chromium/weblayer/Navigation.java
index 3c17d8d..607ae82 100644
--- a/weblayer/public/java/org/chromium/weblayer/Navigation.java
+++ b/weblayer/public/java/org/chromium/weblayer/Navigation.java
@@ -452,9 +452,10 @@
 
     /**
      * Returns the offset between the indices of the previous last committed and the newly committed
-     * navigation entries (e.g. -1 for back navigations, 0 for reloads, 1 for forward navigations).
-     * This may not cover all corner cases, and can be incorrect in cases like main frame client
-     * redirects.
+     * navigation entries, for example -1 for back navigations, 0 for reloads, 1 for forward
+     * navigations or new navigations. Note that the return value can be less than -1 or greater
+     * than 1 if the navigation goes back/forward multiple entries. This may not cover all corner
+     * cases, and can be incorrect in cases like main frame client redirects.
      *
      * @since 92
      */