diff --git a/DEPS b/DEPS
index 06a64d8..e01072d 100644
--- a/DEPS
+++ b/DEPS
@@ -283,11 +283,11 @@
   # 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': 'b4d4231b75944e5b6616267bb178d3beeca16077',
+  'angle_revision': '593cd597a31e649f7219d3bdd021b76ec2f835c1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '47c22467388bd67935e0dc65017e70f3fb9a4afc',
+  'swiftshader_revision': 'afbfd351374d93484f8eecf532afbd0dac084865',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -920,7 +920,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'Hv4Itz1fIjMhW5YyoWnY4NPZ7dPYr76pHLVwW717k98C',
+          'version': '6e-zhUwS-KT1j_VrRmOMCbv07k-qvd16ONwqxtkR5mEC',
       },
     ],
     'condition': 'checkout_android',
@@ -1705,7 +1705,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'bb289ce3cb15bbabd42fdcb01439367846d9069d',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '9474c0e203c79ec191b18c3d305513282bbda1de',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'edd275f0ad33c708fba30643cf971adf0f6ba488',
 
   'src/third_party/webrtc':
     Var('webrtc_git') + '/src.git' + '@' + '83ff95add93ca6cb262eb5f6f1ce4065858ae1dd',
diff --git a/ash/components/arc/session/arc_container_client_adapter.cc b/ash/components/arc/session/arc_container_client_adapter.cc
index bfaacce..1a038b7 100644
--- a/ash/components/arc/session/arc_container_client_adapter.cc
+++ b/ash/components/arc/session/arc_container_client_adapter.cc
@@ -167,7 +167,6 @@
     request.set_packages_cache_mode(
         ToLoginManagerPackageCacheMode(params.packages_cache_mode));
     request.set_skip_gms_core_cache(params.skip_gms_core_cache);
-    request.set_skip_tts_cache(params.skip_tts_cache);
     request.set_is_demo_session(params.is_demo_session);
     request.set_demo_session_apps_path(params.demo_session_apps_path.value());
     request.set_locale(params.locale);
diff --git a/ash/components/arc/session/arc_container_client_adapter_unittest.cc b/ash/components/arc/session/arc_container_client_adapter_unittest.cc
index 7f6ff379..d7c4a5c 100644
--- a/ash/components/arc/session/arc_container_client_adapter_unittest.cc
+++ b/ash/components/arc/session/arc_container_client_adapter_unittest.cc
@@ -196,26 +196,6 @@
   EXPECT_TRUE(request.enable_tts_caching());
 }
 
-TEST_F(ArcContainerClientAdapterTest, ConvertUpgradeParams_SkipTtsCacheSetup) {
-  UpgradeParams upgrade_params;
-  upgrade_params.skip_tts_cache = true;
-  client_adapter()->UpgradeArc(std::move(upgrade_params),
-                               base::BindOnce(&OnMiniInstanceStarted));
-  const auto& upgrade_request =
-      chromeos::FakeSessionManagerClient::Get()->last_upgrade_arc_request();
-  EXPECT_TRUE(upgrade_request.skip_tts_cache());
-}
-
-TEST_F(ArcContainerClientAdapterTest,
-       ConvertUpgradeParams_EnableTtsCacheSetup) {
-  UpgradeParams upgrade_params;
-  client_adapter()->UpgradeArc(std::move(upgrade_params),
-                               base::BindOnce(&OnMiniInstanceStarted));
-  const auto& upgrade_request =
-      chromeos::FakeSessionManagerClient::Get()->last_upgrade_arc_request();
-  EXPECT_FALSE(upgrade_request.skip_tts_cache());
-}
-
 struct DalvikMemoryProfileTestParam {
   // Requested profile.
   StartParams::DalvikMemoryProfile profile;
diff --git a/ash/components/arc/session/arc_upgrade_params.cc b/ash/components/arc/session/arc_upgrade_params.cc
index 980dba98..db9b3be 100644
--- a/ash/components/arc/session/arc_upgrade_params.cc
+++ b/ash/components/arc/session/arc_upgrade_params.cc
@@ -36,8 +36,6 @@
       packages_cache_mode(GetPackagesCacheMode()),
       skip_gms_core_cache(base::CommandLine::ForCurrentProcess()->HasSwitch(
           ash::switches::kArcDisableGmsCoreCache)),
-      skip_tts_cache(base::CommandLine::ForCurrentProcess()->HasSwitch(
-          ash::switches::kArcDisableTtsCache)),
       enable_arc_nearby_share(
           base::FeatureList::IsEnabled(arc::kEnableArcNearbyShare)) {}
 
diff --git a/ash/components/arc/session/arc_upgrade_params.h b/ash/components/arc/session/arc_upgrade_params.h
index 191b4c6..4dc07d9 100644
--- a/ash/components/arc/session/arc_upgrade_params.h
+++ b/ash/components/arc/session/arc_upgrade_params.h
@@ -67,10 +67,6 @@
   // The constructor automatically populates this from command-line.
   bool skip_gms_core_cache;
 
-  // Option to disable TTS cache.
-  // The constructor automatically populates this from command-line.
-  bool skip_tts_cache;
-
   // The supervision transition state for this account. Indicates whether
   // child account should become regular, regular account should become child
   // or neither.
diff --git a/ash/components/arc/session/arc_vm_client_adapter.cc b/ash/components/arc/session/arc_vm_client_adapter.cc
index f09f575c..00c3413 100644
--- a/ash/components/arc/session/arc_vm_client_adapter.cc
+++ b/ash/components/arc/session/arc_vm_client_adapter.cc
@@ -160,8 +160,6 @@
           static_cast<int>(upgrade_params.management_transition)),
       base::StringPrintf("%s.serialno=%s", prefix.c_str(),
                          serial_number.c_str()),
-      base::StringPrintf("%s.skip_tts_cache=%d", prefix.c_str(),
-                         upgrade_params.skip_tts_cache),
   };
   // Conditionally sets more properties based on |upgrade_params|.
   if (!upgrade_params.locale.empty()) {
diff --git a/ash/components/arc/session/arc_vm_client_adapter_unittest.cc b/ash/components/arc/session/arc_vm_client_adapter_unittest.cc
index 29d3b82..a8d6a6e2 100644
--- a/ash/components/arc/session/arc_vm_client_adapter_unittest.cc
+++ b/ash/components/arc/session/arc_vm_client_adapter_unittest.cc
@@ -2544,22 +2544,5 @@
       base::Contains(request.params(), "androidboot.arc.tts.caching=1"));
 }
 
-TEST_F(ArcVmClientAdapterTest, ConvertUpgradeParams_SkipTtsCacheSetup) {
-  StartMiniArc();
-  UpgradeParams upgrade_params = GetPopulatedUpgradeParams();
-  upgrade_params.skip_tts_cache = true;
-  UpgradeArcWithParams(true, std::move(upgrade_params));
-  EXPECT_TRUE(base::Contains(boot_notification_server()->received_data(),
-                             "ro.boot.skip_tts_cache=1"));
-}
-
-TEST_F(ArcVmClientAdapterTest, ConvertUpgradeParams_EnableTtsCacheSetup) {
-  StartMiniArc();
-  UpgradeParams upgrade_params = GetPopulatedUpgradeParams();
-  UpgradeArcWithParams(true, std::move(upgrade_params));
-  EXPECT_TRUE(base::Contains(boot_notification_server()->received_data(),
-                             "ro.boot.skip_tts_cache=0"));
-}
-
 }  // namespace
 }  // namespace arc
diff --git a/ash/constants/ash_switches.cc b/ash/constants/ash_switches.cc
index 17caef6..3f79d74 100644
--- a/ash/constants/ash_switches.cc
+++ b/ash/constants/ash_switches.cc
@@ -101,9 +101,6 @@
 // apps silently. Used in autotests to resolve racy conditions.
 const char kArcDisablePlayAutoInstall[] = "arc-disable-play-auto-install";
 
-// Used in autotest to disable TTS cache which is on by default.
-const char kArcDisableTtsCache[] = "arc-disable-tts-cache";
-
 // Flag that disables ureadahead completely, including host and guest parts.
 // See also |kArcVmUreadaheadMode|.
 const char kArcDisableUreadahead[] = "arc-disable-ureadahead";
diff --git a/ash/constants/ash_switches.h b/ash/constants/ash_switches.h
index 39e47b91..854ed04 100644
--- a/ash/constants/ash_switches.h
+++ b/ash/constants/ash_switches.h
@@ -40,7 +40,6 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kArcDisableMediaStoreMaintenance[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcDisablePlayAutoInstall[];
-COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcDisableTtsCache[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcDisableUreadahead[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcForceShowOptInUi[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcGeneratePlayAutoInstall[];
diff --git a/ash/webui/personalization_app/personalization_app_ui.cc b/ash/webui/personalization_app/personalization_app_ui.cc
index 6dbecb7..76cd412 100644
--- a/ash/webui/personalization_app/personalization_app_ui.cc
+++ b/ash/webui/personalization_app/personalization_app_ui.cc
@@ -161,6 +161,8 @@
        IDS_PERSONALIZATION_APP_AMBIENT_MODE_TOPIC_SOURCE_UNSELECTED_ROW},
       {"ambientModeWeatherTitle",
        IDS_PERSONALIZATION_APP_AMBIENT_MODE_WEATHER_TITLE},
+      {"ambientModeAriaDescriptionWeather",
+       IDS_PERSONALIZATION_APP_ARIA_DESCRIPTION_AMBIENT_MODE_WEATHER},
       {"ambientModeWeatherUnitFahrenheit",
        IDS_PERSONALIZATION_APP_AMBIENT_MODE_WEATHER_UNIT_FAHRENHEIT},
       {"ambientModeWeatherUnitCelsius",
diff --git a/ash/webui/personalization_app/resources/trusted/ambient/ambient_weather_element.html b/ash/webui/personalization_app/resources/trusted/ambient/ambient_weather_element.html
index 297866a..b106001 100644
--- a/ash/webui/personalization_app/resources/trusted/ambient/ambient_weather_element.html
+++ b/ash/webui/personalization_app/resources/trusted/ambient/ambient_weather_element.html
@@ -11,13 +11,15 @@
   }
 </style>
 <div id="weatherDiv">
-  <h3 class="ambient-subpage-element-title" aria-hidden="true">
+  <h3 id="weatherTitle" class="ambient-subpage-element-title"
+      aria-hidden="true">
     $i18n{ambientModeWeatherTitle}
   </h3>
   <div class="weather-unit-list">
     <cr-radio-group
         id="ambientTemperatureUnit"
         selected="{{selectedTemperatureUnit}}"
+        aria-description="$i18n{ambientModeAriaDescriptionWeather}"
         aria-labelledby="weatherTitle">
       <cr-radio-button
           name="[[temperatureUnit_.kFahrenheit]]"
diff --git a/ash/webui/personalization_app/resources/trusted/ambient/animation_theme_list_element.html b/ash/webui/personalization_app/resources/trusted/ambient/animation_theme_list_element.html
index 18b10ceb..307f462 100644
--- a/ash/webui/personalization_app/resources/trusted/ambient/animation_theme_list_element.html
+++ b/ash/webui/personalization_app/resources/trusted/ambient/animation_theme_list_element.html
@@ -1,8 +1,4 @@
 <style include="common-style">
-  #animationThemeList {
-    display: flex;
-    flex-direction: row;
-  }
   iron-list {
     height: 100%;
     /* 516px is the total width + padding for all 3 options */
@@ -10,11 +6,12 @@
   }
 </style>
 
-<h3 class="ambient-subpage-element-title" aria-hidden="true">
+<h3 id="animationThemeDescription" class="ambient-subpage-element-title"
+    aria-hidden="true">
   $i18n{ambientModeAnimationTitle}
 </h3>
-<iron-list id="grid" items="[[animationThemes]]" as="theme" grid
-    role="listbox">
+<iron-list items="[[animationThemes]]" as="theme" grid
+    role="listbox" aria-describedby="animationThemeDescription">
   <template>
     <animation-theme-item tabindex$="[[tabIndex]]"
         animation-theme="[[theme]]" role="option"
diff --git a/ash/webui/personalization_app/resources/trusted/ambient/toggle_row_element.html b/ash/webui/personalization_app/resources/trusted/ambient/toggle_row_element.html
index 2a9b7c8..d6a78587 100644
--- a/ash/webui/personalization_app/resources/trusted/ambient/toggle_row_element.html
+++ b/ash/webui/personalization_app/resources/trusted/ambient/toggle_row_element.html
@@ -2,9 +2,10 @@
 </style>
 <div class="ambient-toggle-row-container">
   <div class="ambient-toggle-row">
-    <p>$i18n{ambientModePageDescription}</p>
+    <p id="toggleDescription">$i18n{ambientModePageDescription}</p>
     <cr-toggle id="toggle" checked="{{checked}}"
-        aria-label$="[[getAriaLabel_(checked)]]">
+        aria-label$="[[getAriaLabel_(checked)]]"
+        aria-describedby="toggleDescription">
     </cr-toggle>
   </div>
 </div>
diff --git a/chrome/browser/ash/crosapi/BUILD.gn b/chrome/browser/ash/crosapi/BUILD.gn
index 4caff55..6ec2206 100644
--- a/chrome/browser/ash/crosapi/BUILD.gn
+++ b/chrome/browser/ash/crosapi/BUILD.gn
@@ -169,6 +169,8 @@
     "select_file_ash.h",
     "sharesheet_ash.cc",
     "sharesheet_ash.h",
+    "speech_recognition_ash.cc",
+    "speech_recognition_ash.h",
     "structured_metrics_service_ash.cc",
     "structured_metrics_service_ash.h",
     "system_display_ash.cc",
@@ -250,6 +252,7 @@
     "//components/flags_ui",
     "//components/keyed_service/content",
     "//components/language/core/browser:browser",
+    "//components/live_caption:utils",
     "//components/metrics",
     "//components/metrics/structured",
     "//components/metrics_services_manager:metrics_services_manager",
diff --git a/chrome/browser/ash/crosapi/crosapi_ash.cc b/chrome/browser/ash/crosapi/crosapi_ash.cc
index 0817a08..46f067f 100644
--- a/chrome/browser/ash/crosapi/crosapi_ash.cc
+++ b/chrome/browser/ash/crosapi/crosapi_ash.cc
@@ -72,6 +72,7 @@
 #include "chrome/browser/ash/crosapi/search_provider_ash.h"
 #include "chrome/browser/ash/crosapi/select_file_ash.h"
 #include "chrome/browser/ash/crosapi/sharesheet_ash.h"
+#include "chrome/browser/ash/crosapi/speech_recognition_ash.h"
 #include "chrome/browser/ash/crosapi/structured_metrics_service_ash.h"
 #include "chrome/browser/ash/crosapi/system_display_ash.h"
 #include "chrome/browser/ash/crosapi/task_manager_ash.h"
@@ -206,6 +207,7 @@
       search_provider_ash_(std::make_unique<SearchProviderAsh>()),
       select_file_ash_(std::make_unique<SelectFileAsh>()),
       sharesheet_ash_(std::make_unique<SharesheetAsh>()),
+      speech_recognition_ash_(std::make_unique<SpeechRecognitionAsh>()),
 #if BUILDFLAG(USE_VAAPI) || BUILDFLAG(USE_V4L2_CODEC)
       stable_video_decoder_factory_ash_(
           std::make_unique<media::StableVideoDecoderFactoryService>()),
@@ -438,6 +440,11 @@
   sharesheet_ash_->BindReceiver(std::move(receiver));
 }
 
+void CrosapiAsh::BindSpeechRecognition(
+    mojo::PendingReceiver<mojom::SpeechRecognition> receiver) {
+  speech_recognition_ash_->BindReceiver(std::move(receiver));
+}
+
 void CrosapiAsh::BindScreenManager(
     mojo::PendingReceiver<mojom::ScreenManager> receiver) {
   screen_manager_ash_->BindReceiver(std::move(receiver));
diff --git a/chrome/browser/ash/crosapi/crosapi_ash.h b/chrome/browser/ash/crosapi/crosapi_ash.h
index e2951ed..b5ef586e4 100644
--- a/chrome/browser/ash/crosapi/crosapi_ash.h
+++ b/chrome/browser/ash/crosapi/crosapi_ash.h
@@ -80,6 +80,7 @@
 class SearchProviderAsh;
 class SelectFileAsh;
 class SharesheetAsh;
+class SpeechRecognitionAsh;
 class StructuredMetricsServiceAsh;
 class SystemDisplayAsh;
 class TaskManagerAsh;
@@ -229,6 +230,8 @@
       override;
   void BindSharesheet(
       mojo::PendingReceiver<mojom::Sharesheet> receiver) override;
+  void BindSpeechRecognition(
+      mojo::PendingReceiver<mojom::SpeechRecognition> receiver) override;
   void BindStableVideoDecoderFactory(
       mojo::GenericPendingReceiver receiver) override;
   void BindHidManager(
@@ -430,6 +433,7 @@
   std::unique_ptr<SearchProviderAsh> search_provider_ash_;
   std::unique_ptr<SelectFileAsh> select_file_ash_;
   std::unique_ptr<SharesheetAsh> sharesheet_ash_;
+  std::unique_ptr<SpeechRecognitionAsh> speech_recognition_ash_;
 #if BUILDFLAG(USE_VAAPI) || BUILDFLAG(USE_V4L2_CODEC)
   std::unique_ptr<media::StableVideoDecoderFactoryService>
       stable_video_decoder_factory_ash_;
diff --git a/chrome/browser/ash/crosapi/crosapi_util.cc b/chrome/browser/ash/crosapi/crosapi_util.cc
index 1cb4b2d..10563aa 100644
--- a/chrome/browser/ash/crosapi/crosapi_util.cc
+++ b/chrome/browser/ash/crosapi/crosapi_util.cc
@@ -84,6 +84,7 @@
 #include "chromeos/crosapi/mojom/remoting.mojom.h"
 #include "chromeos/crosapi/mojom/screen_manager.mojom.h"
 #include "chromeos/crosapi/mojom/sharesheet.mojom.h"
+#include "chromeos/crosapi/mojom/speech_recognition.mojom.h"
 #include "chromeos/crosapi/mojom/structured_metrics_service.mojom.h"
 #include "chromeos/crosapi/mojom/sync.mojom.h"
 #include "chromeos/crosapi/mojom/system_display.mojom.h"
@@ -206,7 +207,7 @@
   return {T::Uuid_, T::Version_};
 }
 
-static_assert(crosapi::mojom::Crosapi::Version_ == 80,
+static_assert(crosapi::mojom::Crosapi::Version_ == 81,
               "If you add a new crosapi, please add it to "
               "kInterfaceVersionEntries below.");
 
@@ -273,6 +274,7 @@
     MakeInterfaceVersionEntry<crosapi::mojom::ScreenManager>(),
     MakeInterfaceVersionEntry<crosapi::mojom::SearchControllerRegistry>(),
     MakeInterfaceVersionEntry<crosapi::mojom::Sharesheet>(),
+    MakeInterfaceVersionEntry<crosapi::mojom::SpeechRecognition>(),
     MakeInterfaceVersionEntry<crosapi::mojom::StructuredMetricsService>(),
     MakeInterfaceVersionEntry<crosapi::mojom::SnapshotCapturer>(),
     MakeInterfaceVersionEntry<crosapi::mojom::SyncService>(),
diff --git a/chrome/browser/ash/crosapi/speech_recognition_ash.cc b/chrome/browser/ash/crosapi/speech_recognition_ash.cc
new file mode 100644
index 0000000..fa50b63a
--- /dev/null
+++ b/chrome/browser/ash/crosapi/speech_recognition_ash.cc
@@ -0,0 +1,45 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/crosapi/speech_recognition_ash.h"
+
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/speech/cros_speech_recognition_service_factory.h"
+#include "chrome/browser/speech/speech_recognition_client_browser_interface.h"
+#include "chrome/browser/speech/speech_recognition_client_browser_interface_factory.h"
+#include "chrome/browser/speech/speech_recognition_service.h"
+#include "components/live_caption/caption_util.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+
+namespace crosapi {
+
+SpeechRecognitionAsh::SpeechRecognitionAsh() = default;
+SpeechRecognitionAsh::~SpeechRecognitionAsh() = default;
+
+void SpeechRecognitionAsh::BindReceiver(
+    mojo::PendingReceiver<mojom::SpeechRecognition> pending_receiver) {
+  receivers_.Add(this, std::move(pending_receiver));
+}
+
+void SpeechRecognitionAsh::BindSpeechRecognitionContext(
+    mojo::PendingReceiver<media::mojom::SpeechRecognitionContext> receiver) {
+  if (captions::IsLiveCaptionFeatureSupported()) {
+    CrosSpeechRecognitionServiceFactory::GetForProfile(
+        ProfileManager::GetPrimaryUserProfile())
+        ->BindSpeechRecognitionContext(std::move(receiver));
+  }
+}
+
+void SpeechRecognitionAsh::BindSpeechRecognitionClientBrowserInterface(
+    mojo::PendingReceiver<media::mojom::SpeechRecognitionClientBrowserInterface>
+        receiver) {
+  if (captions::IsLiveCaptionFeatureSupported()) {
+    SpeechRecognitionClientBrowserInterfaceFactory::GetForProfile(
+        ProfileManager::GetPrimaryUserProfile())
+        ->BindReceiver(std::move(receiver));
+  }
+}
+
+}  // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/speech_recognition_ash.h b/chrome/browser/ash/crosapi/speech_recognition_ash.h
new file mode 100644
index 0000000..685ffeb
--- /dev/null
+++ b/chrome/browser/ash/crosapi/speech_recognition_ash.h
@@ -0,0 +1,38 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_CROSAPI_SPEECH_RECOGNITION_ASH_H_
+#define CHROME_BROWSER_ASH_CROSAPI_SPEECH_RECOGNITION_ASH_H_
+
+#include "chromeos/crosapi/mojom/speech_recognition.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+
+namespace crosapi {
+
+class SpeechRecognitionAsh : public mojom::SpeechRecognition {
+ public:
+  SpeechRecognitionAsh();
+  SpeechRecognitionAsh(const SpeechRecognitionAsh&) = delete;
+  SpeechRecognitionAsh& operator=(const SpeechRecognitionAsh&) = delete;
+  ~SpeechRecognitionAsh() override;
+
+  void BindReceiver(mojo::PendingReceiver<mojom::SpeechRecognition> receiver);
+
+  // mojom::SpeechRecognition:
+  void BindSpeechRecognitionContext(
+      mojo::PendingReceiver<media::mojom::SpeechRecognitionContext> receiver)
+      override;
+  void BindSpeechRecognitionClientBrowserInterface(
+      mojo::PendingReceiver<
+          media::mojom::SpeechRecognitionClientBrowserInterface> receiver)
+      override;
+
+ private:
+  mojo::ReceiverSet<mojom::SpeechRecognition> receivers_;
+};
+
+}  // namespace crosapi
+
+#endif  // CHROME_BROWSER_ASH_CROSAPI_SPEECH_RECOGNITION_ASH_H_
diff --git a/chrome/browser/ash/crostini/crostini_installer.cc b/chrome/browser/ash/crostini/crostini_installer.cc
index 07f8848..644eb11 100644
--- a/chrome/browser/ash/crostini/crostini_installer.cc
+++ b/chrome/browser/ash/crostini/crostini_installer.cc
@@ -318,10 +318,8 @@
       LOG(ERROR) << "Network connection dropped while downloading cros-termina";
       HandleError(InstallerError::kErrorOffline);
     } else if (result == CrostiniResult::NEED_UPDATE) {
-      LOG(ERROR) << "Need to update device before installing termina-dlc";
       HandleError(InstallerError::kNeedUpdate);
     } else {
-      LOG(ERROR) << "Failed to install the cros-termina component";
       HandleError(InstallerError::kErrorLoadingTermina);
     }
     return;
@@ -421,7 +419,6 @@
   ansible_management_service_observation_.Reset();
 
   if (!success) {
-    LOG(ERROR) << "Failed to configure container";
     CrostiniManager::GetForProfile(profile_)->RemoveCrostini(
         kCrostiniDefaultVmName,
         base::BindOnce(
@@ -433,10 +430,6 @@
 
 void CrostiniInstaller::OnCrostiniRemovedAfterConfigurationFailed(
     CrostiniResult result) {
-  if (result != CrostiniResult::SUCCESS) {
-    LOG(ERROR) << "Failed to remove Crostini after failed configuration";
-  }
-
   if (content::GetNetworkConnectionTracker()->IsOffline()) {
     LOG(ERROR) << "Network connection dropped while configuring container";
     HandleError(InstallerError::kErrorOffline);
@@ -450,8 +443,6 @@
          installing_state_ == InstallerState::kConfigureContainer);
 
   if (result != CrostiniResult::SUCCESS) {
-    LOG(ERROR) << "Failed to start container with error code: "
-               << static_cast<int>(result);
     HandleError(InstallerError::kErrorStartingContainer);
     return;
   }
@@ -611,8 +602,6 @@
     if (state_ != State::ERROR && result != CrostiniResult::RESTART_ABORTED &&
         result != CrostiniResult::RESTART_REQUEST_CANCELLED) {
       DCHECK_EQ(state_, State::INSTALLING);
-      LOG(ERROR) << "Failed to restart Crostini with error code: "
-                 << static_cast<int>(result);
       HandleError(InstallerError::kErrorUnknown);
     }
     return;
diff --git a/chrome/browser/ash/crostini/crostini_manager.cc b/chrome/browser/ash/crostini/crostini_manager.cc
index 0d93613..0adcb1c8 100644
--- a/chrome/browser/ash/crostini/crostini_manager.cc
+++ b/chrome/browser/ash/crostini/crostini_manager.cc
@@ -544,7 +544,6 @@
   }
   EmitMetricIfInIncorrectState(mojom::InstallerState::kStartContainer);
   if (result != CrostiniResult::SUCCESS) {
-    LOG(ERROR) << "Failed to Start Lxd Container.";
     FinishRestart(result);
     return;
   }
@@ -753,7 +752,6 @@
     return;
   }
   if (!success) {
-    LOG(ERROR) << "Failed to Configure Lxd Container.";
     // Failed to configure, time to abort.
     FinishRestart(CrostiniResult::CONTAINER_CONFIGURATION_FAILED);
     return;
@@ -875,7 +873,6 @@
   }
   EmitMetricIfInIncorrectState(mojom::InstallerState::kCreateContainer);
   if (result != CrostiniResult::SUCCESS) {
-    LOG(ERROR) << "Failed to Create Lxd Container.";
     FinishRestart(result);
     return;
   }
@@ -916,6 +913,11 @@
 
   base::OnceClosure closure;
   if (abort_callbacks_.empty()) {
+    if (!requests_.empty() && result != CrostiniResult::SUCCESS) {
+      LOG(ERROR) << "Failed to restart Crostini with error code: "
+                 << static_cast<int>(result)
+                 << ", container: " << container_id();
+    }
     closure = ExtractRequests(
         base::BindRepeating([](const RestartRequest& request) { return true; }),
         result);
@@ -1344,10 +1346,13 @@
             if (result == TerminaInstaller::InstallResult::Success) {
               res = CrostiniResult::SUCCESS;
             } else if (result == TerminaInstaller::InstallResult::Offline) {
+              LOG(ERROR) << "Installing Termina failed: offline";
               res = CrostiniResult::OFFLINE_WHEN_UPGRADE_REQUIRED;
             } else if (result == TerminaInstaller::InstallResult::Failure) {
+              LOG(ERROR) << "Installing Termina failed";
               res = CrostiniResult::LOAD_COMPONENT_FAILED;
             } else if (result == TerminaInstaller::InstallResult::NeedUpdate) {
+              LOG(ERROR) << "Installing Termina failed: need update";
               res = CrostiniResult::NEED_UPDATE;
             } else {
               CHECK(false)
@@ -2798,9 +2803,10 @@
   if (signal.owner_id() != owner_id_)
     return;
 
+  ContainerId container(signal.vm_name(), signal.container_name());
+  LOG(ERROR) << "Container startup failed for container: " << container;
   InvokeAndErasePendingContainerCallbacks(
-      &start_container_callbacks_,
-      ContainerId(signal.vm_name(), signal.container_name()),
+      &start_container_callbacks_, container,
       CrostiniResult::CONTAINER_START_FAILED);
 }
 
@@ -3034,7 +3040,7 @@
     CrostiniResultCallback callback,
     absl::optional<vm_tools::cicerone::StartLxdContainerResponse> response) {
   if (!response) {
-    VLOG(1) << "Failed to start lxd container in vm. Empty response.";
+    LOG(ERROR) << "Failed to start lxd container in vm. Empty response.";
     std::move(callback).Run(CrostiniResult::CONTAINER_START_FAILED);
     return;
   }
@@ -3082,7 +3088,7 @@
     CrostiniResultCallback callback,
     absl::optional<vm_tools::cicerone::StopLxdContainerResponse> response) {
   if (!response) {
-    VLOG(1) << "Failed to stop lxd container in vm. Empty response.";
+    LOG(ERROR) << "Failed to stop lxd container in vm. Empty response.";
     std::move(callback).Run(CrostiniResult::CONTAINER_STOP_FAILED);
     return;
   }
@@ -3106,7 +3112,7 @@
       break;
 
     case vm_tools::cicerone::StopLxdContainerResponse::DOES_NOT_EXIST:
-      VLOG(1) << "Container does not exist " << container_id;
+      LOG(ERROR) << "Container does not exist " << container_id;
       std::move(callback).Run(CrostiniResult::CONTAINER_STOP_FAILED);
       break;
 
diff --git a/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_list.html b/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_list.html
index 86b1a9d..a0407e6 100644
--- a/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_list.html
+++ b/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_list.html
@@ -21,7 +21,8 @@
   $i18n{ambientModeTopicSourceTitle}
 </h2>
 
-<iron-list id="topicSourceList" items="[[topicSources]]">
+<iron-list id="topicSourceList" items="[[topicSources]]"
+    aria-describedby="topicSourceTitle">
   <template>
     <topic-source-item item="[[item]]" disabled$="[[disabled]]"
         tabindex$="[[computeTabIndex_(tabIndex, disabled)]]"
diff --git a/chrome/browser/segmentation_platform/segmentation_platform_config.cc b/chrome/browser/segmentation_platform/segmentation_platform_config.cc
index 230cd16..7cd5260 100644
--- a/chrome/browser/segmentation_platform/segmentation_platform_config.cc
+++ b/chrome/browser/segmentation_platform/segmentation_platform_config.cc
@@ -157,7 +157,7 @@
   auto config = std::make_unique<Config>();
   config->segmentation_key = kContextualPageActionsPriceTrackingKey;
   config->segment_ids = {
-      SegmentId::CONTEXTUAL_PAGE_ACTIONS_PRICE_TRACKING,
+      SegmentId::OPTIMIZATION_TARGET_CONTEXTUAL_PAGE_ACTION_PRICE_TRACKING,
   };
   config->on_demand_execution = true;
   config->trigger = TriggerType::kPageLoad;
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarStatePredictor.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarStatePredictor.java
index 450e8d9..afa4b617 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarStatePredictor.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarStatePredictor.java
@@ -230,7 +230,7 @@
                 return AdaptiveToolbarButtonVariant.SHARE;
             case OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
                 return AdaptiveToolbarButtonVariant.VOICE;
-            case CONTEXTUAL_PAGE_ACTIONS_PRICE_TRACKING:
+            case OPTIMIZATION_TARGET_CONTEXTUAL_PAGE_ACTION_PRICE_TRACKING:
                 return AdaptiveToolbarButtonVariant.PRICE_TRACKING;
             default:
                 return AdaptiveToolbarButtonVariant.UNKNOWN;
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc
index 20bf0aac..09492721 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc
@@ -65,20 +65,6 @@
   helper_.CheckPlatformShortcutAndIcon(Site::kSiteA);
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppIntegrationBrowserTestMacWinLinux,
-                       CheckSiteHandlesFile) {
-  helper_.InstallCreateShortcutWindowed(Site::kSiteB);
-  helper_.CheckSiteHandlesFile(Site::kSiteB, "foo");
-  helper_.CheckSiteHandlesFile(Site::kSiteB, "qux");
-}
-
-IN_PROC_BROWSER_TEST_F(WebAppIntegrationBrowserTestMacWinLinux,
-                       CheckSiteNotHandlesFile) {
-  helper_.InstallCreateShortcutWindowed(Site::kSiteA);
-  helper_.CheckSiteNotHandlesFile(Site::kSiteA, "foo");
-  helper_.CheckSiteNotHandlesFile(Site::kSiteA, "qux");
-}
-
 // Generated tests:
 
 IN_PROC_BROWSER_TEST_F(
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
index e2919a3..3bcac7a 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
@@ -32,7 +32,6 @@
 #include "chrome/browser/banners/test_app_banner_manager_desktop.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/shell_integration.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_dialogs.h"
@@ -98,7 +97,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
 #include "third_party/re2/src/re2/re2.h"
 #include "ui/accessibility/ax_action_data.h"
@@ -122,18 +120,13 @@
 #if BUILDFLAG(IS_MAC)
 #include <ImageIO/ImageIO.h>
 #include "chrome/browser/apps/app_shim/app_shim_manager_mac.h"
-#include "chrome/browser/shell_integration.h"
 #include "chrome/browser/web_applications/app_shim_registry_mac.h"
-#include "net/base/filename_util.h"
 #include "skia/ext/skia_utils_mac.h"
 #endif
 
 #if BUILDFLAG(IS_WIN)
-#include "base/test/test_reg_util_win.h"
 #include "base/win/shortcut.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/web_applications/os_integration/web_app_handler_registration_utils_win.h"
-#include "chrome/installer/util/shell_util.h"
 #endif
 
 namespace web_app::integration_tests {
@@ -300,23 +293,6 @@
 }
 
 #if BUILDFLAG(IS_WIN)
-std::vector<std::wstring> GetFileExtensionsForProgId(
-    const std::wstring& file_handler_prog_id) {
-  const std::wstring prog_id_path =
-      base::StrCat({ShellUtil::kRegClasses, L"\\", file_handler_prog_id});
-
-  // Get list of handled file extensions from value FileExtensions at
-  // HKEY_CURRENT_USER\Software\Classes\<file_handler_prog_id>.
-  base::win::RegKey file_extensions_key(HKEY_CURRENT_USER, prog_id_path.c_str(),
-                                        KEY_QUERY_VALUE);
-  std::wstring handled_file_extensions;
-  EXPECT_EQ(file_extensions_key.ReadValue(L"FileExtensions",
-                                          &handled_file_extensions),
-            ERROR_SUCCESS);
-  return base::SplitString(handled_file_extensions, std::wstring(L";"),
-                           base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-}
-
 base::FilePath GetShortcutProfile(base::FilePath shortcut_path) {
   base::FilePath shortcut_profile;
   std::wstring cmd_line_string;
@@ -568,7 +544,7 @@
 }
 
 void WebAppIntegrationTestDriver::SetUpOnMainThread() {
-  shortcut_override_ = OverrideShortcutsForTesting(base::GetHomeDir());
+  shortcut_override_ = OverrideShortcutsForTesting();
 
   // Only support manifest updates on non-sync tests, as the current
   // infrastructure here only supports listening on one profile.
@@ -1855,26 +1831,6 @@
   AfterStateCheckAction();
 }
 
-void WebAppIntegrationTestDriver::CheckSiteHandlesFile(
-    Site site,
-    std::string file_extension) {
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
-  BeforeStateCheckAction(__FUNCTION__);
-  ASSERT_TRUE(IsFileHandledBySite(site, file_extension));
-  AfterStateCheckAction();
-#endif
-}
-
-void WebAppIntegrationTestDriver::CheckSiteNotHandlesFile(
-    Site site,
-    std::string file_extension) {
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
-  BeforeStateCheckAction(__FUNCTION__);
-  ASSERT_FALSE(IsFileHandledBySite(site, file_extension));
-  AfterStateCheckAction();
-#endif
-}
-
 void WebAppIntegrationTestDriver::CheckUserCannotSetRunOnOsLogin(Site site) {
 #if !BUILDFLAG(IS_CHROMEOS)
   BeforeStateCheckAction(__FUNCTION__);
@@ -2512,49 +2468,6 @@
   return is_shortcut_and_icon_correct;
 }
 
-bool WebAppIntegrationTestDriver::IsFileHandledBySite(
-    Site site,
-    std::string file_extension) {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-  bool is_file_handled = false;
-#if BUILDFLAG(IS_WIN)
-  AppId app_id = GetAppIdBySiteMode(site);
-  const std::wstring prog_id =
-      GetProgIdForApp(browser()->profile()->GetPath(), app_id);
-  const std::vector<std::wstring> file_handler_prog_ids =
-      ShellUtil::GetFileHandlerProgIdsForAppId(prog_id);
-
-  base::win::RegKey key;
-  std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
-  for (const auto& file_handler_prog_id : file_handler_prog_ids) {
-    const std::vector<std::wstring> supported_file_extensions =
-        GetFileExtensionsForProgId(file_handler_prog_id);
-    std::wstring extension = converter.from_bytes("." + file_extension);
-    if (std::find(supported_file_extensions.begin(),
-                  supported_file_extensions.end(),
-                  extension) != supported_file_extensions.end()) {
-      const std::wstring reg_key =
-          L"Software\\Classes\\" + extension + L"\\OpenWithProgids";
-      EXPECT_EQ(ERROR_SUCCESS,
-                key.Open(HKEY_CURRENT_USER, reg_key.data(), KEY_READ));
-      return key.HasValue(file_handler_prog_id.data());
-    }
-  }
-#elif BUILDFLAG(IS_MAC)
-  std::string app_name = g_site_to_app_name.find(site)->second;
-  const base::FilePath test_file_path =
-      shortcut_override_->chrome_apps_folder.GetPath().AppendASCII(
-          "test." + file_extension);
-  const base::File test_file(
-      test_file_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
-  const GURL test_file_url = net::FilePathToFileURL(test_file_path);
-  is_file_handled =
-      (base::UTF8ToUTF16(app_name) ==
-       shell_integration::GetApplicationNameForProtocol(test_file_url));
-#endif
-  return is_file_handled;
-}
-
 void WebAppIntegrationTestDriver::SetRunOnOsLoginMode(
     Site site,
     apps::RunOnOsLoginMode login_mode) {
@@ -2629,7 +2542,6 @@
   enabled_features.push_back(features::kPwaUpdateDialogForName);
   enabled_features.push_back(features::kDesktopPWAsEnforceWebAppSettingsPolicy);
   enabled_features.push_back(features::kWebAppWindowControlsOverlay);
-  enabled_features.push_back(blink::features::kFileHandlingAPI);
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   disabled_features.push_back(features::kWebAppsCrosapi);
   disabled_features.push_back(chromeos::features::kLacrosPrimary);
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
index 30a283fc..b68a41d 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
@@ -261,8 +261,6 @@
   void CheckPlatformShortcutNotExists(Site site);
   void CheckRunOnOsLoginEnabled(Site site);
   void CheckRunOnOsLoginDisabled(Site site);
-  void CheckSiteHandlesFile(Site site, std::string file_extension);
-  void CheckSiteNotHandlesFile(Site site, std::string file_extension);
   void CheckUserCannotSetRunOnOsLogin(Site site);
   void CheckUserDisplayModeInternal(UserDisplayMode user_display_mode);
   void CheckWindowClosed();
@@ -334,8 +332,6 @@
                                 const std::string& name,
                                 const AppId& id);
 
-  bool IsFileHandledBySite(Site site, std::string file_extension);
-
   void SetRunOnOsLoginMode(Site site, apps::RunOnOsLoginMode login_mode);
 
   void LaunchAppStartupBrowserCreator(const AppId& app_id);
diff --git a/chrome/browser/webapps/web_app_offline_browsertest.cc b/chrome/browser/webapps/web_app_offline_browsertest.cc
index 880c3e8..983c85a 100644
--- a/chrome/browser/webapps/web_app_offline_browsertest.cc
+++ b/chrome/browser/webapps/web_app_offline_browsertest.cc
@@ -29,6 +29,10 @@
 #include "chromeos/constants/chromeos_features.h"
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ash/constants/ash_features.h"
+#endif
+
 namespace web_app {
 
 enum class PageFlagParam {
@@ -214,13 +218,18 @@
       public testing::WithParamInterface<blink::mojom::PreferredColorScheme> {
  public:
   WebAppOfflineDarkModeTest() {
+    std::vector<base::Feature> disabled_features;
+#if BUILDFLAG(IS_CHROMEOS)
+    disabled_features.push_back(chromeos::features::kDarkLightMode);
+#endif
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    disabled_features.push_back(ash::features::kNotificationsRefresh);
+#endif
+
     feature_list_.InitWithFeatures({features::kDesktopPWAsDefaultOfflinePage,
                                     blink::features::kWebAppEnableDarkMode},
-                                   {
-#if BUILDFLAG(IS_CHROMEOS)
-                                     chromeos::features::kDarkLightMode
-#endif
-                                   });
+                                   {disabled_features});
   }
 
   void SetUp() override {
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index a5db090..3e69c5b 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1654635373-99ce6ee7d2689b2563f41e4dbe77883a04442548.profdata
+chrome-win32-main-1654646316-cc64f043d6eaf00ceb77f0b06fbef47e57b8c4a4.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 15e3f7e4..f371125b 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1654646316-1ed50d10661c4fe04520b6ca1648e02d1280c7e4.profdata
+chrome-win64-main-1654657147-f31aea24b202c703f44c852b045de09e50068779.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 2bcef5a..1e9114c 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -9325,6 +9325,7 @@
     if (is_chromeos_ash) {
       deps += [
         "//ash/components/login/auth",
+        "//ash/constants",
         "//chrome/browser/chromeos:test_support",
         "//chrome/browser/media/router:test_support",
         "//chromeos/dbus",
diff --git a/chrome/test/data/web_apps/site_b/basic.json b/chrome/test/data/web_apps/site_b/basic.json
index 0caa09a..36a63d10 100644
--- a/chrome/test/data/web_apps/site_b/basic.json
+++ b/chrome/test/data/web_apps/site_b/basic.json
@@ -20,8 +20,7 @@
       "action": "/web_apps/site_b/text_handler.html",
       "name": "Plain Text",
       "accept": {
-        "text/plain": [".txt", ".md", ".csv", ".text"],
-        "application/octet-stream": [".foo", ".qux"]
+        "text/plain": [".txt", ".md", ".csv", ".text"]
       }
     },
     {
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 5153d9e..930f7c4 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -2257,6 +2257,9 @@
       <message name="IDS_PERSONALIZATION_APP_AMBIENT_MODE_WEATHER_TITLE" desc="Label for the radio button group of ambient mode weather unit.">
         Weather
       </message>
+      <message name="IDS_PERSONALIZATION_APP_ARIA_DESCRIPTION_AMBIENT_MODE_WEATHER" desc="Aria description for the radio button group of ambient mode weather unit.">
+        Select a temperature unit
+      </message>
       <message name="IDS_PERSONALIZATION_APP_AMBIENT_MODE_WEATHER_UNIT_FAHRENHEIT" desc="Label for the radio button to choose weather unit as Fahrenheit.">
         Fahrenheit
       </message>
diff --git a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_ARIA_DESCRIPTION_AMBIENT_MODE_WEATHER.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_ARIA_DESCRIPTION_AMBIENT_MODE_WEATHER.png.sha1
new file mode 100644
index 0000000..19facc2
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_ARIA_DESCRIPTION_AMBIENT_MODE_WEATHER.png.sha1
@@ -0,0 +1 @@
+e869e2ff73a973db4900be862eb68857561e54cf
\ No newline at end of file
diff --git a/chromeos/crosapi/mojom/BUILD.gn b/chromeos/crosapi/mojom/BUILD.gn
index 4155aea..45b34101 100644
--- a/chromeos/crosapi/mojom/BUILD.gn
+++ b/chromeos/crosapi/mojom/BUILD.gn
@@ -67,6 +67,7 @@
     "screen_manager.mojom",
     "select_file.mojom",
     "sharesheet.mojom",
+    "speech_recognition.mojom",
     "structured_metrics_service.mojom",
     "sync.mojom",
     "system_display.mojom",
@@ -93,6 +94,7 @@
     "//components/services/app_service/public/mojom:mojom",
     "//media/capture/mojom:image_capture",
     "//media/capture/mojom:video_capture_types",
+    "//media/mojo/mojom:speech_recognition",
     "//mojo/public/mojom/base",
     "//printing/backend/mojom",
     "//printing/mojom",
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index fa6a2473..372d6f7 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -60,6 +60,7 @@
 import "chromeos/crosapi/mojom/screen_manager.mojom";
 import "chromeos/crosapi/mojom/select_file.mojom";
 import "chromeos/crosapi/mojom/sharesheet.mojom";
+import "chromeos/crosapi/mojom/speech_recognition.mojom";
 import "chromeos/crosapi/mojom/structured_metrics_service.mojom";
 import "chromeos/crosapi/mojom/sync.mojom";
 import "chromeos/crosapi/mojom/system_display.mojom";
@@ -111,8 +112,8 @@
 // please note the milestone when you added it, to help us reason about
 // compatibility between the client applications and older ash-chrome binaries.
 //
-// Next version: 81
-// Next method id: 84
+// Next version: 82
+// Next method id: 85
 [Stable, Uuid="8b79c34f-2bf8-4499-979a-b17cac522c1e",
  RenamedFrom="crosapi.mojom.AshChromeService"]
 interface Crosapi {
@@ -407,6 +408,12 @@
   BindSharesheet@70(
       pending_receiver<Sharesheet> receiver);
 
+  // Binds the SpeechRecognition interface to access on-device speech
+  // recognition.
+  // Added in M104.
+  [MinVersion=81]
+  BindSpeechRecognition@84(pending_receiver<SpeechRecognition> receiver);
+
   // Binds the StableVideoDecoderFactory, which allows lacros-chrome to request
   // hardware accelerated video decoding. We need to use a
   // GenericPendingReceiver to avoid dependency circularities.
diff --git a/chromeos/crosapi/mojom/speech_recognition.mojom b/chromeos/crosapi/mojom/speech_recognition.mojom
new file mode 100644
index 0000000..2091641
--- /dev/null
+++ b/chromeos/crosapi/mojom/speech_recognition.mojom
@@ -0,0 +1,22 @@
+// Copyright 2022 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.
+
+module crosapi.mojom;
+
+import "media/mojo/mojom/speech_recognition.mojom";
+
+// Allows LaCrOS to access speech recognition provided by Ash, to power features
+// such as Live Caption.
+[Stable, Uuid="c881b018-a0a5-4d2e-9bfe-1f0cc9bd78bd"]
+interface SpeechRecognition {
+  // Binds a speech recognition engine.
+  BindSpeechRecognitionContext@0(
+      pending_receiver<media.mojom.SpeechRecognitionContext> receiver);
+
+  // Binds a browser interface for renderers to use to react to changing
+  // availability of speech recognition.
+  BindSpeechRecognitionClientBrowserInterface@1(
+      pending_receiver<media.mojom.SpeechRecognitionClientBrowserInterface>
+      receiver);
+};
diff --git a/components/optimization_guide/core/model_util.cc b/components/optimization_guide/core/model_util.cc
index eb4c4afd..eb3ab99 100644
--- a/components/optimization_guide/core/model_util.cc
+++ b/components/optimization_guide/core/model_util.cc
@@ -69,6 +69,8 @@
       return "SegmentationChromeLowUserEngagement";
     case proto::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER:
       return "SegmentationFeedUser";
+    case proto::OPTIMIZATION_TARGET_CONTEXTUAL_PAGE_ACTION_PRICE_TRACKING:
+      return "ContextualPageActionPriceTracking";
       // Whenever a new value is added, make sure to add it to the OptTarget
       // variant list in
       // //tools/metrics/histograms/metadata/optimization/histograms.xml.
diff --git a/components/optimization_guide/proto/models.proto b/components/optimization_guide/proto/models.proto
index 28b705ad..f93899b 100644
--- a/components/optimization_guide/proto/models.proto
+++ b/components/optimization_guide/proto/models.proto
@@ -277,6 +277,9 @@
   OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT = 16;
   // Target for segmentation: Determine users who prefer to use Feed.
   OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER = 17;
+  // Target for segmentation: Determine whether price tracking should be shown
+  // as a contextual page action.
+  OPTIMIZATION_TARGET_CONTEXTUAL_PAGE_ACTION_PRICE_TRACKING = 18;
 }
 
 // The model engine versions that can be used to do model inference.
diff --git a/components/segmentation_platform/public/proto/segmentation_platform.proto b/components/segmentation_platform/public/proto/segmentation_platform.proto
index 246105ba..99c9b17 100644
--- a/components/segmentation_platform/public/proto/segmentation_platform.proto
+++ b/components/segmentation_platform/public/proto/segmentation_platform.proto
@@ -51,11 +51,11 @@
   OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT = 16;
   // Target for segmentation: Determine users who prefer to use Feed.
   OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER = 17;
+  // Target for price tracking action when shown as a contextual page action.
+  OPTIMIZATION_TARGET_CONTEXTUAL_PAGE_ACTION_PRICE_TRACKING = 18;
   // Add new entries to OptimizationTarget proto.
 
   // New entries should start from a 1000 if OptimizationTarget does not have a
   // corresponding type.
   MAX_OPTIMIZATION_TARGET = 999;
-  // Target for price tracking action when shown as a contextual page action.
-  CONTEXTUAL_PAGE_ACTIONS_PRICE_TRACKING = 1000;
 };
diff --git a/components/translate/ios/browser/language_detection_controller.h b/components/translate/ios/browser/language_detection_controller.h
index da3a272..c5c8701 100644
--- a/components/translate/ios/browser/language_detection_controller.h
+++ b/components/translate/ios/browser/language_detection_controller.h
@@ -25,6 +25,9 @@
 class NavigationContext;
 }
 
+FORWARD_DECLARE_TEST(ChromeIOSTranslateClientTest,
+                     TFLiteLanguageDetectionDurationRecorded);
+
 namespace translate {
 
 class LanguageDetectionModel;
@@ -46,6 +49,9 @@
   ~LanguageDetectionController() override;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(::ChromeIOSTranslateClientTest,
+                           TFLiteLanguageDetectionDurationRecorded);
+
   // Starts the page language detection and initiates the translation process.
   void StartLanguageDetection();
 
diff --git a/components/translate/ios/browser/language_detection_controller.mm b/components/translate/ios/browser/language_detection_controller.mm
index cd4fb32b..f345b4e1 100644
--- a/components/translate/ios/browser/language_detection_controller.mm
+++ b/components/translate/ios/browser/language_detection_controller.mm
@@ -13,6 +13,7 @@
 #include "base/notreached.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
+#include "base/timer/elapsed_timer.h"
 #include "components/language/ios/browser/ios_language_detection_tab_helper.h"
 #include "components/prefs/pref_member.h"
 #include "components/translate/core/browser/translate_pref_names.h"
@@ -36,6 +37,9 @@
 namespace {
 // Name for the UMA metric used to track text extraction time.
 const char kTranslateCaptureText[] = "Translate.CaptureText";
+// Name for the UMA metric used to track language detection evaluation duration.
+const char kTranslateLanguageDetectionTFLiteModelEvaluationDuration[] =
+    "Translate.LanguageDetection.TFLiteModelEvaluationDuration";
 // Prefix for the language detection javascript commands. Must be kept in sync
 // with language_detection.js.
 const char kCommandPrefix[] = "languageDetection";
@@ -160,10 +164,14 @@
         LanguageDetectionMethod::kTFLiteModelUsed);
     // TODO(crbug/1309448): Remove logging
     NSLog(@"LanguageDetectionController: Using TFLite language detection.");
+    base::ElapsedTimer timer;
     language = language_detection_model_->DeterminePageLanguage(
         http_content_language, html_lang,
         GetStringByClippingLastWord(text, kMaxIndexChars),
         &model_detected_language, &is_model_reliable, model_reliability_score);
+    base::UmaHistogramTimes(
+        kTranslateLanguageDetectionTFLiteModelEvaluationDuration,
+        timer.Elapsed());
     detection_model_version = language_detection_model_->GetModelVersion();
   } else {
     if (IsTFLiteLanguageDetectionEnabled()) {
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 24a9541..47eb313 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -390,12 +390,12 @@
     "attribution_reporting/aggregatable_attribution_utils.h",
     "attribution_reporting/aggregatable_histogram_contribution.cc",
     "attribution_reporting/aggregatable_histogram_contribution.h",
-    "attribution_reporting/attribution_aggregatable_source.cc",
-    "attribution_reporting/attribution_aggregatable_source.h",
     "attribution_reporting/attribution_aggregatable_trigger_data.cc",
     "attribution_reporting/attribution_aggregatable_trigger_data.h",
     "attribution_reporting/attribution_aggregatable_values.cc",
     "attribution_reporting/attribution_aggregatable_values.h",
+    "attribution_reporting/attribution_aggregation_keys.cc",
+    "attribution_reporting/attribution_aggregation_keys.h",
     "attribution_reporting/attribution_cookie_checker.h",
     "attribution_reporting/attribution_cookie_checker_impl.cc",
     "attribution_reporting/attribution_cookie_checker_impl.h",
diff --git a/content/browser/attribution_reporting/aggregatable_attribution_utils.cc b/content/browser/attribution_reporting/aggregatable_attribution_utils.cc
index 2ae6c9d..b1d185e 100644
--- a/content/browser/attribution_reporting/aggregatable_attribution_utils.cc
+++ b/content/browser/attribution_reporting/aggregatable_attribution_utils.cc
@@ -17,9 +17,9 @@
 #include "base/values.h"
 #include "content/browser/aggregation_service/aggregatable_report.h"
 #include "content/browser/attribution_reporting/aggregatable_histogram_contribution.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
+#include "content/browser/attribution_reporting/attribution_aggregation_keys.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_info.h"
 #include "content/browser/attribution_reporting/attribution_report.h"
@@ -49,13 +49,13 @@
 
 std::vector<AggregatableHistogramContribution> CreateAggregatableHistogram(
     const AttributionFilterData& source_filter_data,
-    const AttributionAggregatableSource& source,
+    const AttributionAggregationKeys& keys,
     const std::vector<AttributionAggregatableTriggerData>&
         aggregatable_trigger_data,
     const AttributionAggregatableValues& aggregatable_values) {
   int num_trigger_data_filtered = 0;
 
-  AttributionAggregatableSource::Keys buckets = source.keys();
+  AttributionAggregationKeys::Keys buckets = keys.keys();
 
   // For each piece of trigger data specified, check if its filters/not_filters
   // match for the given source, and if applicable modify the bucket based on
@@ -101,7 +101,7 @@
 
   const int kExclusiveMaxHistogramValue = 101;
 
-  static_assert(blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger <
+  static_assert(blink::kMaxAttributionAggregationKeysPerSourceOrTrigger <
                     kExclusiveMaxHistogramValue,
                 "Bump the version for histogram "
                 "Conversions.AggregatableReport.NumContributionsPerReport");
@@ -113,7 +113,7 @@
   return contributions;
 }
 
-std::string HexEncodeAggregatableKey(absl::uint128 value) {
+std::string HexEncodeAggregationKey(absl::uint128 value) {
   std::ostringstream out;
   out << "0x";
   out.setf(out.hex, out.basefield);
diff --git a/content/browser/attribution_reporting/aggregatable_attribution_utils.h b/content/browser/attribution_reporting/aggregatable_attribution_utils.h
index 19ab461..b2d0b6b 100644
--- a/content/browser/attribution_reporting/aggregatable_attribution_utils.h
+++ b/content/browser/attribution_reporting/aggregatable_attribution_utils.h
@@ -19,7 +19,7 @@
 
 class AggregatableHistogramContribution;
 class AggregatableReportRequest;
-class AttributionAggregatableSource;
+class AttributionAggregationKeys;
 class AttributionAggregatableTriggerData;
 class AttributionAggregatableValues;
 class AttributionFilterData;
@@ -29,14 +29,14 @@
 CONTENT_EXPORT std::vector<AggregatableHistogramContribution>
 CreateAggregatableHistogram(
     const AttributionFilterData& source_filter_data,
-    const AttributionAggregatableSource& source,
+    const AttributionAggregationKeys& keys,
     const std::vector<AttributionAggregatableTriggerData>&
         aggregatable_trigger_data,
     const AttributionAggregatableValues& aggregatable_values);
 
 // Returns a hex string representation of the 128-bit aggregatable key in big
 // endian order.
-CONTENT_EXPORT std::string HexEncodeAggregatableKey(absl::uint128 value);
+CONTENT_EXPORT std::string HexEncodeAggregationKey(absl::uint128 value);
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
diff --git a/content/browser/attribution_reporting/aggregatable_attribution_utils_unittest.cc b/content/browser/attribution_reporting/aggregatable_attribution_utils_unittest.cc
index 8ac91ac..e012725 100644
--- a/content/browser/attribution_reporting/aggregatable_attribution_utils_unittest.cc
+++ b/content/browser/attribution_reporting/aggregatable_attribution_utils_unittest.cc
@@ -15,9 +15,9 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "content/browser/attribution_reporting/aggregatable_histogram_contribution.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
+#include "content/browser/attribution_reporting/attribution_aggregation_keys.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.h"
@@ -38,7 +38,7 @@
 TEST(AggregatableAttributionUtilsTest, CreateAggregatableHistogram) {
   base::HistogramTester histograms;
 
-  auto source = AttributionAggregatableSource::FromKeys(
+  auto source = AttributionAggregationKeys::FromKeys(
       {{"key1", 345}, {"key2", 5}, {"key3", 123}});
   ASSERT_TRUE(source.has_value());
 
@@ -102,7 +102,7 @@
       "Conversions.AggregatableReport.NumContributionsPerReport", 2, 1);
 }
 
-TEST(AggregatableAttributionUtilsTest, HexEncodeAggregatableKey) {
+TEST(AggregatableAttributionUtilsTest, HexEncodeAggregationKey) {
   const struct {
     absl::uint128 input;
     std::string output;
@@ -119,7 +119,7 @@
   };
 
   for (const auto& test_case : kTestCases) {
-    EXPECT_EQ(HexEncodeAggregatableKey(test_case.input), test_case.output)
+    EXPECT_EQ(HexEncodeAggregationKey(test_case.input), test_case.output)
         << test_case.input;
   }
 }
@@ -128,7 +128,7 @@
      NoTriggerData_FilteredPercentageNotRecorded) {
   base::HistogramTester histograms;
 
-  auto source = AttributionAggregatableSource::FromKeys({{"key1", 345}});
+  auto source = AttributionAggregationKeys::FromKeys({{"key1", 345}});
   ASSERT_TRUE(source.has_value());
 
   std::vector<AggregatableHistogramContribution> contributions =
diff --git a/content/browser/attribution_reporting/attribution_aggregatable_source.cc b/content/browser/attribution_reporting/attribution_aggregatable_source.cc
deleted file mode 100644
index 69d048e..0000000
--- a/content/browser/attribution_reporting/attribution_aggregatable_source.cc
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
-
-#include <string>
-#include <utility>
-
-#include "base/check.h"
-#include "base/ranges/algorithm.h"
-#include "content/browser/attribution_reporting/attribution_reporting.pb.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/public/common/attribution_reporting/constants.h"
-
-namespace content {
-
-// static
-absl::optional<AttributionAggregatableSource>
-AttributionAggregatableSource::FromKeys(Keys keys) {
-  bool is_valid =
-      keys.size() <= blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger &&
-      base::ranges::all_of(keys, [](const auto& key) {
-        return key.first.size() <=
-               blink::kMaxBytesPerAttributionAggregatableKeyId;
-      });
-  return is_valid ? absl::make_optional(
-                        AttributionAggregatableSource(std::move(keys)))
-                  : absl::nullopt;
-}
-
-// static
-absl::optional<AttributionAggregatableSource>
-AttributionAggregatableSource::Deserialize(const std::string& str) {
-  proto::AttributionAggregatableSource msg;
-  if (!msg.ParseFromString(str))
-    return absl::nullopt;
-
-  Keys::container_type keys;
-  keys.reserve(msg.keys().size());
-
-  for (const auto& [id, key] : msg.keys()) {
-    if (!key.has_high_bits() || !key.has_low_bits())
-      return absl::nullopt;
-
-    keys.emplace_back(id, absl::MakeUint128(key.high_bits(), key.low_bits()));
-  }
-
-  return FromKeys(std::move(keys));
-}
-
-AttributionAggregatableSource::AttributionAggregatableSource(Keys keys)
-    : keys_(std::move(keys)) {}
-
-AttributionAggregatableSource::AttributionAggregatableSource() = default;
-
-AttributionAggregatableSource::~AttributionAggregatableSource() = default;
-
-AttributionAggregatableSource::AttributionAggregatableSource(
-    const AttributionAggregatableSource&) = default;
-
-AttributionAggregatableSource::AttributionAggregatableSource(
-    AttributionAggregatableSource&&) = default;
-
-AttributionAggregatableSource& AttributionAggregatableSource::operator=(
-    const AttributionAggregatableSource&) = default;
-
-AttributionAggregatableSource& AttributionAggregatableSource::operator=(
-    AttributionAggregatableSource&&) = default;
-
-std::string AttributionAggregatableSource::Serialize() const {
-  proto::AttributionAggregatableSource msg;
-
-  for (const auto& [id, key] : keys_) {
-    proto::AttributionAggregatableKey key_msg;
-    key_msg.set_high_bits(absl::Uint128High64(key));
-    key_msg.set_low_bits(absl::Uint128Low64(key));
-    (*msg.mutable_keys())[id] = std::move(key_msg);
-  }
-
-  std::string str;
-  bool success = msg.SerializeToString(&str);
-  DCHECK(success);
-  return str;
-}
-
-}  // namespace content
diff --git a/content/browser/attribution_reporting/attribution_aggregatable_source.h b/content/browser/attribution_reporting/attribution_aggregatable_source.h
deleted file mode 100644
index bc2a9b12..0000000
--- a/content/browser/attribution_reporting/attribution_aggregatable_source.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_AGGREGATABLE_SOURCE_H_
-#define CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_AGGREGATABLE_SOURCE_H_
-
-#include <string>
-
-#include "base/containers/flat_map.h"
-#include "content/common/content_export.h"
-#include "third_party/abseil-cpp/absl/numeric/int128.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-namespace content {
-
-// This is a wrapper of `proto::AttributionAggregatableSource`.
-class CONTENT_EXPORT AttributionAggregatableSource {
- public:
-  using Keys = base::flat_map<std::string, absl::uint128>;
-
-  // Returns `absl::nullopt` if `keys` is invalid.
-  static absl::optional<AttributionAggregatableSource> FromKeys(Keys keys);
-
-  // Deserializes `str`, if valid. Returns `absl::nullopt` if not.
-  static absl::optional<AttributionAggregatableSource> Deserialize(
-      const std::string& str);
-
-  AttributionAggregatableSource();
-  ~AttributionAggregatableSource();
-
-  AttributionAggregatableSource(const AttributionAggregatableSource&);
-  AttributionAggregatableSource(AttributionAggregatableSource&&);
-
-  AttributionAggregatableSource& operator=(
-      const AttributionAggregatableSource&);
-  AttributionAggregatableSource& operator=(AttributionAggregatableSource&&);
-
-  const Keys& keys() const { return keys_; }
-
-  std::string Serialize() const;
-
- private:
-  explicit AttributionAggregatableSource(Keys keys);
-
-  Keys keys_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_AGGREGATABLE_SOURCE_H_
diff --git a/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.cc b/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.cc
index fbc374b..693a44cc 100644
--- a/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.cc
+++ b/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.cc
@@ -20,12 +20,12 @@
     AttributionFilterData filters,
     AttributionFilterData not_filters) {
   if (source_keys.size() >
-      blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger) {
+      blink::kMaxAttributionAggregationKeysPerSourceOrTrigger) {
     return absl::nullopt;
   }
 
   bool is_valid = base::ranges::all_of(source_keys, [](const auto& key) {
-    return key.size() <= blink::kMaxBytesPerAttributionAggregatableKeyId;
+    return key.size() <= blink::kMaxBytesPerAttributionAggregationKeyId;
   });
   if (!is_valid)
     return absl::nullopt;
diff --git a/content/browser/attribution_reporting/attribution_aggregatable_values.cc b/content/browser/attribution_reporting/attribution_aggregatable_values.cc
index 32c31b5..efcbbaf 100644
--- a/content/browser/attribution_reporting/attribution_aggregatable_values.cc
+++ b/content/browser/attribution_reporting/attribution_aggregatable_values.cc
@@ -15,14 +15,13 @@
 // static
 absl::optional<AttributionAggregatableValues>
 AttributionAggregatableValues::FromValues(Values values) {
-  if (values.size() >
-      blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger) {
+  if (values.size() > blink::kMaxAttributionAggregationKeysPerSourceOrTrigger) {
     return absl::nullopt;
   }
 
   bool is_valid = base::ranges::all_of(values, [](const auto& value) {
     return value.first.size() <=
-               blink::kMaxBytesPerAttributionAggregatableKeyId &&
+               blink::kMaxBytesPerAttributionAggregationKeyId &&
            value.second > 0 &&
            value.second <= blink::kMaxAttributionAggregatableValue;
   });
diff --git a/content/browser/attribution_reporting/attribution_aggregation_keys.cc b/content/browser/attribution_reporting/attribution_aggregation_keys.cc
new file mode 100644
index 0000000..6ac791e
--- /dev/null
+++ b/content/browser/attribution_reporting/attribution_aggregation_keys.cc
@@ -0,0 +1,87 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/attribution_reporting/attribution_aggregation_keys.h"
+
+#include <string>
+#include <utility>
+
+#include "base/check.h"
+#include "base/ranges/algorithm.h"
+#include "content/browser/attribution_reporting/attribution_reporting.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/attribution_reporting/constants.h"
+
+namespace content {
+
+// static
+absl::optional<AttributionAggregationKeys> AttributionAggregationKeys::FromKeys(
+    Keys keys) {
+  bool is_valid =
+      keys.size() <= blink::kMaxAttributionAggregationKeysPerSourceOrTrigger &&
+      base::ranges::all_of(keys, [](const auto& key) {
+        return key.first.size() <=
+               blink::kMaxBytesPerAttributionAggregationKeyId;
+      });
+  return is_valid
+             ? absl::make_optional(AttributionAggregationKeys(std::move(keys)))
+             : absl::nullopt;
+}
+
+// static
+absl::optional<AttributionAggregationKeys>
+AttributionAggregationKeys::Deserialize(const std::string& str) {
+  proto::AttributionAggregatableSource msg;
+  if (!msg.ParseFromString(str))
+    return absl::nullopt;
+
+  Keys::container_type keys;
+  keys.reserve(msg.keys().size());
+
+  for (const auto& [id, key] : msg.keys()) {
+    if (!key.has_high_bits() || !key.has_low_bits())
+      return absl::nullopt;
+
+    keys.emplace_back(id, absl::MakeUint128(key.high_bits(), key.low_bits()));
+  }
+
+  return FromKeys(std::move(keys));
+}
+
+AttributionAggregationKeys::AttributionAggregationKeys(Keys keys)
+    : keys_(std::move(keys)) {}
+
+AttributionAggregationKeys::AttributionAggregationKeys() = default;
+
+AttributionAggregationKeys::~AttributionAggregationKeys() = default;
+
+AttributionAggregationKeys::AttributionAggregationKeys(
+    const AttributionAggregationKeys&) = default;
+
+AttributionAggregationKeys::AttributionAggregationKeys(
+    AttributionAggregationKeys&&) = default;
+
+AttributionAggregationKeys& AttributionAggregationKeys::operator=(
+    const AttributionAggregationKeys&) = default;
+
+AttributionAggregationKeys& AttributionAggregationKeys::operator=(
+    AttributionAggregationKeys&&) = default;
+
+std::string AttributionAggregationKeys::Serialize() const {
+  proto::AttributionAggregatableSource msg;
+
+  for (const auto& [id, key] : keys_) {
+    proto::AttributionAggregationKey key_msg;
+    key_msg.set_high_bits(absl::Uint128High64(key));
+    key_msg.set_low_bits(absl::Uint128Low64(key));
+    (*msg.mutable_keys())[id] = std::move(key_msg);
+  }
+
+  std::string str;
+  bool success = msg.SerializeToString(&str);
+  DCHECK(success);
+  return str;
+}
+
+}  // namespace content
diff --git a/content/browser/attribution_reporting/attribution_aggregation_keys.h b/content/browser/attribution_reporting/attribution_aggregation_keys.h
new file mode 100644
index 0000000..f455266
--- /dev/null
+++ b/content/browser/attribution_reporting/attribution_aggregation_keys.h
@@ -0,0 +1,49 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_AGGREGATION_KEYS_H_
+#define CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_AGGREGATION_KEYS_H_
+
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "content/common/content_export.h"
+#include "third_party/abseil-cpp/absl/numeric/int128.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace content {
+
+class CONTENT_EXPORT AttributionAggregationKeys {
+ public:
+  using Keys = base::flat_map<std::string, absl::uint128>;
+
+  // Returns `absl::nullopt` if `keys` is invalid.
+  static absl::optional<AttributionAggregationKeys> FromKeys(Keys keys);
+
+  // Deserializes `str`, if valid. Returns `absl::nullopt` if not.
+  static absl::optional<AttributionAggregationKeys> Deserialize(
+      const std::string& str);
+
+  AttributionAggregationKeys();
+  ~AttributionAggregationKeys();
+
+  AttributionAggregationKeys(const AttributionAggregationKeys&);
+  AttributionAggregationKeys(AttributionAggregationKeys&&);
+
+  AttributionAggregationKeys& operator=(const AttributionAggregationKeys&);
+  AttributionAggregationKeys& operator=(AttributionAggregationKeys&&);
+
+  const Keys& keys() const { return keys_; }
+
+  std::string Serialize() const;
+
+ private:
+  explicit AttributionAggregationKeys(Keys keys);
+
+  Keys keys_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_AGGREGATION_KEYS_H_
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
index 8409c06..586bc6d9 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
@@ -14,9 +14,9 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
+#include "content/browser/attribution_reporting/attribution_aggregation_keys.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_manager.h"
 #include "content/browser/attribution_reporting/attribution_source_type.h"
@@ -319,10 +319,9 @@
     return;
   }
 
-  absl::optional<AttributionAggregatableSource> aggregatable_source =
-      AttributionAggregatableSource::FromKeys(
-          std::move(data->aggregation_keys));
-  if (!aggregatable_source.has_value()) {
+  absl::optional<AttributionAggregationKeys> aggregation_keys =
+      AttributionAggregationKeys::FromKeys(std::move(data->aggregation_keys));
+  if (!aggregation_keys.has_value()) {
     RecordSourceDataHandleStatus(DataHandleStatus::kInvalidData);
     mojo::ReportBadMessage("AttributionDataHost: Invalid aggregatable source.");
     return;
@@ -341,7 +340,7 @@
       context.source_type, data->priority, std::move(*filter_data),
       data->debug_key ? absl::make_optional(data->debug_key->value)
                       : absl::nullopt,
-      std::move(*aggregatable_source)));
+      std::move(*aggregation_keys)));
 
   attribution_manager_->HandleSource(std::move(storable_source));
 }
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
index 48df41d..ebeb616b4 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
@@ -20,7 +20,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/time/time.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
+#include "content/browser/attribution_reporting/attribution_aggregation_keys.h"
 #include "content/browser/attribution_reporting/attribution_manager.h"
 #include "content/browser/attribution_reporting/attribution_source_type.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.h"
@@ -124,7 +124,7 @@
                 SourceEventIdIs(10), ConversionOriginIs(destination_origin),
                 ImpressionOriginIs(page_origin), SourcePriorityIs(20),
                 SourceDebugKeyIs(789),
-                AggregatableSourceAre(*AttributionAggregatableSource::FromKeys(
+                AggregationKeysAre(*AttributionAggregationKeys::FromKeys(
                     {{"key", absl::MakeUint128(/*high=*/5, /*low=*/345)}})))));
   {
     RemoteDataHost data_host_remote{.task_environment = task_environment_};
@@ -407,13 +407,12 @@
   const AggregatableSourceizeTestCase kTestCases[] = {
       {"empty", true, 0, 0},
       {"max_keys", true,
-       blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger, 1},
+       blink::kMaxAttributionAggregationKeysPerSourceOrTrigger, 1},
       {"too_many_keys", false,
-       blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger + 1, 1},
-      {"max_key_size", true, 1,
-       blink::kMaxBytesPerAttributionAggregatableKeyId},
+       blink::kMaxAttributionAggregationKeysPerSourceOrTrigger + 1, 1},
+      {"max_key_size", true, 1, blink::kMaxBytesPerAttributionAggregationKeyId},
       {"excessive_key_size", false, 1,
-       blink::kMaxBytesPerAttributionAggregatableKeyId + 1},
+       blink::kMaxBytesPerAttributionAggregationKeyId + 1},
   };
 
   for (auto& test_case : kTestCases) {
@@ -834,8 +833,8 @@
     size_t size;
     bool expected;
   } kTestCases[] = {
-      {blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger, true},
-      {blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger + 1, false},
+      {blink::kMaxAttributionAggregationKeysPerSourceOrTrigger, true},
+      {blink::kMaxAttributionAggregationKeysPerSourceOrTrigger + 1, false},
   };
 
   for (const auto& test_case : kTestCases) {
@@ -1043,7 +1042,7 @@
             SourceEventIdIs(10), ConversionOriginIs(destination_origin),
             ImpressionOriginIs(page_origin), SourcePriorityIs(20),
             SourceDebugKeyIs(789),
-            AggregatableSourceAre(*AttributionAggregatableSource::FromKeys(
+            AggregationKeysAre(*AttributionAggregationKeys::FromKeys(
                 {{"key", absl::MakeUint128(/*high=*/5, /*low=*/345)}})))));
     EXPECT_CALL(checkpoint, Call(1));
     EXPECT_CALL(mock_manager_, HandleSource).Times(0);
diff --git a/content/browser/attribution_reporting/attribution_internals.mojom b/content/browser/attribution_reporting/attribution_internals.mojom
index d83eacbf..1b4172e 100644
--- a/content/browser/attribution_reporting/attribution_internals.mojom
+++ b/content/browser/attribution_reporting/attribution_internals.mojom
@@ -100,7 +100,7 @@
   array<uint64> dedup_keys;
   map<string, array<string>> filter_data;
   // The value is a hex-encoded unsigned 128-bit integer.
-  map<string, string> aggregatable_source;
+  map<string, string> aggregation_keys;
 
   // Union of `StorableSource::Result` and `StoredSource::AttributionLogic`,
   // and `AttributionStorage::DeactivatedSource::Reason`.
diff --git a/content/browser/attribution_reporting/attribution_internals_browsertest.cc b/content/browser/attribution_reporting/attribution_internals_browsertest.cc
index bebbb4c..dd29714 100644
--- a/content/browser/attribution_reporting/attribution_internals_browsertest.cc
+++ b/content/browser/attribution_reporting/attribution_internals_browsertest.cc
@@ -14,9 +14,9 @@
 #include "base/command_line.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
+#include "content/browser/attribution_reporting/attribution_aggregation_keys.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_manager.h"
 #include "content/browser/attribution_reporting/attribution_observer_types.h"
@@ -263,8 +263,8 @@
                .SetDedupKeys({13, 17})
                .SetFilterData(*AttributionFilterData::FromSourceFilterValues(
                    {{"a", {"b", "c"}}}))
-               .SetAggregatableSource(
-                   *AttributionAggregatableSource::FromKeys({{"a", 1}}))
+               .SetAggregationKeys(
+                   *AttributionAggregationKeys::FromKeys({{"a", 1}}))
                .BuildStored(),
            SourceBuilder(now + base::Hours(2))
                .SetActiveState(StoredSource::ActiveState::
diff --git a/content/browser/attribution_reporting/attribution_internals_handler_impl.cc b/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
index 010c6ff3..7e54625 100644
--- a/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
+++ b/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
@@ -17,7 +17,7 @@
 #include "base/ranges/algorithm.h"
 #include "base/time/time.h"
 #include "content/browser/attribution_reporting/aggregatable_attribution_utils.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
+#include "content/browser/attribution_reporting/attribution_aggregation_keys.h"
 #include "content/browser/attribution_reporting/attribution_info.h"
 #include "content/browser/attribution_reporting/attribution_observer_types.h"
 #include "content/browser/attribution_reporting/attribution_report.h"
@@ -66,10 +66,10 @@
       WebUIDebugKey(source.debug_key()), dedup_keys,
       source.filter_data().filter_values(),
       base::MakeFlatMap<std::string, std::string>(
-          source.aggregatable_source().keys(), {},
+          source.aggregation_keys().keys(), {},
           [](const auto& key) {
             return std::make_pair(key.first,
-                                  HexEncodeAggregatableKey(key.second));
+                                  HexEncodeAggregationKey(key.second));
           }),
       attributability);
 }
@@ -133,7 +133,7 @@
           [](const auto& contribution) {
             return attribution_internals::mojom::
                 AggregatableHistogramContribution::New(
-                    HexEncodeAggregatableKey(contribution.key()),
+                    HexEncodeAggregationKey(contribution.key()),
                     contribution.value());
           });
       return attribution_internals::mojom::WebUIReportData::
@@ -439,7 +439,7 @@
     web_ui_trigger->aggregatable_triggers.emplace_back(
         absl::in_place,
         /*key_piece=*/
-        HexEncodeAggregatableKey(aggregatable_trigger_data.key_piece()),
+        HexEncodeAggregationKey(aggregatable_trigger_data.key_piece()),
         /*source_keys=*/
         std::vector<std::string>(
             aggregatable_trigger_data.source_keys().begin(),
diff --git a/content/browser/attribution_reporting/attribution_reporting.proto b/content/browser/attribution_reporting/attribution_reporting.proto
index 93dbd76..9ff9d270 100644
--- a/content/browser/attribution_reporting/attribution_reporting.proto
+++ b/content/browser/attribution_reporting/attribution_reporting.proto
@@ -8,15 +8,13 @@
 
 package content.proto;
 
-// Proto equivalent of `blink::mojom::AttributionAggregatableKey`.
-message AttributionAggregatableKey {
+message AttributionAggregationKey {
   optional uint64 high_bits = 1;
   optional uint64 low_bits = 2;
 }
 
-// Proto equivalent of `blink::mojom::AttributionAggregatableSource`.
 message AttributionAggregatableSource {
-  map<string, AttributionAggregatableKey> keys = 1;
+  map<string, AttributionAggregationKey> keys = 1;
 }
 
 message AttributionFilterValues {
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.cc b/content/browser/attribution_reporting/attribution_storage_sql.cc
index 61c6fdc..18c8742 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql.cc
@@ -27,7 +27,7 @@
 #include "base/time/time.h"
 #include "content/browser/attribution_reporting/aggregatable_attribution_utils.h"
 #include "content/browser/attribution_reporting/aggregatable_histogram_contribution.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
+#include "content/browser/attribution_reporting/attribution_aggregation_keys.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_info.h"
 #include "content/browser/attribution_reporting/attribution_observer_types.h"
@@ -316,13 +316,13 @@
   absl::optional<uint64_t> debug_key = ColumnUint64OrNull(statement, col++);
   int num_conversions = statement.ColumnInt(col++);
   int64_t aggregatable_budget_consumed = statement.ColumnInt64(col++);
-  absl::optional<AttributionAggregatableSource> aggregatable_source =
-      AttributionAggregatableSource::Deserialize(statement.ColumnString(col++));
+  absl::optional<AttributionAggregationKeys> aggregation_keys =
+      AttributionAggregationKeys::Deserialize(statement.ColumnString(col++));
 
   if (impression_origin.opaque() || conversion_origin.opaque() ||
       reporting_origin.opaque() || !source_type.has_value() ||
       !attribution_logic.has_value() || num_conversions < 0 ||
-      aggregatable_budget_consumed < 0 || !aggregatable_source.has_value()) {
+      aggregatable_budget_consumed < 0 || !aggregation_keys.has_value()) {
     return absl::nullopt;
   }
 
@@ -341,12 +341,11 @@
 
   return StoredSourceData{
       .source = StoredSource(
-          CommonSourceInfo(source_event_id, std::move(impression_origin),
-                           std::move(conversion_origin),
-                           std::move(reporting_origin), impression_time,
-                           expiry_time, *source_type, priority,
-                           std::move(*filter_data), debug_key,
-                           std::move(*aggregatable_source)),
+          CommonSourceInfo(
+              source_event_id, std::move(impression_origin),
+              std::move(conversion_origin), std::move(reporting_origin),
+              impression_time, expiry_time, *source_type, priority,
+              std::move(*filter_data), debug_key, std::move(*aggregation_keys)),
           *attribution_logic, *active_state, source_id),
       .num_conversions = num_conversions,
       .aggregatable_budget_consumed = aggregatable_budget_consumed};
@@ -583,7 +582,7 @@
       GetSourceActiveState(event_level_active, aggregatable_active);
   DCHECK(active_state.has_value());
 
-  statement.BindBlob(15, common_info.aggregatable_source().Serialize());
+  statement.BindBlob(15, common_info.aggregation_keys().Serialize());
   statement.BindBlob(16, common_info.filter_data().Serialize());
 
   if (!statement.Run())
@@ -803,7 +802,7 @@
   }
 
   if (source_to_attribute->source.common_info()
-          .aggregatable_source()
+          .aggregation_keys()
           .keys()
           .empty()) {
     aggregatable_status = AggregatableResult::kNotRegistered;
@@ -2588,7 +2587,7 @@
   std::vector<AggregatableHistogramContribution> contributions =
       CreateAggregatableHistogram(
           attribution_info.source.common_info().filter_data(),
-          attribution_info.source.common_info().aggregatable_source(),
+          attribution_info.source.common_info().aggregation_keys(),
           trigger.aggregatable_trigger_data(), trigger.aggregatable_values());
   if (contributions.empty())
     return AggregatableResult::kNoHistograms;
diff --git a/content/browser/attribution_reporting/attribution_storage_unittest.cc b/content/browser/attribution_reporting/attribution_storage_unittest.cc
index 03126ee..4e95342 100644
--- a/content/browser/attribution_reporting/attribution_storage_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_unittest.cc
@@ -25,9 +25,9 @@
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "content/browser/attribution_reporting/aggregatable_histogram_contribution.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
+#include "content/browser/attribution_reporting/attribution_aggregation_keys.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_observer_types.h"
 #include "content/browser/attribution_reporting/attribution_report.h"
@@ -1944,14 +1944,13 @@
                                 TriggerDebugKeyIs(33))));
 }
 
-TEST_F(AttributionStorageTest, AttributionAggregatableSource_RoundTrips) {
-  auto aggregatable_source =
-      AttributionAggregatableSource::FromKeys({{"key", 345}});
-  ASSERT_TRUE(aggregatable_source.has_value());
+TEST_F(AttributionStorageTest, AttributionAggregationKeys_RoundTrips) {
+  auto aggregation_keys = AttributionAggregationKeys::FromKeys({{"key", 345}});
+  ASSERT_TRUE(aggregation_keys.has_value());
   storage()->StoreSource(
-      SourceBuilder().SetAggregatableSource(*aggregatable_source).Build());
+      SourceBuilder().SetAggregationKeys(*aggregation_keys).Build());
   EXPECT_THAT(storage()->GetActiveSources(),
-              ElementsAre(AggregatableSourceAre(*aggregatable_source)));
+              ElementsAre(AggregationKeysAre(*aggregation_keys)));
 }
 
 TEST_F(AttributionStorageTest, MaybeCreateAndStoreReport_ReturnsNewReport) {
@@ -2384,8 +2383,7 @@
           .SetReportingOrigin(origin)
           .SetFilterData(*AttributionFilterData::FromSourceFilterValues(
               {{"abc", {"123"}}}))
-          .SetAggregatableSource(
-              *AttributionAggregatableSource::FromKeys({{"0", 1}}))
+          .SetAggregationKeys(*AttributionAggregationKeys::FromKeys({{"0", 1}}))
           .Build());
 
   AttributionTrigger trigger1(origin, origin,
@@ -2532,8 +2530,7 @@
       SourceBuilder()
           .SetFilterData(*AttributionFilterData::FromSourceFilterValues(
               {{"abc", {"123"}}}))
-          .SetAggregatableSource(
-              *AttributionAggregatableSource::FromKeys({{"0", 1}}))
+          .SetAggregationKeys(*AttributionAggregationKeys::FromKeys({{"0", 1}}))
           .Build());
 
   EXPECT_EQ(MaybeCreateAndStoreAggregatableReport(
diff --git a/content/browser/attribution_reporting/attribution_test_utils.cc b/content/browser/attribution_reporting/attribution_test_utils.cc
index 579a43172..6991e240 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.cc
+++ b/content/browser/attribution_reporting/attribution_test_utils.cc
@@ -550,9 +550,9 @@
   return *this;
 }
 
-SourceBuilder& SourceBuilder::SetAggregatableSource(
-    AttributionAggregatableSource aggregatable_source) {
-  aggregatable_source_ = std::move(aggregatable_source);
+SourceBuilder& SourceBuilder::SetAggregationKeys(
+    AttributionAggregationKeys aggregation_keys) {
+  aggregation_keys_ = std::move(aggregation_keys);
   return *this;
 }
 
@@ -561,7 +561,7 @@
       source_event_id_, impression_origin_, conversion_origin_,
       reporting_origin_, impression_time_,
       /*expiry_time=*/impression_time_ + expiry_, source_type_, priority_,
-      filter_data_, debug_key_, aggregatable_source_);
+      filter_data_, debug_key_, aggregation_keys_);
 }
 
 StorableSource SourceBuilder::Build() const {
@@ -782,7 +782,7 @@
                            source.reporting_origin(), source.impression_time(),
                            source.expiry_time(), source.source_type(),
                            source.priority(), source.filter_data(),
-                           source.debug_key(), source.aggregatable_source());
+                           source.debug_key(), source.aggregation_keys());
   };
   return tie(a) == tie(b);
 }
@@ -1106,7 +1106,7 @@
              << ",filter_data=" << source.filter_data() << ",debug_key="
              << (source.debug_key() ? base::NumberToString(*source.debug_key())
                                     : "null")
-             << ",aggregatable_source=" << source.aggregatable_source() << "}";
+             << ",aggregation_keys=" << source.aggregation_keys() << "}";
 }
 
 std::ostream& operator<<(std::ostream& out,
@@ -1292,22 +1292,21 @@
   return map;
 }
 
-bool operator==(const AttributionAggregatableSource& a,
-                const AttributionAggregatableSource& b) {
+bool operator==(const AttributionAggregationKeys& a,
+                const AttributionAggregationKeys& b) {
   return a.keys() == b.keys();
 }
 
-std::ostream& operator<<(
-    std::ostream& out,
-    const AttributionAggregatableSource& aggregatable_source) {
-  out << "{keys=[";
+std::ostream& operator<<(std::ostream& out,
+                         const AttributionAggregationKeys& aggregation_keys) {
+  out << "{";
 
   const char* separator = "";
-  for (const auto& [key_id, key] : aggregatable_source.keys()) {
+  for (const auto& [key_id, key] : aggregation_keys.keys()) {
     out << separator << key_id << ":" << key;
     separator = ", ";
   }
-  return out << "]}";
+  return out << "}";
 }
 
 EventTriggerDataMatcherConfig::EventTriggerDataMatcherConfig(
@@ -1390,13 +1389,13 @@
 }
 
 TestAggregatableSourceProvider::TestAggregatableSourceProvider(size_t size) {
-  AttributionAggregatableSource::Keys::container_type keys;
+  AttributionAggregationKeys::Keys::container_type keys;
   keys.reserve(size);
   for (size_t i = 0; i < size; ++i) {
     keys.emplace_back(base::NumberToString(i), i);
   }
 
-  auto source = AttributionAggregatableSource::FromKeys(std::move(keys));
+  auto source = AttributionAggregationKeys::FromKeys(std::move(keys));
   DCHECK(source.has_value());
   source_ = std::move(*source);
 }
@@ -1405,7 +1404,7 @@
 
 SourceBuilder TestAggregatableSourceProvider::GetBuilder(
     base::Time source_time) const {
-  return SourceBuilder(source_time).SetAggregatableSource(source_);
+  return SourceBuilder(source_time).SetAggregationKeys(source_);
 }
 
 TriggerBuilder DefaultAggregatableTriggerBuilder(
diff --git a/content/browser/attribution_reporting/attribution_test_utils.h b/content/browser/attribution_reporting/attribution_test_utils.h
index f08aa1e3..dd8e1b3 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.h
+++ b/content/browser/attribution_reporting/attribution_test_utils.h
@@ -23,9 +23,9 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/time/time.h"
 #include "content/browser/attribution_reporting/aggregatable_histogram_contribution.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
+#include "content/browser/attribution_reporting/attribution_aggregation_keys.h"
 #include "content/browser/attribution_reporting/attribution_data_host_manager.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_host.h"
@@ -464,8 +464,8 @@
 
   SourceBuilder& SetDedupKeys(std::vector<uint64_t> dedup_keys);
 
-  SourceBuilder& SetAggregatableSource(
-      AttributionAggregatableSource aggregatable_source);
+  SourceBuilder& SetAggregationKeys(
+      AttributionAggregationKeys aggregation_keys);
 
   StorableSource Build() const;
 
@@ -491,7 +491,7 @@
   // Ensure that we don't use uninitialized memory.
   StoredSource::Id source_id_{0};
   std::vector<uint64_t> dedup_keys_;
-  AttributionAggregatableSource aggregatable_source_;
+  AttributionAggregationKeys aggregation_keys_;
 };
 
 // Returns a AttributionTrigger with default data which matches the default
@@ -713,12 +713,11 @@
 std::ostream& operator<<(std::ostream& out,
                          const AttributionAggregatableValues& values);
 
-bool operator==(const AttributionAggregatableSource& a,
-                const AttributionAggregatableSource& b);
+bool operator==(const AttributionAggregationKeys& a,
+                const AttributionAggregationKeys& b);
 
-std::ostream& operator<<(
-    std::ostream& out,
-    const AttributionAggregatableSource& aggregatable_source);
+std::ostream& operator<<(std::ostream& out,
+                         const AttributionAggregationKeys& aggregation_keys);
 
 std::vector<AttributionReport> GetAttributionReportsForTesting(
     AttributionManager* manager);
@@ -776,8 +775,8 @@
   return ExplainMatchResult(matcher, arg.dedup_keys(), result_listener);
 }
 
-MATCHER_P(AggregatableSourceAre, matcher, "") {
-  return ExplainMatchResult(matcher, arg.common_info().aggregatable_source(),
+MATCHER_P(AggregationKeysAre, matcher, "") {
+  return ExplainMatchResult(matcher, arg.common_info().aggregation_keys(),
                             result_listener);
 }
 
@@ -969,7 +968,7 @@
   SourceBuilder GetBuilder(base::Time source_time = base::Time::Now()) const;
 
  private:
-  AttributionAggregatableSource source_;
+  AttributionAggregationKeys source_;
 };
 
 TriggerBuilder DefaultAggregatableTriggerBuilder(
diff --git a/content/browser/attribution_reporting/common_source_info.cc b/content/browser/attribution_reporting/common_source_info.cc
index ef67152..8f86a531 100644
--- a/content/browser/attribution_reporting/common_source_info.cc
+++ b/content/browser/attribution_reporting/common_source_info.cc
@@ -33,18 +33,17 @@
          base::clamp(expiry, kMinImpressionExpiry, kDefaultImpressionExpiry);
 }
 
-CommonSourceInfo::CommonSourceInfo(
-    uint64_t source_event_id,
-    url::Origin impression_origin,
-    url::Origin conversion_origin,
-    url::Origin reporting_origin,
-    base::Time impression_time,
-    base::Time expiry_time,
-    AttributionSourceType source_type,
-    int64_t priority,
-    AttributionFilterData filter_data,
-    absl::optional<uint64_t> debug_key,
-    AttributionAggregatableSource aggregatable_source)
+CommonSourceInfo::CommonSourceInfo(uint64_t source_event_id,
+                                   url::Origin impression_origin,
+                                   url::Origin conversion_origin,
+                                   url::Origin reporting_origin,
+                                   base::Time impression_time,
+                                   base::Time expiry_time,
+                                   AttributionSourceType source_type,
+                                   int64_t priority,
+                                   AttributionFilterData filter_data,
+                                   absl::optional<uint64_t> debug_key,
+                                   AttributionAggregationKeys aggregation_keys)
     : source_event_id_(source_event_id),
       impression_origin_(std::move(impression_origin)),
       conversion_origin_(std::move(conversion_origin)),
@@ -55,7 +54,7 @@
       priority_(priority),
       filter_data_(std::move(filter_data)),
       debug_key_(debug_key),
-      aggregatable_source_(std::move(aggregatable_source)) {
+      aggregation_keys_(std::move(aggregation_keys)) {
   // 30 days is the max allowed expiry for an impression.
   DCHECK_GE(base::Days(30), expiry_time - impression_time);
   // The impression must expire strictly after it occurred.
diff --git a/content/browser/attribution_reporting/common_source_info.h b/content/browser/attribution_reporting/common_source_info.h
index 8b797c07..9ac91c3ca 100644
--- a/content/browser/attribution_reporting/common_source_info.h
+++ b/content/browser/attribution_reporting/common_source_info.h
@@ -8,7 +8,7 @@
 #include <stdint.h>
 
 #include "base/time/time.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
+#include "content/browser/attribution_reporting/attribution_aggregation_keys.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_source_type.h"
 #include "content/common/content_export.h"
@@ -39,7 +39,7 @@
                    int64_t priority,
                    AttributionFilterData filter_data,
                    absl::optional<uint64_t> debug_key,
-                   AttributionAggregatableSource aggregatable_source);
+                   AttributionAggregationKeys aggregation_keys);
 
   ~CommonSourceInfo();
 
@@ -69,8 +69,8 @@
 
   absl::optional<uint64_t> debug_key() const { return debug_key_; }
 
-  const AttributionAggregatableSource& aggregatable_source() const {
-    return aggregatable_source_;
+  const AttributionAggregationKeys& aggregation_keys() const {
+    return aggregation_keys_;
   }
 
   void ClearDebugKey() { debug_key_ = absl::nullopt; }
@@ -98,7 +98,7 @@
   int64_t priority_;
   AttributionFilterData filter_data_;
   absl::optional<uint64_t> debug_key_;
-  AttributionAggregatableSource aggregatable_source_;
+  AttributionAggregationKeys aggregation_keys_;
 
   // When adding new members, the corresponding `operator==()` definition in
   // `attribution_test_utils.h` should also be updated.
diff --git a/content/browser/resources/attribution_reporting/attribution_internals.ts b/content/browser/resources/attribution_reporting/attribution_internals.ts
index 9e81bab..dd4ad77 100644
--- a/content/browser/resources/attribution_reporting/attribution_internals.ts
+++ b/content/browser/resources/attribution_reporting/attribution_internals.ts
@@ -194,7 +194,7 @@
   expiryTime: Date;
   sourceType: string;
   filterData: string;
-  aggregatableSource: string;
+  aggregationKeys: string;
   debugKey: string;
   dedupKeys: string;
   priority: bigint;
@@ -210,8 +210,8 @@
     this.sourceType = sourceTypeToText(mojo.sourceType);
     this.priority = mojo.priority;
     this.filterData = JSON.stringify(mojo.filterData, null, ' ');
-    this.aggregatableSource =
-        JSON.stringify(mojo.aggregatableSource, bigintReplacer, ' ');
+    this.aggregationKeys =
+        JSON.stringify(mojo.aggregationKeys, bigintReplacer, ' ');
     this.debugKey = mojo.debugKey ? mojo.debugKey.value.toString() : '';
     this.dedupKeys = mojo.dedupKeys.join(', ');
     this.status = attributabilityToText(mojo.attributability);
@@ -241,7 +241,7 @@
       new ValueColumn<Source, bigint>('Priority', (e) => e.priority),
       new CodeColumn<Source>('Filter Data', (e) => e.filterData),
       new CodeColumn<Source>(
-          'Aggregatable Source', (e) => e.aggregatableSource),
+          'Aggregation Keys', (e) => e.aggregationKeys),
       new ValueColumn<Source, string>('Debug Key', (e) => e.debugKey),
       new ValueColumn<Source, string>('Dedup Keys', (e) => e.dedupKeys),
     ];
diff --git a/content/test/attribution_simulator_impl.cc b/content/test/attribution_simulator_impl.cc
index f5faf02e..aac37594 100644
--- a/content/test/attribution_simulator_impl.cc
+++ b/content/test/attribution_simulator_impl.cc
@@ -165,7 +165,7 @@
       base::Value::List list;
       for (const auto& contribution : aggregatable_data->contributions) {
         base::Value::Dict dict;
-        dict.Set("key", HexEncodeAggregatableKey(contribution.key()));
+        dict.Set("key", HexEncodeAggregationKey(contribution.key()));
         dict.Set("value", base::checked_cast<int>(contribution.value()));
 
         list.Append(std::move(dict));
diff --git a/content/test/attribution_simulator_input_parser.cc b/content/test/attribution_simulator_input_parser.cc
index de03170..6cb971a 100644
--- a/content/test/attribution_simulator_input_parser.cc
+++ b/content/test/attribution_simulator_input_parser.cc
@@ -20,9 +20,9 @@
 #include "base/test/bind.h"
 #include "base/time/time.h"
 #include "base/values.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
+#include "content/browser/attribution_reporting/attribution_aggregation_keys.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_parser_test_utils.h"
 #include "content/browser/attribution_reporting/attribution_source_type.h"
@@ -249,7 +249,7 @@
     int64_t priority = 0;
     base::TimeDelta expiry;
     AttributionFilterData filter_data;
-    AttributionAggregatableSource aggregatable_source;
+    AttributionAggregationKeys aggregation_keys;
 
     if (!ParseAttributionEvent(
             source_dict, "Attribution-Reporting-Register-Source",
@@ -262,7 +262,7 @@
               filter_data = ParseFilterData(
                   dict, "filter_data",
                   &AttributionFilterData::FromSourceFilterValues);
-              aggregatable_source = ParseAggregatableSource(dict);
+              aggregation_keys = ParseAggregationKeys(dict);
             }))) {
       return;
     }
@@ -277,7 +277,7 @@
             source_time,
             CommonSourceInfo::GetExpiryTime(expiry, source_time, *source_type),
             *source_type, priority, std::move(filter_data), debug_key,
-            std::move(aggregatable_source))),
+            std::move(aggregation_keys))),
         std::move(source));
   }
 
@@ -570,7 +570,7 @@
     return expiry;
   }
 
-  absl::uint128 ParseAggregatableKey(const base::Value& key_value) {
+  absl::uint128 ParseAggregationKey(const base::Value& key_value) {
     const std::string* s = key_value.GetIfString();
 
     absl::uint128 value = 0;
@@ -580,35 +580,34 @@
     return value;
   }
 
-  AttributionAggregatableSource ParseAggregatableSource(
+  AttributionAggregationKeys ParseAggregationKeys(
       const base::Value::Dict& cfg) {
     static constexpr char kKey[] = "aggregation_keys";
 
     const base::Value* value = cfg.Find(kKey);
     if (!value)
-      return AttributionAggregatableSource();
+      return AttributionAggregationKeys();
 
     auto context = PushContext(kKey);
 
     if (!EnsureDictionary(*value))
-      return AttributionAggregatableSource();
+      return AttributionAggregationKeys();
 
-    AttributionAggregatableSource::Keys::container_type keys;
+    AttributionAggregationKeys::Keys::container_type keys;
 
     for (auto [id, key_value] : value->GetDict()) {
       auto key_context = PushContext(id);
-      absl::uint128 key = ParseAggregatableKey(key_value);
+      absl::uint128 key = ParseAggregationKey(key_value);
       if (!has_error())
         keys.emplace_back(std::move(id), key);
     }
 
-    absl::optional<AttributionAggregatableSource> aggregatable_source =
-        AttributionAggregatableSource::FromKeys(std::move(keys));
-    if (!aggregatable_source)
+    absl::optional<AttributionAggregationKeys> aggregation_keys =
+        AttributionAggregationKeys::FromKeys(std::move(keys));
+    if (!aggregation_keys)
       *Error() << "invalid";
 
-    return std::move(aggregatable_source)
-        .value_or(AttributionAggregatableSource());
+    return std::move(aggregation_keys).value_or(AttributionAggregationKeys());
   }
 
   base::flat_set<std::string> ParseAggregatableTriggerDataSourceKeys(
@@ -667,7 +666,7 @@
                 auto key_context = PushContext(kKeyKeyPiece);
                 const base::Value* key_piece = trigger_dict.Find(kKeyKeyPiece);
                 if (key_piece) {
-                  key = ParseAggregatableKey(*key_piece);
+                  key = ParseAggregationKey(*key_piece);
                 } else {
                   *Error() << "must be present";
                 }
diff --git a/content/test/attribution_simulator_input_parser_unittest.cc b/content/test/attribution_simulator_input_parser_unittest.cc
index f6d6d92..9ebe3631 100644
--- a/content/test/attribution_simulator_input_parser_unittest.cc
+++ b/content/test/attribution_simulator_input_parser_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/values.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
+#include "content/browser/attribution_reporting/attribution_aggregation_keys.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_source_type.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.h"
@@ -232,8 +233,8 @@
                   .SetExpiry(base::Days(10))  // rounded to whole number of days
                   .SetPriority(0)             // default
                   .SetDebugKey(absl::nullopt)  // default
-                  .SetAggregatableSource(
-                      *AttributionAggregatableSource::FromKeys({{"a", 1}}))
+                  .SetAggregationKeys(
+                      *AttributionAggregationKeys::FromKeys({{"a", 1}}))
                   .Build(),
               _))));
   EXPECT_THAT(error_stream.str(), IsEmpty());
diff --git a/ios/chrome/browser/translate/BUILD.gn b/ios/chrome/browser/translate/BUILD.gn
index 6eaaac5..4840b3e0 100644
--- a/ios/chrome/browser/translate/BUILD.gn
+++ b/ios/chrome/browser/translate/BUILD.gn
@@ -82,12 +82,14 @@
   deps = [
     ":public",
     ":translate",
+    ":unit_tests_bundle_data",
     "//base",
     "//base/test:test_support",
     "//components/language/core/browser",
     "//components/language/ios/browser",
     "//components/translate/core/browser",
     "//components/translate/core/common",
+    "//components/translate/core/language_detection",
     "//components/translate/ios/browser",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state:test_support",
@@ -164,3 +166,11 @@
     "//ios/third_party/earl_grey2:test_lib",
   ]
 }
+
+bundle_data("unit_tests_bundle_data") {
+  visibility = [ ":unit_tests" ]
+  testonly = true
+  sources = [ "//components/test/data/translate/valid_model.tflite" ]
+  outputs = [ "{{bundle_resources_dir}}/" +
+              "{{source_root_relative_dir}}/{{source_file_part}}" ]
+}
diff --git a/ios/chrome/browser/translate/chrome_ios_translate_client_unittest.mm b/ios/chrome/browser/translate/chrome_ios_translate_client_unittest.mm
index 5bafcb1..b95edd0 100644
--- a/ios/chrome/browser/translate/chrome_ios_translate_client_unittest.mm
+++ b/ios/chrome/browser/translate/chrome_ios_translate_client_unittest.mm
@@ -2,14 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/chrome/browser/translate/chrome_ios_translate_client.h"
+#import "ios/chrome/browser/translate/chrome_ios_translate_client.h"
+#import "base/files/file_util.h"
 #import "base/metrics/metrics_hashes.h"
+#import "base/path_service.h"
 #import "base/test/metrics/histogram_tester.h"
 #import "base/test/scoped_feature_list.h"
 #import "base/test/task_environment.h"
+#import "base/values.h"
 #import "components/language/ios/browser/ios_language_detection_tab_helper.h"
 #import "components/translate/core/browser/translate_metrics_logger.h"
 #import "components/translate/core/common/translate_util.h"
+#import "components/translate/core/language_detection/language_detection_model.h"
+#import "components/translate/ios/browser/language_detection_controller.h"
+#import "components/translate/ios/browser/language_detection_model_service.h"
 #import "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
 #import "ios/chrome/browser/language/language_model_manager_factory.h"
@@ -58,6 +64,19 @@
   web::FakeWebState web_state_;
 };
 
+base::File GetValidModelFile() {
+  base::FilePath source_root_dir;
+  base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &source_root_dir);
+  base::FilePath model_file_path = source_root_dir.AppendASCII("components")
+                                       .AppendASCII("test")
+                                       .AppendASCII("data")
+                                       .AppendASCII("translate")
+                                       .AppendASCII("valid_model.tflite");
+  base::File file(model_file_path,
+                  (base::File::FLAG_OPEN | base::File::FLAG_READ));
+  return file;
+}
+
 TEST_F(ChromeIOSTranslateClientTest, TranslateUICreated) {
   ChromeIOSTranslateClient* translate_client =
       ChromeIOSTranslateClient::FromWebState(&web_state_);
@@ -140,3 +159,25 @@
   histogram_tester_.ExpectUniqueSample("Translate.PageLoad.NumTranslations", 1,
                                        1);
 }
+
+TEST_F(ChromeIOSTranslateClientTest, TFLiteLanguageDetectionDurationRecorded) {
+  ChromeIOSTranslateClient* translate_client =
+      ChromeIOSTranslateClient::FromWebState(&web_state_);
+
+  histogram_tester_.ExpectTotalCount(
+      "Translate.LanguageDetection.TFLiteModelEvaluationDuration", 0);
+
+  translate::LanguageDetectionController* language_detection_controller =
+      translate_client->GetTranslateDriver()->language_detection_controller();
+  language_detection_controller->language_detection_model_->UpdateWithFile(
+      GetValidModelFile());
+  EXPECT_TRUE(
+      language_detection_controller->language_detection_model_->IsAvailable());
+
+  base::Value text_content("hello world");
+  language_detection_controller->OnTextRetrieved(true, "en", "en", GURL(""),
+                                                 &text_content);
+
+  histogram_tester_.ExpectTotalCount(
+      "Translate.LanguageDetection.TFLiteModelEvaluationDuration", 1);
+}
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index e482fbdd..27ec61d 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -543,7 +543,6 @@
     const net::HttpRequestHeaders& headers) {
   // These are lowercase to permit case-insensitive matching.
   auto incompatible_headers = base::MakeFixedFlatSet<base::StringPiece>({
-      "accept",
       "accept-charset",
       "accept-encoding",
       "authorization",
diff --git a/third_party/blink/public/common/attribution_reporting/constants.h b/third_party/blink/public/common/attribution_reporting/constants.h
index 6d48785..75aae53 100644
--- a/third_party/blink/public/common/attribution_reporting/constants.h
+++ b/third_party/blink/public/common/attribution_reporting/constants.h
@@ -11,8 +11,8 @@
 constexpr size_t kMaxValuesPerAttributionFilter = 50;
 constexpr size_t kMaxAttributionFiltersPerSource = 50;
 
-constexpr size_t kMaxBytesPerAttributionAggregatableKeyId = 25;
-constexpr size_t kMaxAttributionAggregatableKeysPerSourceOrTrigger = 50;
+constexpr size_t kMaxBytesPerAttributionAggregationKeyId = 25;
+constexpr size_t kMaxAttributionAggregationKeysPerSourceOrTrigger = 50;
 
 constexpr size_t kMaxAttributionAggregatableTriggerDataPerTrigger = 50;
 
diff --git a/third_party/blink/renderer/core/frame/attribution_response_parsing.cc b/third_party/blink/renderer/core/frame/attribution_response_parsing.cc
index 920353d..e2f28c9 100644
--- a/third_party/blink/renderer/core/frame/attribution_response_parsing.cc
+++ b/third_party/blink/renderer/core/frame/attribution_response_parsing.cc
@@ -31,8 +31,8 @@
 
 namespace {
 
-bool ParseAttributionAggregatableKey(const JSONValue* value,
-                                     absl::uint128* out) {
+bool ParseAttributionAggregationKey(const JSONValue* value,
+                                    absl::uint128* out) {
   if (!value)
     return false;
 
@@ -153,7 +153,7 @@
   const int kExclusiveMaxHistogramValue = 101;
 
   static_assert(
-      kMaxAttributionAggregatableKeysPerSourceOrTrigger <
+      kMaxAttributionAggregationKeysPerSourceOrTrigger <
           kExclusiveMaxHistogramValue,
       "Bump the version for histogram Conversions.AggregatableKeysPerSource");
 
@@ -162,7 +162,7 @@
     return false;
 
   const wtf_size_t num_keys = object->size();
-  if (num_keys > kMaxAttributionAggregatableKeysPerSourceOrTrigger)
+  if (num_keys > kMaxAttributionAggregationKeysPerSourceOrTrigger)
     return false;
 
   base::UmaHistogramCounts100("Conversions.AggregatableKeysPerSource",
@@ -177,12 +177,12 @@
     DCHECK(value);
 
     if (key_id.CharactersSizeInBytes() >
-        kMaxBytesPerAttributionAggregatableKeyId) {
+        kMaxBytesPerAttributionAggregationKeyId) {
       return false;
     }
 
     absl::uint128 key;
-    if (!ParseAttributionAggregatableKey(value, &key))
+    if (!ParseAttributionAggregationKey(value, &key))
       return false;
 
     aggregation_keys.insert(std::move(key_id), key);
@@ -385,15 +385,15 @@
 
     auto data = mojom::blink::AttributionAggregatableTriggerData::New();
 
-    if (!ParseAttributionAggregatableKey(object->Get("key_piece"),
-                                         &data->key_piece)) {
+    if (!ParseAttributionAggregationKey(object->Get("key_piece"),
+                                        &data->key_piece)) {
       return false;
     }
 
     JSONArray* source_keys_val = object->GetArray("source_keys");
     if (!source_keys_val ||
         source_keys_val->size() >
-            kMaxAttributionAggregatableKeysPerSourceOrTrigger) {
+            kMaxAttributionAggregationKeysPerSourceOrTrigger) {
       return false;
     }
 
@@ -407,7 +407,7 @@
       String source_key;
       if (!source_key_val->AsString(&source_key) ||
           source_key.CharactersSizeInBytes() >
-              kMaxBytesPerAttributionAggregatableKeyId) {
+              kMaxBytesPerAttributionAggregationKeyId) {
         return false;
       }
       data->source_keys.push_back(std::move(source_key));
@@ -438,7 +438,7 @@
 
   const auto* object = JSONObject::Cast(json);
   if (!object ||
-      object->size() > kMaxAttributionAggregatableKeysPerSourceOrTrigger) {
+      object->size() > kMaxAttributionAggregationKeysPerSourceOrTrigger) {
     return false;
   }
 
@@ -452,7 +452,7 @@
     DCHECK(value);
 
     if (key_id.CharactersSizeInBytes() >
-        kMaxBytesPerAttributionAggregatableKeyId) {
+        kMaxBytesPerAttributionAggregationKeyId) {
       return false;
     }
 
diff --git a/third_party/blink/renderer/core/frame/attribution_response_parsing_test.cc b/third_party/blink/renderer/core/frame/attribution_response_parsing_test.cc
index 1337c30..560f18f7 100644
--- a/third_party/blink/renderer/core/frame/attribution_response_parsing_test.cc
+++ b/third_party/blink/renderer/core/frame/attribution_response_parsing_test.cc
@@ -131,7 +131,7 @@
    private:
     String GetKey(wtf_size_t index) const {
       // Note that this might not be robust as
-      // `blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger` varies which
+      // `blink::kMaxAttributionAggregationKeysPerSourceOrTrigger` varies which
       // might generate invalid JSON.
       return String(
           std::string(key_size, 'A' + index % 26 + 32 * (index / 26)));
@@ -141,13 +141,12 @@
   const AttributionAggregatableSourceSizeTestCase kTestCases[] = {
       {"empty", true, 0, 0},
       {"max_keys", true,
-       blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger, 1},
+       blink::kMaxAttributionAggregationKeysPerSourceOrTrigger, 1},
       {"too_many_keys", false,
-       blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger + 1, 1},
-      {"max_key_size", true, 1,
-       blink::kMaxBytesPerAttributionAggregatableKeyId},
+       blink::kMaxAttributionAggregationKeysPerSourceOrTrigger + 1, 1},
+      {"max_key_size", true, 1, blink::kMaxBytesPerAttributionAggregationKeyId},
       {"excessive_key_size", false, 1,
-       blink::kMaxBytesPerAttributionAggregatableKeyId + 1},
+       blink::kMaxBytesPerAttributionAggregationKeyId + 1},
   };
 
   for (const auto& test_case : kTestCases) {
@@ -305,12 +304,12 @@
       {"too_many_trigger_data", false,
        blink::kMaxAttributionAggregatableTriggerDataPerTrigger + 1, 0, 0},
       {"max_key_count", true, 1,
-       blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger, 0},
+       blink::kMaxAttributionAggregationKeysPerSourceOrTrigger, 0},
       {"too many keys", false, 1,
-       blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger + 1, 0},
-      {"max_key_size", true, 1, 1, kMaxBytesPerAttributionAggregatableKeyId},
+       blink::kMaxAttributionAggregationKeysPerSourceOrTrigger + 1, 0},
+      {"max_key_size", true, 1, 1, kMaxBytesPerAttributionAggregationKeyId},
       {"excessive_key_size", false, 1, 1,
-       kMaxBytesPerAttributionAggregatableKeyId + 1},
+       kMaxBytesPerAttributionAggregationKeyId + 1},
   };
 
   for (const auto& test_case : kTestCases) {
@@ -390,7 +389,7 @@
    private:
     String GetKey(wtf_size_t index) const {
       // Note that this might not be robust as
-      // `blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger` varies which
+      // `blink::kMaxAttributionAggregationKeysPerSourceOrTrigger` varies which
       // might generate invalid JSON characters.
       return String(
           std::string(key_size, 'A' + index % 26 + 32 * (index / 26)));
@@ -400,13 +399,12 @@
   const AttributionAggregatableValuesSizeTestCase kTestCases[] = {
       {"empty", true, 0, 0},
       {"max_keys", true,
-       blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger, 1},
+       blink::kMaxAttributionAggregationKeysPerSourceOrTrigger, 1},
       {"too_many_keys", false,
-       blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger + 1, 1},
-      {"max_key_size", true, 1,
-       blink::kMaxBytesPerAttributionAggregatableKeyId},
+       blink::kMaxAttributionAggregationKeysPerSourceOrTrigger + 1, 1},
+      {"max_key_size", true, 1, blink::kMaxBytesPerAttributionAggregationKeyId},
       {"excessive_key_size", false, 1,
-       blink::kMaxBytesPerAttributionAggregatableKeyId + 1},
+       blink::kMaxBytesPerAttributionAggregationKeyId + 1},
   };
 
   for (const auto& test_case : kTestCases) {
@@ -1207,7 +1205,7 @@
   }
 }
 
-TEST(AttributionResponseParsingTest, SourceAggregatableKeysHistogram) {
+TEST(AttributionResponseParsingTest, SourceAggregationKeysHistogram) {
   const auto make_aggregatable_source_with_keys = [](wtf_size_t n) {
     auto object = std::make_unique<JSONObject>();
     for (wtf_size_t i = 0; i < n; ++i) {
@@ -1221,8 +1219,8 @@
     bool expected;
   } kTestCases[] = {
       {0, true},
-      {kMaxAttributionAggregatableKeysPerSourceOrTrigger, true},
-      {kMaxAttributionAggregatableKeysPerSourceOrTrigger + 1, false},
+      {kMaxAttributionAggregationKeysPerSourceOrTrigger, true},
+      {kMaxAttributionAggregationKeysPerSourceOrTrigger + 1, false},
   };
 
   for (const auto& test_case : kTestCases) {
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index dc1c1aa4..82887d2f 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -3273,17 +3273,18 @@
         .SetFragmentChildrenInvalid();
   }
 
-  AddLayoutResult(std::move(result), 0);
+  SetLayoutResult(std::move(result), 0);
 }
 
-void LayoutBox::AddLayoutResult(const NGLayoutResult* result,
+void LayoutBox::SetLayoutResult(const NGLayoutResult* result,
                                 wtf_size_t index) {
   NOT_DESTROYED();
   DCHECK_EQ(result->Status(), NGLayoutResult::kSuccess);
+  const auto& box_fragment =
+      To<NGPhysicalBoxFragment>(result->PhysicalFragment());
+
   if (index != WTF::kNotFound && layout_results_.size() > index) {
     if (layout_results_.size() > index + 1) {
-      const auto& box_fragment =
-          To<NGPhysicalBoxFragment>(result->PhysicalFragment());
       // If we have reached the end, remove surplus results from previous
       // layout.
       //
@@ -3316,21 +3317,19 @@
   }
 
   DCHECK(index == layout_results_.size() || index == kNotFound);
-  AddLayoutResult(std::move(result));
+  AppendLayoutResult(result);
+
+  if (!box_fragment.BreakToken())
+    FinalizeLayoutResults();
 }
 
-void LayoutBox::AddLayoutResult(const NGLayoutResult* result) {
+void LayoutBox::AppendLayoutResult(const NGLayoutResult* result) {
   const auto& fragment = To<NGPhysicalBoxFragment>(result->PhysicalFragment());
   // |layout_results_| is particularly critical when side effects are disabled.
   DCHECK(!NGDisableSideEffectsScope::IsDisabled());
   layout_results_.push_back(std::move(result));
   CheckDidAddFragment(*this, fragment);
 
-  // If this is the last fragment for the node, and its node establishes an
-  // inline formatting context, we have some finalization to do.
-  if (!fragment.BreakToken() && HasFragmentItems())
-    NGFragmentItems::FinalizeAfterLayout(layout_results_);
-
   if (layout_results_.size() > 1)
     FragmentCountOrSizeDidChange();
 }
@@ -3361,25 +3360,12 @@
   layout_results_[index] = std::move(result);
   CheckDidAddFragment(*this, fragment, index);
 
-  // If this is the last fragment for the node, and its node establishes an
-  // inline formatting context, we have some finalization to do.
-  if (got_new_fragment && !fragment.BreakToken() && HasFragmentItems())
-    NGFragmentItems::FinalizeAfterLayout(layout_results_);
-}
+  if (got_new_fragment && !fragment.BreakToken()) {
+    // If this is the last result, the results vector better agree on that.
+    DCHECK_EQ(index, layout_results_.size() - 1);
 
-void LayoutBox::ReplaceLayoutResult(const NGLayoutResult* result,
-                                    const NGPhysicalBoxFragment& old_fragment) {
-  DCHECK_EQ(this, old_fragment.OwnerLayoutBox());
-  DCHECK_EQ(result->PhysicalFragment().GetSelfOrContainerLayoutObject(),
-            old_fragment.GetSelfOrContainerLayoutObject());
-  // TODO(kojii): |IndexOf| is O(n). Consider if we can avoid this.
-  const wtf_size_t index = PhysicalFragments().IndexOf(old_fragment);
-  if (index != kNotFound) {
-    ReplaceLayoutResult(std::move(result), index);
-    return;
+    FinalizeLayoutResults();
   }
-  NOTREACHED();
-  AddLayoutResult(std::move(result));
 }
 
 void LayoutBox::RestoreLegacyLayoutResults(
@@ -3389,11 +3375,20 @@
   DCHECK(!IsLayoutNGObject());
   measure_result_ = measure_result;
   if (layout_result)
-    AddLayoutResult(layout_result, 0);
+    SetLayoutResult(layout_result, 0);
   else
     DCHECK(layout_results_.IsEmpty());
 }
 
+void LayoutBox::FinalizeLayoutResults() {
+  DCHECK(!layout_results_.IsEmpty());
+  DCHECK(!layout_results_.back()->PhysicalFragment().BreakToken());
+  // If we've added all the results we were going to, and the node establishes
+  // an inline formatting context, we have some finalization to do.
+  if (HasFragmentItems())
+    NGFragmentItems::FinalizeAfterLayout(layout_results_);
+}
+
 void LayoutBox::ClearLayoutResults() {
   NOT_DESTROYED();
   if (measure_result_)
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index 9a14049..901fd81 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -1186,16 +1186,43 @@
   void SetCachedLayoutResult(const NGLayoutResult*);
 
   // Store one layout result (with its physical fragment) at the specified
-  // index, and delete all entries following it.
-  void AddLayoutResult(const NGLayoutResult*, wtf_size_t index);
-  void AddLayoutResult(const NGLayoutResult*);
+  // index.
+  //
+  // If there's already a result at the specified index, use
+  // ReplaceLayoutResult() to do the job. Otherwise, use AppendLayoutResult().
+  //
+  // If it's going to be the last result, we'll also perform any necessary
+  // finalization (see FinalizeLayoutResults()), and also delete all the old
+  // entries following it (if there used to be more results in a previous
+  // layout).
+  //
+  // In a few specific cases we'll even delete the entries following this
+  // result, even if it's *not* going to be the last one. This is necessary when
+  // we might read out the layout results again before we've got to the end (OOF
+  // block fragmentation, etc.). In all other cases, we'll leave the old results
+  // until we're done, as deleting entries will trigger unnecessary paint
+  // invalidation. With any luck, we'll end up with the same number of results
+  // as the last time, so that paint invalidation might not be necessary.
+  void SetLayoutResult(const NGLayoutResult*, wtf_size_t index);
+
+  // Append one layout result at the end.
+  void AppendLayoutResult(const NGLayoutResult*);
+
+  // Replace a specific layout result. Also perform finalization if it's the
+  // last result (see FinalizeLayoutResults()), but this function does not
+  // delete any (old) results following this one. Callers should generally use
+  // SetLayoutResult() instead of this one, unless they have good reasons not
+  // to.
   void ReplaceLayoutResult(const NGLayoutResult*, wtf_size_t index);
-  void ReplaceLayoutResult(const NGLayoutResult*,
-                           const NGPhysicalBoxFragment& old_fragment);
 
   void ShrinkLayoutResults(wtf_size_t results_to_keep);
   void RestoreLegacyLayoutResults(const NGLayoutResult* measure_result,
                                   const NGLayoutResult* layout_result);
+
+  // Perform any finalization needed after all the layout results have been
+  // added.
+  void FinalizeLayoutResults();
+
   void ClearLayoutResults();
   // Clear LayoutObject fields of physical fragments.
   void DisassociatePhysicalFragments();
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 54ccee3..5d49a25 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -837,7 +837,7 @@
     // token corresponds with the fragment index in the layout object (off by 1,
     // though). When writing back a layout result, we remove any fragments in
     // the layout box at higher indices than that of the one we're writing back.
-    box_->AddLayoutResult(std::move(result), FragmentIndex(break_token));
+    box_->SetLayoutResult(std::move(result), FragmentIndex(break_token));
   }
 }
 
diff --git a/third_party/blink/web_tests/wpt_internal/webgpu/cts.https.html b/third_party/blink/web_tests/wpt_internal/webgpu/cts.https.html
index ecf9766..f55ad60 100644
--- a/third_party/blink/web_tests/wpt_internal/webgpu/cts.https.html
+++ b/third_party/blink/web_tests/wpt_internal/webgpu/cts.https.html
@@ -1385,7 +1385,6 @@
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToBuffer";format="depth32float";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToBuffer";format="depth16unorm";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToBuffer";format="stencil8";*'>
-<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToBuffer";format="depth24unorm-stencil8";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToBuffer";format="depth32float-stencil8";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToTexture";format="r8unorm";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToTexture";format="r8snorm";*'>
@@ -1426,7 +1425,6 @@
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToTexture";format="depth32float";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToTexture";format="depth16unorm";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToTexture";format="stencil8";*'>
-<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToTexture";format="depth24unorm-stencil8";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToTexture";format="depth32float-stencil8";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="r8unorm";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="r8snorm";*'>
@@ -1468,7 +1466,6 @@
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="DepthTest";format="depth16unorm";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="DepthTest";format="depth24plus";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="DepthTest";format="depth24plus-stencil8";*'>
-<meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="DepthTest";format="depth24unorm-stencil8";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="DepthTest";format="depth32float-stencil8";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="StencilTest";*'>
 <meta name=variant content='?q=webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="3d";readMethod="CopyToBuffer";format="r8unorm";*'>
@@ -2591,6 +2588,7 @@
 <meta name=variant content='?q=webgpu:api,validation,resource_usages,buffer,in_pass_encoder:subresources,buffer_usage_in_one_render_pass_with_two_draws:*'>
 <meta name=variant content='?q=webgpu:api,validation,resource_usages,buffer,in_pass_misc:subresources,reset_buffer_usage_before_dispatch:*'>
 <meta name=variant content='?q=webgpu:api,validation,resource_usages,buffer,in_pass_misc:subresources,reset_buffer_usage_before_draw:*'>
+<meta name=variant content='?q=webgpu:api,validation,resource_usages,buffer,in_pass_misc:subresources,buffer_usages_in_copy_and_pass:*'>
 <meta name=variant content='?q=webgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_color:*'>
 <meta name=variant content='?q=webgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_aspect:compute=false;binding0InBundle=false;*'>
 <meta name=variant content='?q=webgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_aspect:compute=false;binding0InBundle=true;*'>
diff --git a/tools/metrics/histograms/metadata/optimization/histograms.xml b/tools/metrics/histograms/metadata/optimization/histograms.xml
index 8d85d1b9..02c127b 100644
--- a/tools/metrics/histograms/metadata/optimization/histograms.xml
+++ b/tools/metrics/histograms/metadata/optimization/histograms.xml
@@ -31,6 +31,8 @@
 
 <variants name="OptimizationTarget">
   <variant name="AutofillAssistant" summary="Autofill Assistant"/>
+  <variant name="ContextualPageActionPriceTracking"
+      summary="Contextual Page Action: Show price tracking"/>
   <variant name="LanguageDetection" summary="Language detection"/>
   <variant name="ModelValidation" summary="Model validation triggered via CLI"/>
   <variant name="NotificationPermissions" summary="Notification permissions"/>
diff --git a/tools/metrics/histograms/metadata/translate/histograms.xml b/tools/metrics/histograms/metadata/translate/histograms.xml
index 09817b0..b1e30eb 100644
--- a/tools/metrics/histograms/metadata/translate/histograms.xml
+++ b/tools/metrics/histograms/metadata/translate/histograms.xml
@@ -337,6 +337,19 @@
   </summary>
 </histogram>
 
+<histogram name="Translate.LanguageDetection.TFLiteModelEvaluationDuration"
+    units="ms" expires_after="2022-11-13">
+  <owner>rajendrant@chromium.org</owner>
+  <owner>mcrouse@chromium.org</owner>
+  <owner>chrome-language@google.com</owner>
+  <summary>
+    Records the time taken for the TFLite language detection model to determine
+    page language. Unlike Translate.LanguageDeterminedDuration, this metric does
+    not record the duration for CDL3 language detection when the TFLite model is
+    unavailable.
+  </summary>
+</histogram>
+
 <histogram name="Translate.LanguageDetectionTiming"
     enum="TranslateLanguageDetectionTiming" expires_after="M85">
   <owner>andrewhayden@chromium.org</owner>