diff --git a/DEPS b/DEPS
index 380ffd7..364f73d6 100644
--- a/DEPS
+++ b/DEPS
@@ -239,7 +239,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '16d3cc04cc85928e3492bc2df7e0a88b8422b73c',
+  'skia_revision': '68e240d9cdb3f09fc0c9de834aaef04aa3b3f579',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -247,7 +247,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'fefd7ae66ad94d8fcb16ef9f5f7304c1b4b9fbf8',
+  'angle_revision': '36fcf80b1f2a99fdaa46d044994dfe96a08d7362',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -306,7 +306,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'e3f9ae73db5135ad998108113af7ef82a47efc51',
+  'catapult_revision': '563885e399ba18672cd7d009a0c0f0a996d71f6b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -354,7 +354,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '4682ae0034e7929dbea9b064ff39f6953eec406b',
+  'dawn_revision': 'f296710f64b6625d204262c09463401def70cea6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1018,7 +1018,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'fff14ebdcbb0fd75801af82d2765758efa1ec579',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '7f13e5c5ff2def048f60931a4f09bdcc0c7c3965',
       'condition': 'checkout_chromeos',
   },
 
@@ -1603,7 +1603,7 @@
   'src/third_party/usrsctp/usrsctplib':
     Var('chromium_git') + '/external/github.com/sctplab/usrsctp' + '@' + '62d7d0c928c9a040dce96aa2f16c00e7e67d59cb',
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@c038900769543d5883b1fab698c007831be50f48',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@2ec74208b0306eff87b9ebc097d8dfbb20e76fc2',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '5e49f57a6e71a026a54eb42e366de09a4142d24e',
@@ -1700,7 +1700,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3f761f4e33519a151e99f110643c052677d2d7a2',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c258c12674403e7c685e3462d136b29560e6b4d8',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_contents.cc b/android_webview/browser/aw_contents.cc
index 0cb2f1c..d824169 100644
--- a/android_webview/browser/aw_contents.cc
+++ b/android_webview/browser/aw_contents.cc
@@ -79,6 +79,7 @@
 #include "content/public/browser/favicon_status.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/page.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
@@ -1512,21 +1513,20 @@
       new_host->GetWidget()->GetFrameSinkId());
 }
 
-void AwContents::DidFinishNavigation(
-    content::NavigationHandle* navigation_handle) {
-  // If this request was blocked in any way, broadcast an error.
-  net::Error error_code = navigation_handle->GetNetErrorCode();
-
-  bool navigation_successful_and_scheme_http_or_https =
-      ((error_code == net::OK) &&
-       navigation_handle->GetURL().SchemeIsHTTPOrHTTPS());
-  if (navigation_successful_and_scheme_http_or_https != scheme_http_or_https_) {
-    scheme_http_or_https_ = navigation_successful_and_scheme_http_or_https;
+void AwContents::PrimaryPageChanged(content::Page& page) {
+  std::string scheme = page.GetMainDocument().GetLastCommittedURL().scheme();
+  if (scheme_ != scheme) {
+    scheme_ = scheme;
     AwBrowserProcess::GetInstance()
         ->visibility_metrics_logger()
         ->ClientVisibilityChanged(this);
   }
+}
 
+void AwContents::DidFinishNavigation(
+    content::NavigationHandle* navigation_handle) {
+  // If this request was blocked in any way, broadcast an error.
+  net::Error error_code = navigation_handle->GetNetErrorCode();
   if (!net::IsRequestBlockedError(error_code) &&
       error_code != net::ERR_ABORTED) {
     return;
@@ -1582,7 +1582,8 @@
   return VisibilityMetricsLogger::VisibilityInfo{
       browser_view_renderer_.attached_to_window(),
       browser_view_renderer_.view_visible(),
-      browser_view_renderer_.window_visible(), scheme_http_or_https_};
+      browser_view_renderer_.window_visible(),
+      VisibilityMetricsLogger::SchemeStringToEnum(scheme_)};
 }
 
 void AwContents::RendererUnresponsive(
diff --git a/android_webview/browser/aw_contents.h b/android_webview/browser/aw_contents.h
index 2c8885fe..7e19b9e 100644
--- a/android_webview/browser/aw_contents.h
+++ b/android_webview/browser/aw_contents.h
@@ -389,6 +389,7 @@
   // content::WebContentsObserver overrides
   void RenderViewHostChanged(content::RenderViewHost* old_host,
                              content::RenderViewHost* new_host) override;
+  void PrimaryPageChanged(content::Page& page) override;
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
 
@@ -428,7 +429,7 @@
   std::unique_ptr<js_injection::JsCommunicationHost> js_communication_host_;
 
   bool view_tree_force_dark_state_ = false;
-  bool scheme_http_or_https_ = false;
+  std::string scheme_;
 
   // GURL is supplied by the content layer as requesting frame.
   // Callback is supplied by the content layer, and is invoked with the result
diff --git a/android_webview/browser/metrics/visibility_metrics_logger.cc b/android_webview/browser/metrics/visibility_metrics_logger.cc
index 417bdbe..8784c5ab 100644
--- a/android_webview/browser/metrics/visibility_metrics_logger.cc
+++ b/android_webview/browser/metrics/visibility_metrics_logger.cc
@@ -11,6 +11,7 @@
 #include "base/metrics/histogram_base.h"
 #include "base/time/time.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/common/url_constants.h"
 
 using content::BrowserThread;
 
@@ -41,20 +42,20 @@
   return histogram;
 }
 
-base::HistogramBase* GetGlobalOpenWebVisibilityHistogram() {
+void LogGlobalVisibleScheme(VisibilityMetricsLogger::Scheme scheme,
+                            int32_t seconds) {
   static base::HistogramBase* histogram(CreateHistogramForDurationTracking(
-      "Android.WebView.WebViewOpenWebVisible.Global",
-      static_cast<int>(
-          VisibilityMetricsLogger::WebViewOpenWebVisibility::kMaxValue)));
-  return histogram;
+      "Android.WebView.VisibleScheme.Global",
+      static_cast<int>(VisibilityMetricsLogger::Scheme::kMaxValue)));
+  histogram->AddCount(static_cast<int32_t>(scheme), seconds);
 }
 
-base::HistogramBase* GetPerWebViewOpenWebVisibilityHistogram() {
+void LogPerWebViewVisibleScheme(VisibilityMetricsLogger::Scheme scheme,
+                                int32_t seconds) {
   static base::HistogramBase* histogram(CreateHistogramForDurationTracking(
-      "Android.WebView.WebViewOpenWebVisible.PerWebView",
-      static_cast<int>(
-          VisibilityMetricsLogger::WebViewOpenWebVisibility::kMaxValue)));
-  return histogram;
+      "Android.WebView.VisibleScheme.PerWebView",
+      static_cast<int>(VisibilityMetricsLogger::Scheme::kMaxValue)));
+  histogram->AddCount(static_cast<int32_t>(scheme), seconds);
 }
 
 base::HistogramBase* GetOpenWebVisibileScreenPortionHistogram() {
@@ -67,6 +68,36 @@
 
 }  // anonymous namespace
 
+// static
+VisibilityMetricsLogger::Scheme VisibilityMetricsLogger::SchemeStringToEnum(
+    const std::string& scheme) {
+  if (scheme.empty())
+    return Scheme::kEmpty;
+  if (scheme == url::kHttpScheme)
+    return Scheme::kHttp;
+  if (scheme == url::kHttpsScheme)
+    return Scheme::kHttps;
+  if (scheme == url::kFileScheme)
+    return Scheme::kFile;
+  if (scheme == url::kFtpScheme)
+    return Scheme::kFtp;
+  if (scheme == url::kDataScheme)
+    return Scheme::kData;
+  if (scheme == url::kJavaScriptScheme)
+    return Scheme::kJavaScript;
+  if (scheme == url::kAboutScheme)
+    return Scheme::kAbout;
+  if (scheme == content::kChromeUIScheme)
+    return Scheme::kChrome;
+  if (scheme == url::kBlobScheme)
+    return Scheme::kBlob;
+  if (scheme == url::kContentScheme)
+    return Scheme::kContent;
+  if (scheme == "intent")
+    return Scheme::kIntent;
+  return Scheme::kUnknown;
+}
+
 VisibilityMetricsLogger::VisibilityMetricsLogger() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   last_update_time_ = base::TimeTicks::Now();
@@ -125,29 +156,26 @@
 void VisibilityMetricsLogger::UpdateDurations() {
   base::TimeTicks update_time = base::TimeTicks::Now();
   base::TimeDelta delta = update_time - last_update_time_;
-  if (visible_client_count_ > 0) {
-    visible_duration_tracker_.any_webview_tracked_duration_ += delta;
+  if (all_clients_visible_count_ > 0) {
+    all_clients_tracker_.any_webview_tracked_duration_ += delta;
   } else {
-    visible_duration_tracker_.no_webview_tracked_duration_ += delta;
+    all_clients_tracker_.no_webview_tracked_duration_ += delta;
+  }
+  all_clients_tracker_.per_webview_duration_ +=
+      delta * all_clients_visible_count_;
+  all_clients_tracker_.per_webview_untracked_duration_ +=
+      delta * (client_visibility_.size() - all_clients_visible_count_);
+
+  for (size_t i = 0; i < base::size(per_scheme_visible_counts_); i++) {
+    if (!per_scheme_visible_counts_[i])
+      continue;
+    per_scheme_trackers_[i].any_webview_tracked_duration_ += delta;
+    per_scheme_trackers_[i].per_webview_duration_ +=
+        delta * per_scheme_visible_counts_[i];
   }
 
-  if (visible_webcontent_client_count_ > 0) {
-    webcontent_visible_tracker_.any_webview_tracked_duration_ += delta;
-  } else {
-    webcontent_visible_tracker_.no_webview_tracked_duration_ += delta;
-  }
-
-  visible_duration_tracker_.per_webview_duration_ +=
-      delta * visible_client_count_;
-  visible_duration_tracker_.per_webview_untracked_duration_ +=
-      delta * (client_visibility_.size() - visible_client_count_);
-
-  webcontent_visible_tracker_.per_webview_duration_ +=
-      delta * visible_webcontent_client_count_;
-  webcontent_visible_tracker_.per_webview_untracked_duration_ +=
-      delta * (client_visibility_.size() - visible_webcontent_client_count_);
-
-  if (visible_webcontent_client_count_ > 0) {
+  if (per_scheme_visible_counts_[static_cast<size_t>(Scheme::kHttp)] > 0 ||
+      per_scheme_visible_counts_[static_cast<size_t>(Scheme::kHttps)] > 0) {
     open_web_screen_portion_tracked_duration_[static_cast<int>(
         current_open_web_screen_portion_)] += delta;
   }
@@ -159,13 +187,9 @@
   return view_attached && view_visible && window_visible;
 }
 
-bool VisibilityMetricsLogger::VisibilityInfo::ContainsOpenWebContent() const {
-  return scheme_http_or_https;
-}
-
 bool VisibilityMetricsLogger::VisibilityInfo::IsDisplayingOpenWebContent()
     const {
-  return IsVisible() && ContainsOpenWebContent();
+  return IsVisible() && (scheme == Scheme::kHttp || scheme == Scheme::kHttps);
 }
 
 void VisibilityMetricsLogger::ProcessClientUpdate(Client* client,
@@ -173,26 +197,25 @@
   VisibilityInfo curr_info = client_visibility_[client];
   bool was_visible = curr_info.IsVisible();
   bool is_visible = info.IsVisible();
-  bool was_visible_web = curr_info.IsDisplayingOpenWebContent();
-  bool is_visible_web = info.IsDisplayingOpenWebContent();
+  Scheme old_scheme = curr_info.scheme;
+  Scheme new_scheme = info.scheme;
   client_visibility_[client] = info;
-  DCHECK(!was_visible || visible_client_count_ > 0);
+  DCHECK(!was_visible || all_clients_visible_count_ > 0);
 
-  bool any_client_was_visible = visible_client_count_ > 0;
+  bool any_client_was_visible = all_clients_visible_count_ > 0;
 
   if (!was_visible && is_visible) {
-    ++visible_client_count_;
+    ++all_clients_visible_count_;
   } else if (was_visible && !is_visible) {
-    --visible_client_count_;
+    --all_clients_visible_count_;
   }
 
-  if (!was_visible_web && is_visible_web) {
-    ++visible_webcontent_client_count_;
-  } else if (was_visible_web && !is_visible_web) {
-    --visible_webcontent_client_count_;
-  }
+  if (was_visible)
+    per_scheme_visible_counts_[static_cast<size_t>(old_scheme)]--;
+  if (is_visible)
+    per_scheme_visible_counts_[static_cast<size_t>(new_scheme)]++;
 
-  bool any_client_is_visible = visible_client_count_ > 0;
+  bool any_client_is_visible = all_clients_visible_count_ > 0;
   if (on_visibility_changed_callback_ &&
       any_client_was_visible != any_client_is_visible) {
     on_visibility_changed_callback_.Run(any_client_is_visible);
@@ -209,7 +232,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   UpdateDurations();
   RecordVisibilityMetrics();
-  RecordOpenWebDisplayMetrics();
+  RecordVisibleSchemeMetrics();
   RecordScreenPortionMetrics();
 }
 
@@ -220,21 +243,21 @@
   int32_t total_no_webview_visible_seconds;
 
   any_webview_visible_seconds =
-      visible_duration_tracker_.any_webview_tracked_duration_.InSeconds();
-  visible_duration_tracker_.any_webview_tracked_duration_ -=
+      all_clients_tracker_.any_webview_tracked_duration_.InSeconds();
+  all_clients_tracker_.any_webview_tracked_duration_ -=
       base::Seconds(any_webview_visible_seconds);
   no_webview_visible_seconds =
-      visible_duration_tracker_.no_webview_tracked_duration_.InSeconds();
-  visible_duration_tracker_.no_webview_tracked_duration_ -=
+      all_clients_tracker_.no_webview_tracked_duration_.InSeconds();
+  all_clients_tracker_.no_webview_tracked_duration_ -=
       base::Seconds(no_webview_visible_seconds);
 
   total_webview_visible_seconds =
-      visible_duration_tracker_.per_webview_duration_.InSeconds();
-  visible_duration_tracker_.per_webview_duration_ -=
+      all_clients_tracker_.per_webview_duration_.InSeconds();
+  all_clients_tracker_.per_webview_duration_ -=
       base::Seconds(total_webview_visible_seconds);
   total_no_webview_visible_seconds =
-      visible_duration_tracker_.per_webview_untracked_duration_.InSeconds();
-  visible_duration_tracker_.per_webview_untracked_duration_ -=
+      all_clients_tracker_.per_webview_untracked_duration_.InSeconds();
+  all_clients_tracker_.per_webview_untracked_duration_ -=
       base::Seconds(total_no_webview_visible_seconds);
 
   if (any_webview_visible_seconds) {
@@ -257,50 +280,24 @@
   }
 }
 
-void VisibilityMetricsLogger::RecordOpenWebDisplayMetrics() {
-  int32_t any_webcontent_visible_seconds;
-  int32_t no_webcontent_visible_seconds;
-  int32_t total_webcontent_visible_seconds;
-  int32_t total_not_webcontent_or_not_visible_seconds;
+void VisibilityMetricsLogger::RecordVisibleSchemeMetrics() {
+  for (size_t i = 0; i < base::size(per_scheme_trackers_); i++) {
+    Scheme scheme = static_cast<Scheme>(i);
+    auto& tracker = per_scheme_trackers_[i];
 
-  any_webcontent_visible_seconds =
-      webcontent_visible_tracker_.any_webview_tracked_duration_.InSeconds();
-  webcontent_visible_tracker_.any_webview_tracked_duration_ -=
-      base::Seconds(any_webcontent_visible_seconds);
-  no_webcontent_visible_seconds =
-      webcontent_visible_tracker_.no_webview_tracked_duration_.InSeconds();
-  webcontent_visible_tracker_.no_webview_tracked_duration_ -=
-      base::Seconds(no_webcontent_visible_seconds);
+    int32_t any_webview_seconds =
+        tracker.any_webview_tracked_duration_.InSeconds();
+    if (any_webview_seconds) {
+      tracker.any_webview_tracked_duration_ -=
+          base::Seconds(any_webview_seconds);
+      LogGlobalVisibleScheme(scheme, any_webview_seconds);
+    }
 
-  total_webcontent_visible_seconds =
-      webcontent_visible_tracker_.per_webview_duration_.InSeconds();
-  webcontent_visible_tracker_.per_webview_duration_ -=
-      base::Seconds(total_webcontent_visible_seconds);
-  total_not_webcontent_or_not_visible_seconds =
-      webcontent_visible_tracker_.per_webview_untracked_duration_.InSeconds();
-  webcontent_visible_tracker_.per_webview_untracked_duration_ -=
-      base::Seconds(total_not_webcontent_or_not_visible_seconds);
-
-  if (any_webcontent_visible_seconds) {
-    GetGlobalOpenWebVisibilityHistogram()->AddCount(
-        static_cast<int>(WebViewOpenWebVisibility::kDisplayOpenWebContent),
-        any_webcontent_visible_seconds);
-  }
-  if (no_webcontent_visible_seconds) {
-    GetGlobalOpenWebVisibilityHistogram()->AddCount(
-        static_cast<int>(WebViewOpenWebVisibility::kNotDisplayOpenWebContent),
-        no_webcontent_visible_seconds);
-  }
-
-  if (total_webcontent_visible_seconds) {
-    GetPerWebViewOpenWebVisibilityHistogram()->AddCount(
-        static_cast<int>(WebViewOpenWebVisibility::kDisplayOpenWebContent),
-        total_webcontent_visible_seconds);
-  }
-  if (total_not_webcontent_or_not_visible_seconds) {
-    GetPerWebViewOpenWebVisibilityHistogram()->AddCount(
-        static_cast<int>(WebViewOpenWebVisibility::kNotDisplayOpenWebContent),
-        total_not_webcontent_or_not_visible_seconds);
+    int32_t per_webview_seconds = tracker.per_webview_duration_.InSeconds();
+    if (per_webview_seconds) {
+      tracker.per_webview_duration_ -= base::Seconds(per_webview_seconds);
+      LogPerWebViewVisibleScheme(scheme, per_webview_seconds);
+    }
   }
 }
 
diff --git a/android_webview/browser/metrics/visibility_metrics_logger.h b/android_webview/browser/metrics/visibility_metrics_logger.h
index 925e237..c0b9936 100644
--- a/android_webview/browser/metrics/visibility_metrics_logger.h
+++ b/android_webview/browser/metrics/visibility_metrics_logger.h
@@ -6,6 +6,7 @@
 #define ANDROID_WEBVIEW_BROWSER_METRICS_VISIBILITY_METRICS_LOGGER_H_
 
 #include <map>
+#include <string>
 
 #include "base/callback.h"
 #include "base/time/time.h"
@@ -14,14 +15,35 @@
 
 class VisibilityMetricsLogger {
  public:
+  // These values are persisted to logs and must match the WebViewUrlScheme enum
+  // defined in enums.xml. Entries should not be renumbered and numeric values
+  // should never be reused.
+  enum class Scheme {
+    kEmpty = 0,
+    kUnknown = 1,
+    kHttp = 2,
+    kHttps = 3,
+    kFile = 4,
+    kFtp = 5,
+    kData = 6,
+    kJavaScript = 7,
+    kAbout = 8,
+    kChrome = 9,
+    kBlob = 10,
+    kContent = 11,
+    kIntent = 12,
+    kMaxValue = kIntent,
+  };
+
+  static Scheme SchemeStringToEnum(const std::string& scheme);
+
   struct VisibilityInfo {
     bool view_attached = false;
     bool view_visible = false;
     bool window_visible = false;
-    bool scheme_http_or_https = false;
+    Scheme scheme = Scheme::kEmpty;
 
     bool IsVisible() const;
-    bool ContainsOpenWebContent() const;
     bool IsDisplayingOpenWebContent() const;
   };
 
@@ -51,14 +73,6 @@
     kMaxValue = kExactlyZeroPercent,
   };
 
-  // These values are persisted to logs. Entries should not be renumbered and
-  // numeric values should never be reused.
-  enum class WebViewOpenWebVisibility {
-    kDisplayOpenWebContent = 0,
-    kNotDisplayOpenWebContent = 1,
-    kMaxValue = kNotDisplayOpenWebContent
-  };
-
   class Client {
    public:
     virtual VisibilityInfo GetVisibilityInfo() = 0;
@@ -88,13 +102,14 @@
   void UpdateDurations();
   void ProcessClientUpdate(Client* client, const VisibilityInfo& info);
   void RecordVisibilityMetrics();
-  void RecordOpenWebDisplayMetrics();
+  void RecordVisibleSchemeMetrics();
   void RecordScreenPortionMetrics();
 
-  // Counter for visible clients
-  size_t visible_client_count_ = 0;
-  // Counter for visible web clients
-  size_t visible_webcontent_client_count_ = 0;
+  // Counts the number of visible clients.
+  size_t all_clients_visible_count_ = 0;
+  // Counts the number of visible clients per scheme.
+  size_t per_scheme_visible_counts_[static_cast<size_t>(Scheme::kMaxValue) +
+                                    1] = {};
 
   struct WebViewDurationTracker {
     // Duration any WebView meets the tracking criteria
@@ -108,8 +123,9 @@
     base::TimeDelta per_webview_untracked_duration_ = base::Seconds(0);
   };
 
-  WebViewDurationTracker visible_duration_tracker_;
-  WebViewDurationTracker webcontent_visible_tracker_;
+  WebViewDurationTracker all_clients_tracker_;
+  WebViewDurationTracker
+      per_scheme_trackers_[static_cast<size_t>(Scheme::kMaxValue) + 1] = {};
 
   base::TimeTicks last_update_time_;
   std::map<Client*, VisibilityInfo> client_visibility_;
diff --git a/android_webview/browser/metrics/visibility_metrics_logger_unittest.cc b/android_webview/browser/metrics/visibility_metrics_logger_unittest.cc
index b8f6c98..259d37c 100644
--- a/android_webview/browser/metrics/visibility_metrics_logger_unittest.cc
+++ b/android_webview/browser/metrics/visibility_metrics_logger_unittest.cc
@@ -40,8 +40,8 @@
     logger_->ClientVisibilityChanged(this);
   }
 
-  void SetSchemeHttpOrHttps(bool scheme_http_or_https) {
-    visibility_info_.scheme_http_or_https = scheme_http_or_https;
+  void SetScheme(VisibilityMetricsLogger::Scheme scheme) {
+    visibility_info_.scheme = scheme;
     logger_->ClientVisibilityChanged(this);
   }
 
@@ -119,7 +119,7 @@
   client->SetViewVisible(true);
   client->SetViewAttached(true);
   client->SetWindowVisible(true);
-  client->SetSchemeHttpOrHttps(true);
+  client->SetScheme(VisibilityMetricsLogger::Scheme::kHttp);
 
   task_environment().FastForwardBy(base::Seconds(10));
   client->SetWindowVisible(false);
@@ -131,21 +131,16 @@
   histogram_tester.ExpectBucketCount(
       "Android.WebView.Visibility.Global",
       VisibilityMetricsLogger::Visibility::kNotVisible, 40);
-
-  histogram_tester.ExpectBucketCount(
-      "Android.WebView.WebViewOpenWebVisible.Global",
-      VisibilityMetricsLogger::WebViewOpenWebVisibility::kDisplayOpenWebContent,
-      10);
-  histogram_tester.ExpectBucketCount(
-      "Android.WebView.WebViewOpenWebVisible.Global",
-      VisibilityMetricsLogger::WebViewOpenWebVisibility::
-          kNotDisplayOpenWebContent,
-      40);
+  histogram_tester.ExpectBucketCount("Android.WebView.VisibleScheme.Global",
+                                     VisibilityMetricsLogger::Scheme::kHttp,
+                                     10);
+  histogram_tester.ExpectBucketCount("Android.WebView.VisibleScheme.Global",
+                                     VisibilityMetricsLogger::Scheme::kData, 0);
 
   client->SetViewVisible(true);
   client->SetViewAttached(true);
   client->SetWindowVisible(true);
-  client->SetSchemeHttpOrHttps(false);
+  client->SetScheme(VisibilityMetricsLogger::Scheme::kData);
   task_environment().FastForwardBy(base::Seconds(90));
 
   logger()->RecordMetrics();
@@ -155,15 +150,12 @@
   histogram_tester.ExpectBucketCount(
       "Android.WebView.Visibility.Global",
       VisibilityMetricsLogger::Visibility::kNotVisible, 40);
-  histogram_tester.ExpectBucketCount(
-      "Android.WebView.WebViewOpenWebVisible.Global",
-      VisibilityMetricsLogger::WebViewOpenWebVisibility::kDisplayOpenWebContent,
-      10);
-  histogram_tester.ExpectBucketCount(
-      "Android.WebView.WebViewOpenWebVisible.Global",
-      VisibilityMetricsLogger::WebViewOpenWebVisibility::
-          kNotDisplayOpenWebContent,
-      130);
+  histogram_tester.ExpectBucketCount("Android.WebView.VisibleScheme.Global",
+                                     VisibilityMetricsLogger::Scheme::kHttp,
+                                     10);
+  histogram_tester.ExpectBucketCount("Android.WebView.VisibleScheme.Global",
+                                     VisibilityMetricsLogger::Scheme::kData,
+                                     90);
 
   client.reset();
 }
@@ -267,22 +259,22 @@
 TEST_F(VisibilityMetricsLoggerTest, TestTwoVisibleWebContentClients) {
   // t=0: client1 created
   // t=10: client2 created
-  // t=40: client1 visible, recording scheduled for t+60s
-  // t=50: client1 navigates to open web content
-  // t=60: client2 visible
-  // t=70: client1 invisible
-  // t=80: client2 invisible
-  // t=100: clients deleted.
+  // t=40: client1 visible with empty scheme
+  // t=50: client1 navigates to http scheme
+  // t=60: client2 visible and navigates to http scheme
+  // t=70: client2 invisible
+  // t=80: client1 invisible
+  // t=100: clients deleted
 
-  // Time with any client visible: 80 - 40 = 40
-  // Time with no visible client: 100 - 40 = 60
-  // Time x visible clients: (60-40) * 1 + (70-60) * 2 + (80-70) * 1 = 50
-  // Time x hidden clients: 100 + 90 - 50 = 140
+  // Any client visible: 40
+  // No client visible: 60
+  // Per client visible: 40 (client1) + 10 (client2) = 50
+  // Per client existing but invisible: 100 (client1) + 90 (client2) - 50 = 140
 
-  // Time with any client displaying open web content: 70 - 50 = 20
-  // Time with no client displaying open web content: 100 - 20 = 80
-  // Time x clients displaying open web content: (70-50) * 1  = 50
-  // Time x clients not displaying open web content: 100 + 90 - 20 = 170
+  // Any client visible with empty scheme: 10
+  // Any client visible with http scheme: 30
+  // Per client visible with empty scheme: 10
+  // Per client visible with http scheme: 30 (client1) + 10 (client2) = 40
 
   base::HistogramTester histogram_tester;
   std::unique_ptr<TestClient> client1 = std::make_unique<TestClient>(logger());
@@ -291,29 +283,23 @@
   std::unique_ptr<TestClient> client2 = std::make_unique<TestClient>(logger());
 
   task_environment().FastForwardBy(base::Seconds(30));
-  // This queues delayed recording after 60 seconds (test-defined)
   client1->SetViewVisible(true);
   client1->SetViewAttached(true);
   client1->SetWindowVisible(true);
 
   task_environment().FastForwardBy(base::Seconds(10));
-  // No additional task is queued
-  client1->SetSchemeHttpOrHttps(true);
+  client1->SetScheme(VisibilityMetricsLogger::Scheme::kHttp);
 
   task_environment().FastForwardBy(base::Seconds(10));
-  // No additional task is queued
   client2->SetViewVisible(true);
   client2->SetViewAttached(true);
   client2->SetWindowVisible(true);
+  client2->SetScheme(VisibilityMetricsLogger::Scheme::kHttp);
 
   task_environment().FastForwardBy(base::Seconds(10));
-  // This does not cause metrics to be recorded because one client remains
-  // visible.
   client1->SetWindowVisible(false);
 
   task_environment().FastForwardBy(base::Seconds(10));
-  // The last client becoming invisible triggers immediate recording and the
-  // cancellation of the queued task.
   client2->SetWindowVisible(false);
 
   task_environment().FastForwardBy(base::Seconds(20));
@@ -334,24 +320,18 @@
       "Android.WebView.Visibility.PerWebView",
       VisibilityMetricsLogger::Visibility::kNotVisible, 140);
 
-  histogram_tester.ExpectBucketCount(
-      "Android.WebView.WebViewOpenWebVisible.Global",
-      VisibilityMetricsLogger::WebViewOpenWebVisibility::kDisplayOpenWebContent,
-      20);
-  histogram_tester.ExpectBucketCount(
-      "Android.WebView.WebViewOpenWebVisible.Global",
-      VisibilityMetricsLogger::WebViewOpenWebVisibility::
-          kNotDisplayOpenWebContent,
-      80);
-  histogram_tester.ExpectBucketCount(
-      "Android.WebView.WebViewOpenWebVisible.PerWebView",
-      VisibilityMetricsLogger::WebViewOpenWebVisibility::kDisplayOpenWebContent,
-      20);
-  histogram_tester.ExpectBucketCount(
-      "Android.WebView.WebViewOpenWebVisible.PerWebView",
-      VisibilityMetricsLogger::WebViewOpenWebVisibility::
-          kNotDisplayOpenWebContent,
-      170);
+  histogram_tester.ExpectBucketCount("Android.WebView.VisibleScheme.Global",
+                                     VisibilityMetricsLogger::Scheme::kEmpty,
+                                     10);
+  histogram_tester.ExpectBucketCount("Android.WebView.VisibleScheme.Global",
+                                     VisibilityMetricsLogger::Scheme::kHttp,
+                                     30);
+  histogram_tester.ExpectBucketCount("Android.WebView.VisibleScheme.PerWebView",
+                                     VisibilityMetricsLogger::Scheme::kEmpty,
+                                     10);
+  histogram_tester.ExpectBucketCount("Android.WebView.VisibleScheme.PerWebView",
+                                     VisibilityMetricsLogger::Scheme::kHttp,
+                                     40);
 }
 
 TEST_F(VisibilityMetricsLoggerTest, TestScreenPortion) {
@@ -383,7 +363,7 @@
   client->SetViewVisible(true);
   client->SetViewAttached(true);
   client->SetWindowVisible(true);
-  client->SetSchemeHttpOrHttps(true);
+  client->SetScheme(VisibilityMetricsLogger::Scheme::kHttp);
   // If pixels is 0 then time spent is logged under kExactlyZeroPercent,
   // otherwise the screen portion is calculated as percentage / 10.
   logger()->UpdateOpenWebScreenArea(/*pixels=*/0, /*percentage=*/0);
@@ -404,7 +384,7 @@
   client->SetViewVisible(true);
 
   task_environment().FastForwardBy(base::Seconds(25));
-  client->SetSchemeHttpOrHttps(false);
+  client->SetScheme(VisibilityMetricsLogger::Scheme::kData);
 
   task_environment().FastForwardBy(base::Seconds(5));
   logger()->RecordMetrics();
@@ -416,10 +396,9 @@
   histogram_tester.ExpectBucketCount(
       "Android.WebView.Visibility.Global",
       VisibilityMetricsLogger::Visibility::kVisible, 80);
-  histogram_tester.ExpectBucketCount(
-      "Android.WebView.WebViewOpenWebVisible.Global",
-      VisibilityMetricsLogger::WebViewOpenWebVisibility::kDisplayOpenWebContent,
-      75);
+  histogram_tester.ExpectBucketCount("Android.WebView.VisibleScheme.Global",
+                                     VisibilityMetricsLogger::Scheme::kHttp,
+                                     75);
   histogram_tester.ExpectBucketCount(
       "Android.WebView.WebViewOpenWebVisible.ScreenPortion2",
       VisibilityMetricsLogger::WebViewOpenWebScreenPortion::kExactlyZeroPercent,
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 6afd5d6..0812dd74 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -212,6 +212,7 @@
     "autotest_private_api_utils.cc",
     "bluetooth_devices_observer.cc",
     "bluetooth_devices_observer.h",
+    "bubble/bubble_constants.h",
     "bubble/bubble_utils.cc",
     "bubble/bubble_utils.h",
     "bubble/simple_grid_layout.cc",
@@ -3127,6 +3128,7 @@
     "//chromeos/system",
     "//chromeos/ui/frame",
     "//components/account_id",
+    "//components/exo/wayland:weston_test_stub",
     "//components/prefs:test_support",
     "//components/user_manager:user_manager",
     "//components/viz/test:test_support",
diff --git a/ash/app_list/views/app_list_bubble_view.cc b/ash/app_list/views/app_list_bubble_view.cc
index 936b2b0..84216d2 100644
--- a/ash/app_list/views/app_list_bubble_view.cc
+++ b/ash/app_list/views/app_list_bubble_view.cc
@@ -22,6 +22,7 @@
 #include "ash/app_list/views/scrollable_apps_grid_view.h"
 #include "ash/app_list/views/search_box_view.h"
 #include "ash/app_list/views/search_result_page_dialog_controller.h"
+#include "ash/bubble/bubble_constants.h"
 #include "ash/keyboard/ui/keyboard_ui_controller.h"
 #include "ash/public/cpp/app_list/app_list_config_provider.h"
 #include "ash/public/cpp/metrics_util.h"
@@ -29,7 +30,6 @@
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/search_box/search_box_constants.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/system/tray/tray_constants.h"
 #include "base/bind.h"
 #include "base/check.h"
 #include "base/check_op.h"
@@ -111,8 +111,7 @@
   // Set up rounded corners and background blur, similar to TrayBubbleView.
   // Layer color is set in OnThemeChanged().
   SetPaintToLayer(ui::LAYER_SOLID_COLOR);
-  layer()->SetRoundedCornerRadius(
-      gfx::RoundedCornersF{kUnifiedTrayCornerRadius});
+  layer()->SetRoundedCornerRadius(gfx::RoundedCornersF{kBubbleCornerRadius});
   layer()->SetFillsBoundsOpaquely(false);
   layer()->SetIsFastRoundedCorner(true);
   layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
diff --git a/ash/bubble/bubble_constants.h b/ash/bubble/bubble_constants.h
new file mode 100644
index 0000000..f99747a1
--- /dev/null
+++ b/ash/bubble/bubble_constants.h
@@ -0,0 +1,20 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_BUBBLE_BUBBLE_CONSTANTS_H_
+#define ASH_BUBBLE_BUBBLE_CONSTANTS_H_
+
+namespace ash {
+
+// The corner radius of a bubble, like the system tray bubble or the
+// productivity launcher bubble.
+constexpr int kBubbleCornerRadius = 16;
+
+// Padding used for bubbles that represent a menu of options, like the system
+// tray bubble or the switch access menu.
+constexpr int kBubbleMenuPadding = 8;
+
+}  // namespace ash
+
+#endif  // ASH_BUBBLE_BUBBLE_CONSTANTS_H_
diff --git a/ash/login/ui/fingerprint_auth_factor_model.cc b/ash/login/ui/fingerprint_auth_factor_model.cc
index f668717..b31ecb85 100644
--- a/ash/login/ui/fingerprint_auth_factor_model.cc
+++ b/ash/login/ui/fingerprint_auth_factor_model.cc
@@ -93,9 +93,7 @@
     case FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING:
       return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_TOUCH_SENSOR;
     case FingerprintState::DISABLED_FROM_ATTEMPTS:
-      // TODO(crbug.com/1233614): Update this string: "Too many attempts" ->
-      // "Too many fingerprint attempts".
-      return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_FROM_ATTEMPTS;
+      return IDS_FINGERPRINT_LABEL_NOT_AUTHENTICATED;
     case FingerprintState::DISABLED_FROM_TIMEOUT:
       return can_use_pin_ ? IDS_AUTH_FACTOR_LABEL_PASSWORD_OR_PIN_REQUIRED
                           : IDS_AUTH_FACTOR_LABEL_PASSWORD_REQUIRED;
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc b/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
index 589f4e8..5d67876e 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
+++ b/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
@@ -23,6 +23,8 @@
 const char kRetroactiveEngagementFlowMetric[] =
     "Bluetooth.ChromeOS.FastPair.RetroactiveEngagementFunnel.Steps";
 const char kPairingMethodMetric[] = "Bluetooth.ChromeOS.FastPair.PairingMethod";
+const char kRetroactivePairingResultMetric[] =
+    "Bluetooth.ChromeOS.FastPair.RetroactivePairing.Result";
 
 }  // namespace
 
@@ -78,5 +80,9 @@
   base::UmaHistogramEnumeration(kPairingMethodMetric, method);
 }
 
+void RecordRetroactivePairingResult(bool success) {
+  base::UmaHistogramBoolean(kRetroactivePairingResultMetric, success);
+}
+
 }  // namespace quick_pair
 }  // namespace ash
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_metrics.h b/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
index 073904ad..f59ab1c 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
+++ b/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
@@ -70,6 +70,9 @@
 COMPONENT_EXPORT(QUICK_PAIR_COMMON)
 void RecordPairingMethod(PairingMethod method);
 
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordRetroactivePairingResult(bool success);
+
 }  // namespace quick_pair
 }  // namespace ash
 
diff --git a/ash/quick_pair/keyed_service/quick_pair_metrics_logger.cc b/ash/quick_pair/keyed_service/quick_pair_metrics_logger.cc
index bc8d9b3..8eb0540 100644
--- a/ash/quick_pair/keyed_service/quick_pair_metrics_logger.cc
+++ b/ash/quick_pair/keyed_service/quick_pair_metrics_logger.cc
@@ -199,7 +199,12 @@
 
 void QuickPairMetricsLogger::OnAccountKeyWrite(
     scoped_refptr<Device> device,
-    absl::optional<AccountKeyFailure> error) {}
+    absl::optional<AccountKeyFailure> error) {
+  if (device->protocol != Protocol::kFastPairRetroactive)
+    return;
+
+  RecordRetroactivePairingResult(/*success=*/!error.has_value());
+}
 
 void QuickPairMetricsLogger::OnCompanionAppAction(scoped_refptr<Device> device,
                                                   CompanionAppAction action) {}
diff --git a/ash/quick_pair/keyed_service/quick_pair_metrics_logger_unittest.cc b/ash/quick_pair/keyed_service/quick_pair_metrics_logger_unittest.cc
index 5f326ba..57cf0e2 100644
--- a/ash/quick_pair/keyed_service/quick_pair_metrics_logger_unittest.cc
+++ b/ash/quick_pair/keyed_service/quick_pair_metrics_logger_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/quick_pair/common/account_key_failure.h"
 #include "ash/quick_pair/common/constants.h"
 #include "ash/quick_pair/common/device.h"
 #include "ash/quick_pair/common/fast_pair/fast_pair_metrics.h"
@@ -47,6 +48,8 @@
 constexpr char kFastPairPairTimeMetricSubsequent[] =
     "Bluetooth.ChromeOS.FastPair.TotalUxPairTime.SubsequentPairingProtocol";
 const char kPairingMethodMetric[] = "Bluetooth.ChromeOS.FastPair.PairingMethod";
+const char kRetroactivePairingResultMetric[] =
+    "Bluetooth.ChromeOS.FastPair.RetroactivePairing.Result";
 
 constexpr char kTestDeviceAddress[] = "11:12:13:14:15:16";
 constexpr char kTestBleDeviceName[] = "Test Device Name";
@@ -250,6 +253,37 @@
         retroactive_device_, AssociateAccountAction::kLearnMore);
   }
 
+  void SimulateAccountKeyWritten(Protocol protocol) {
+    switch (protocol) {
+      case Protocol::kFastPairInitial:
+        mock_pairer_broker_->NotifyAccountKeyWrite(initial_device_,
+                                                   absl::nullopt);
+        break;
+      case Protocol::kFastPairSubsequent:
+        break;
+      case Protocol::kFastPairRetroactive:
+        mock_pairer_broker_->NotifyAccountKeyWrite(retroactive_device_,
+                                                   absl::nullopt);
+        break;
+    }
+  }
+
+  void SimulateAccountKeyFailure(Protocol protocol) {
+    switch (protocol) {
+      case Protocol::kFastPairInitial:
+        mock_pairer_broker_->NotifyAccountKeyWrite(
+            initial_device_, AccountKeyFailure::kAccountKeyCharacteristicWrite);
+        break;
+      case Protocol::kFastPairSubsequent:
+        break;
+      case Protocol::kFastPairRetroactive:
+        mock_pairer_broker_->NotifyAccountKeyWrite(
+            retroactive_device_,
+            AccountKeyFailure::kAccountKeyCharacteristicWrite);
+        break;
+    }
+  }
+
   void PairFastPairDeviceWithFastPair(std::string address) {
     auto fp_device = base::MakeRefCounted<Device>(kValidModelId, address,
                                                   Protocol::kFastPairInitial);
@@ -1192,5 +1226,23 @@
             1);
 }
 
+TEST_F(QuickPairMetricsLoggerTest, WriteAccountKey_Initial) {
+  histogram_tester().ExpectTotalCount(kRetroactivePairingResultMetric, 0);
+  SimulateAccountKeyWritten(Protocol::kFastPairInitial);
+  histogram_tester().ExpectTotalCount(kRetroactivePairingResultMetric, 0);
+}
+
+TEST_F(QuickPairMetricsLoggerTest, WriteAccountKey_Retroactive) {
+  histogram_tester().ExpectTotalCount(kRetroactivePairingResultMetric, 0);
+  SimulateAccountKeyWritten(Protocol::kFastPairRetroactive);
+  histogram_tester().ExpectTotalCount(kRetroactivePairingResultMetric, 1);
+}
+
+TEST_F(QuickPairMetricsLoggerTest, WriteAccountKeyFailure_Retroactive) {
+  histogram_tester().ExpectTotalCount(kRetroactivePairingResultMetric, 0);
+  SimulateAccountKeyFailure(Protocol::kFastPairRetroactive);
+  histogram_tester().ExpectTotalCount(kRetroactivePairingResultMetric, 1);
+}
+
 }  // namespace quick_pair
 }  // namespace ash
diff --git a/ash/system/accessibility/autoclick_menu_bubble_controller.cc b/ash/system/accessibility/autoclick_menu_bubble_controller.cc
index 2e99aa88..28e856e 100644
--- a/ash/system/accessibility/autoclick_menu_bubble_controller.cc
+++ b/ash/system/accessibility/autoclick_menu_bubble_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/accessibility/autoclick_menu_bubble_controller.h"
 
+#include "ash/bubble/bubble_constants.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
@@ -155,7 +156,7 @@
                                    kCollisionWindowWorkAreaInsetsDp,
                                    kCollisionWindowWorkAreaInsetsDp);
   init_params.preferred_width = kAutoclickMenuWidth;
-  init_params.corner_radius = kUnifiedTrayCornerRadius;
+  init_params.corner_radius = kBubbleCornerRadius;
   init_params.has_shadow = false;
   init_params.translucent = true;
   bubble_view_ = new TrayBubbleView(init_params);
diff --git a/ash/system/accessibility/autoclick_scroll_bubble_controller.cc b/ash/system/accessibility/autoclick_scroll_bubble_controller.cc
index 2bf2d668..184beb8 100644
--- a/ash/system/accessibility/autoclick_scroll_bubble_controller.cc
+++ b/ash/system/accessibility/autoclick_scroll_bubble_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/accessibility/autoclick_scroll_bubble_controller.h"
 
+#include "ash/bubble/bubble_constants.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
@@ -39,7 +40,7 @@
 }
 }  // namespace
 
-AutoclickScrollBubbleController::AutoclickScrollBubbleController() {}
+AutoclickScrollBubbleController::AutoclickScrollBubbleController() = default;
 
 AutoclickScrollBubbleController::~AutoclickScrollBubbleController() {
   if (bubble_widget_ && !bubble_widget_->IsClosed())
@@ -67,7 +68,7 @@
   // Adjust the insets to be the same on all sides, so that when the bubble
   // lays out it isn't too close on the top or bottom.
   bubble_view_->UpdateInsets(
-      gfx::Insets(kUnifiedMenuPadding, kUnifiedMenuPadding));
+      gfx::Insets(kBubbleMenuPadding, kBubbleMenuPadding));
 
   aura::Window* window = Shell::GetPrimaryRootWindow();
   gfx::Rect work_area =
@@ -153,7 +154,7 @@
   set_scroll_rect_ = !positions.empty();
   if (!set_scroll_rect_) {
     bubble_view_->UpdateInsets(gfx::Insets(
-        0, kUnifiedMenuPadding, kUnifiedMenuPadding, kUnifiedMenuPadding));
+        0, kBubbleMenuPadding, kBubbleMenuPadding, kBubbleMenuPadding));
     UpdateAnchorRect(menu_bubble_rect_, menu_bubble_alignment_);
     return;
   }
@@ -197,12 +198,12 @@
   // The widget's shadow is drawn below and on the sides of the scroll view.
   // Do not inset the top, so that when the scroll bubble is shown below the
   // menu bubble it lays out directly below the menu bubble's shadow, at a
-  // height of kUnifiedMenuPadding.
-  init_params.insets = gfx::Insets(0, kUnifiedMenuPadding, kUnifiedMenuPadding,
-                                   kUnifiedMenuPadding);
+  // height of kBubbleMenuPadding.
+  init_params.insets = gfx::Insets(0, kBubbleMenuPadding, kBubbleMenuPadding,
+                                   kBubbleMenuPadding);
   init_params.preferred_width = kAutoclickScrollMenuSizeDips;
   init_params.max_height = kAutoclickScrollMenuSizeDips;
-  init_params.corner_radius = kUnifiedTrayCornerRadius;
+  init_params.corner_radius = kBubbleCornerRadius;
   init_params.has_shadow = false;
   init_params.translucent = true;
   bubble_view_ = new AutoclickScrollBubbleView(init_params);
diff --git a/ash/system/accessibility/floating_accessibility_controller.cc b/ash/system/accessibility/floating_accessibility_controller.cc
index 74daa88..1264efde 100644
--- a/ash/system/accessibility/floating_accessibility_controller.cc
+++ b/ash/system/accessibility/floating_accessibility_controller.cc
@@ -5,6 +5,7 @@
 #include "ash/system/accessibility/floating_accessibility_controller.h"
 
 #include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/bubble/bubble_constants.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
@@ -68,7 +69,7 @@
   init_params.insets = gfx::Insets(0, kCollisionWindowWorkAreaInsetsDp,
                                    kCollisionWindowWorkAreaInsetsDp,
                                    kCollisionWindowWorkAreaInsetsDp);
-  init_params.corner_radius = kUnifiedTrayCornerRadius;
+  init_params.corner_radius = kBubbleCornerRadius;
   init_params.has_shadow = false;
   init_params.max_height = kFloatingMenuHeight;
   init_params.translucent = true;
diff --git a/ash/system/accessibility/floating_accessibility_detailed_controller.cc b/ash/system/accessibility/floating_accessibility_detailed_controller.cc
index 2ab6461..6592e59 100644
--- a/ash/system/accessibility/floating_accessibility_detailed_controller.cc
+++ b/ash/system/accessibility/floating_accessibility_detailed_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/accessibility/floating_accessibility_detailed_controller.h"
 
+#include "ash/bubble/bubble_constants.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
@@ -73,10 +74,10 @@
       Shell::GetPrimaryRootWindow(), kShellWindowId_SettingBubbleContainer);
   init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
   init_params.anchor_rect = anchor_rect;
-  init_params.insets = gfx::Insets(0, kUnifiedMenuPadding, kUnifiedMenuPadding,
-                                   kUnifiedMenuPadding);
+  init_params.insets = gfx::Insets(0, kBubbleMenuPadding, kBubbleMenuPadding,
+                                   kBubbleMenuPadding);
   init_params.close_on_deactivate = false;
-  init_params.corner_radius = kUnifiedTrayCornerRadius;
+  init_params.corner_radius = kBubbleCornerRadius;
   init_params.has_shadow = false;
   init_params.translucent = true;
 
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.cc b/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.cc
index 1dd43eb..bcfb94e 100644
--- a/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.cc
+++ b/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.cc
@@ -5,6 +5,7 @@
 #include "ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.h"
 
 #include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/bubble/bubble_constants.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -47,8 +48,8 @@
                             kShellWindowId_AccessibilityBubbleContainer);
     init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
     init_params.is_anchored_to_status_area = false;
-    init_params.insets = gfx::Insets(kUnifiedMenuPadding, kUnifiedMenuPadding);
-    init_params.corner_radius = kUnifiedTrayCornerRadius;
+    init_params.insets = gfx::Insets(kBubbleMenuPadding, kBubbleMenuPadding);
+    init_params.corner_radius = kBubbleCornerRadius;
     init_params.has_shadow = false;
     init_params.translucent = true;
     init_params.preferred_width = kPreferredWidth;
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.cc b/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.cc
index 2ec2262..668ed2d1 100644
--- a/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.cc
+++ b/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.cc
@@ -5,6 +5,7 @@
 #include "ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.h"
 
 #include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/bubble/bubble_constants.h"
 #include "ash/public/cpp/accessibility_controller_enums.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
@@ -51,7 +52,7 @@
     init_params.anchor_view = anchor_view;
     init_params.is_anchored_to_status_area = false;
     init_params.margin = gfx::Insets(kBubbleViewMargin, kBubbleViewMargin);
-    init_params.corner_radius = kUnifiedTrayCornerRadius;
+    init_params.corner_radius = kBubbleCornerRadius;
     init_params.has_shadow = false;
     init_params.translucent = true;
     init_params.close_on_deactivate = false;
diff --git a/ash/system/accessibility/switch_access/switch_access_menu_bubble_controller.cc b/ash/system/accessibility/switch_access/switch_access_menu_bubble_controller.cc
index dc698d9..2095c00 100644
--- a/ash/system/accessibility/switch_access/switch_access_menu_bubble_controller.cc
+++ b/ash/system/accessibility/switch_access/switch_access_menu_bubble_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/accessibility/switch_access/switch_access_menu_bubble_controller.h"
 
+#include "ash/bubble/bubble_constants.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
 #include "ash/system/accessibility/switch_access/switch_access_back_button_bubble_controller.h"
@@ -45,8 +46,8 @@
                             kShellWindowId_AccessibilityBubbleContainer);
     init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
     init_params.is_anchored_to_status_area = false;
-    init_params.insets = gfx::Insets(kUnifiedMenuPadding, kUnifiedMenuPadding);
-    init_params.corner_radius = kUnifiedTrayCornerRadius;
+    init_params.insets = gfx::Insets(kBubbleMenuPadding, kBubbleMenuPadding);
+    init_params.corner_radius = kBubbleCornerRadius;
     init_params.has_shadow = false;
     init_params.translucent = true;
     bubble_view_ = new TrayBubbleView(init_params);
@@ -54,7 +55,7 @@
 
     menu_view_ = new SwitchAccessMenuView();
     menu_view_->SetBorder(
-        views::CreateEmptyBorder(gfx::Insets(kUnifiedMenuPadding)));
+        views::CreateEmptyBorder(gfx::Insets(kBubbleMenuPadding)));
     bubble_view_->AddChildView(menu_view_);
 
     menu_view_->SetPaintToLayer();
@@ -96,7 +97,7 @@
   // The resting bounds includes padding on each side of the menu.
   // Remove that before passing to the back button controller so the back button
   // appears in the correct position.
-  resting_bounds.Inset(kUnifiedMenuPadding, kUnifiedMenuPadding);
+  resting_bounds.Inset(kBubbleMenuPadding, kBubbleMenuPadding);
   back_button_controller_->ShowBackButton(resting_bounds,
                                           /*show_focus_ring=*/false,
                                           /*for_menu=*/true);
diff --git a/ash/system/accessibility/switch_access/switch_access_menu_view.cc b/ash/system/accessibility/switch_access/switch_access_menu_view.cc
index f23b59a..16b934c 100644
--- a/ash/system/accessibility/switch_access/switch_access_menu_view.cc
+++ b/ash/system/accessibility/switch_access/switch_access_menu_view.cc
@@ -5,6 +5,7 @@
 #include "ash/system/accessibility/switch_access/switch_access_menu_view.h"
 
 #include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/bubble/bubble_constants.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -238,13 +239,13 @@
   views::GridLayout* layout =
       SetLayoutManager(std::make_unique<views::GridLayout>());
   views::ColumnSet* columns = layout->AddColumnSet(0);
-  columns->AddPaddingColumn(0 /* resize_percent */, kUnifiedMenuPadding);
+  columns->AddPaddingColumn(0 /* resize_percent */, kBubbleMenuPadding);
   for (int i = 0; i < kMaxColumns; i++) {
     columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
                        0, /* resize_percent */
                        views::GridLayout::ColumnSize::kFixed,
                        SwitchAccessMenuButton::kWidthDip, 0);
-    columns->AddPaddingColumn(0 /* resize_percent */, kUnifiedMenuPadding);
+    columns->AddPaddingColumn(0 /* resize_percent */, kBubbleMenuPadding);
   }
 
   int button_count = 0;
@@ -256,19 +257,19 @@
     // If this is the first button of a new row, tell the layout to start a
     // new row.
     if (button_count % kMaxColumns == 0)
-      layout->StartRowWithPadding(0, 0, 0, kUnifiedMenuPadding);
+      layout->StartRowWithPadding(0, 0, 0, kBubbleMenuPadding);
     layout->AddView(std::make_unique<SwitchAccessMenuButton>(action, *info.icon,
                                                              info.label_id));
     button_count++;
   }
-  layout->AddPaddingRow(0, kUnifiedMenuPadding);
+  layout->AddPaddingRow(0, kBubbleMenuPadding);
   InvalidateLayout();
 }
 
 int SwitchAccessMenuView::GetBubbleWidthDip() const {
   // In the future this will vary with the number of menu items displayed.
   return (kMaxColumns * SwitchAccessMenuButton::kWidthDip) +
-         ((kMaxColumns - 1) * kUnifiedMenuPadding) +
+         ((kMaxColumns - 1) * kBubbleMenuPadding) +
          kUnifiedMenuItemPadding.left() + kUnifiedMenuItemPadding.right();
 }
 
diff --git a/ash/system/holding_space/holding_space_tray_child_bubble.cc b/ash/system/holding_space/holding_space_tray_child_bubble.cc
index 7bb013e..1e99612 100644
--- a/ash/system/holding_space/holding_space_tray_child_bubble.cc
+++ b/ash/system/holding_space/holding_space_tray_child_bubble.cc
@@ -6,13 +6,13 @@
 
 #include <set>
 
+#include "ash/bubble/bubble_constants.h"
 #include "ash/public/cpp/holding_space/holding_space_constants.h"
 #include "ash/public/cpp/style/color_provider.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/system/holding_space/holding_space_item_views_section.h"
 #include "ash/system/holding_space/holding_space_util.h"
 #include "ash/system/holding_space/holding_space_view_delegate.h"
-#include "ash/system/tray/tray_constants.h"
 #include "base/bind.h"
 #include "ui/compositor/callback_layer_animation_observer.h"
 #include "ui/compositor/layer.h"
@@ -135,8 +135,7 @@
   layer()->SetFillsBoundsOpaquely(false);
   layer()->SetIsFastRoundedCorner(true);
   layer()->SetOpacity(0.f);
-  layer()->SetRoundedCornerRadius(
-      gfx::RoundedCornersF{kUnifiedTrayCornerRadius});
+  layer()->SetRoundedCornerRadius(gfx::RoundedCornersF{kBubbleCornerRadius});
 
   // Sections.
   for (auto& section : CreateSections()) {
diff --git a/ash/system/message_center/ash_notification_view.cc b/ash/system/message_center/ash_notification_view.cc
index 07bca33..7c0bf67 100644
--- a/ash/system/message_center/ash_notification_view.cc
+++ b/ash/system/message_center/ash_notification_view.cc
@@ -983,7 +983,8 @@
 
   // Grouped child notification use notification's icon for the app icon view,
   // so we don't need further update here.
-  if (is_grouped_child_view_ && !notification->icon().IsEmpty())
+  if (!notification ||
+      (is_grouped_child_view_ && !notification->icon().IsEmpty()))
     return;
 
   SkColor accent_color = notification->accent_color().value_or(
diff --git a/ash/system/message_center/unified_message_center_bubble.cc b/ash/system/message_center/unified_message_center_bubble.cc
index 5224e313..f2c1abce 100644
--- a/ash/system/message_center/unified_message_center_bubble.cc
+++ b/ash/system/message_center/unified_message_center_bubble.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/bubble/bubble_constants.h"
 #include "ash/constants/ash_features.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
@@ -58,7 +59,7 @@
     flags.setStyle(cc::PaintFlags::kStroke_Style);
     flags.setStrokeWidth(canvas->image_scale());
     flags.setAntiAlias(true);
-    canvas->DrawRoundRect(bounds, kUnifiedTrayCornerRadius, flags);
+    canvas->DrawRoundRect(bounds, kBubbleCornerRadius, flags);
   }
 
   void OnDeviceScaleFactorChanged(float old_device_scale_factor,
@@ -102,7 +103,7 @@
 
   ui::Layer* widget_layer = bubble_widget_->GetLayer();
   if (!features::IsNotificationsRefreshEnabled()) {
-    float radius = kUnifiedTrayCornerRadius;
+    float radius = kBubbleCornerRadius;
     widget_layer->SetRoundedCornerRadius({radius, radius, radius, radius});
     widget_layer->SetIsFastRoundedCorner(true);
   }
diff --git a/ash/system/message_center/unified_message_center_bubble_unittest.cc b/ash/system/message_center/unified_message_center_bubble_unittest.cc
index d11a129..3177e3db 100644
--- a/ash/system/message_center/unified_message_center_bubble_unittest.cc
+++ b/ash/system/message_center/unified_message_center_bubble_unittest.cc
@@ -156,7 +156,78 @@
   int id_ = 0;
 };
 
-TEST_F(UnifiedMessageCenterBubbleTest, PositionedAboveSystemTray) {
+// TODO(crbug.com/1279984): Make this test a parameterized test. Currently fails
+// when NotificationRefresh enabled.
+TEST_F(UnifiedMessageCenterBubbleTest, HandleAccelerators) {
+  auto id = AddWebNotification();
+  WaitForAnimation();
+
+  // Open and focus message center.
+  DoAltShiftN();
+  WaitForAnimation();
+  EXPECT_TRUE(GetMessageCenterBubble()->IsMessageCenterVisible());
+  EXPECT_EQ(
+      1u,
+      message_center::MessageCenter::Get()->GetVisibleNotifications().size());
+
+  views::Widget* quick_settings_widget =
+      GetSystemTrayBubble()->GetBubbleWidget();
+  views::Widget* message_center_widget =
+      GetMessageCenterBubble()->GetBubbleWidget();
+  EXPECT_FALSE(quick_settings_widget->IsActive());
+  EXPECT_TRUE(message_center_widget->IsActive());
+
+  RemoveAllNotifications();
+  WaitForAnimation();
+  EXPECT_EQ(
+      0u,
+      message_center::MessageCenter::Get()->GetVisibleNotifications().size());
+  EXPECT_FALSE(quick_settings_widget->IsActive());
+  EXPECT_TRUE(message_center_widget->IsActive());
+  EXPECT_EQ(GetFirstMessageCenterFocusable(),
+            message_center_widget->GetFocusManager()->GetFocusedView());
+
+  // Press Esc to close system tray.
+  DoEsc();
+  WaitForAnimation();
+  EXPECT_EQ(nullptr,
+            GetPrimaryUnifiedSystemTray()->GetFocusManager()->GetFocusedView());
+}
+
+// Tests with NotificationsRefresh enabled and disabled.
+class ParameterizedMessageCenterBubbleTest
+    : public UnifiedMessageCenterBubbleTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  ParameterizedMessageCenterBubbleTest() = default;
+
+  ParameterizedMessageCenterBubbleTest(
+      const ParameterizedMessageCenterBubbleTest&) = delete;
+  ParameterizedMessageCenterBubbleTest& operator=(
+      const ParameterizedMessageCenterBubbleTest&) = delete;
+
+  ~ParameterizedMessageCenterBubbleTest() override = default;
+
+  // AshTestBase:
+  void SetUp() override {
+    scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
+    scoped_feature_list_->InitWithFeatureState(features::kNotificationsRefresh,
+                                               IsNotificationsRefreshEnabled());
+
+    UnifiedMessageCenterBubbleTest::SetUp();
+  }
+
+  bool IsNotificationsRefreshEnabled() const { return GetParam(); }
+
+ private:
+  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         ParameterizedMessageCenterBubbleTest,
+                         testing::Bool() /* IsNotificationsRefreshEnabled() */);
+
+TEST_P(ParameterizedMessageCenterBubbleTest, PositionedAboveSystemTray) {
   const int total_notifications = 5;
   GetPrimaryUnifiedSystemTray()->ShowBubble();
   AddNotification();
@@ -185,7 +256,7 @@
   }
 }
 
-TEST_F(UnifiedMessageCenterBubbleTest, FocusCycle) {
+TEST_P(ParameterizedMessageCenterBubbleTest, FocusCycle) {
   GetPrimaryUnifiedSystemTray()->ShowBubble();
   AddNotification();
   AddNotification();
@@ -233,91 +304,7 @@
             GetFirstQuickSettingsFocusable());
 }
 
-TEST_F(UnifiedMessageCenterBubbleTest, HandleAccelerators) {
-  auto id = AddWebNotification();
-  WaitForAnimation();
-
-  // Open and focus message center.
-  DoAltShiftN();
-  WaitForAnimation();
-  EXPECT_TRUE(GetMessageCenterBubble()->IsMessageCenterVisible());
-  EXPECT_EQ(
-      1u,
-      message_center::MessageCenter::Get()->GetVisibleNotifications().size());
-
-  views::Widget* quick_settings_widget =
-      GetSystemTrayBubble()->GetBubbleWidget();
-  views::Widget* message_center_widget =
-      GetMessageCenterBubble()->GetBubbleWidget();
-  EXPECT_FALSE(quick_settings_widget->IsActive());
-  EXPECT_TRUE(message_center_widget->IsActive());
-
-  RemoveAllNotifications();
-  WaitForAnimation();
-  EXPECT_EQ(
-      0u,
-      message_center::MessageCenter::Get()->GetVisibleNotifications().size());
-  EXPECT_FALSE(quick_settings_widget->IsActive());
-  EXPECT_TRUE(message_center_widget->IsActive());
-  EXPECT_EQ(GetFirstMessageCenterFocusable(),
-            message_center_widget->GetFocusManager()->GetFocusedView());
-
-  // Press Esc to close system tray.
-  DoEsc();
-  WaitForAnimation();
-  EXPECT_EQ(nullptr,
-            GetPrimaryUnifiedSystemTray()->GetFocusManager()->GetFocusedView());
-}
-
-TEST_F(UnifiedMessageCenterBubbleTest, ReverseFocusCycle) {
-  GetPrimaryUnifiedSystemTray()->ShowBubble();
-  AddNotification();
-  AddNotification();
-
-  views::Widget* quick_settings_widget =
-      GetSystemTrayBubble()->GetBubbleWidget();
-  views::Widget* message_center_widget =
-      GetMessageCenterBubble()->GetBubbleWidget();
-
-  // First shift tab should focus the last element in the quick settings bubble.
-  DoShiftTab();
-  EXPECT_TRUE(quick_settings_widget->IsActive());
-  EXPECT_FALSE(message_center_widget->IsActive());
-  EXPECT_EQ(quick_settings_widget->GetFocusManager()->GetFocusedView(),
-            GetLastQuickSettingsFocusable());
-
-  // Keep shift tabbing until we reach the first focusable element in the quick
-  // settings bubble.
-  while (quick_settings_widget->GetFocusManager()->GetFocusedView() !=
-         GetFirstQuickSettingsFocusable()) {
-    DoShiftTab();
-  }
-
-  // Shift tab at the first element in the quick settings bubble should move
-  // focus to the last element in the message center.
-  DoShiftTab();
-  EXPECT_TRUE(message_center_widget->IsActive());
-  EXPECT_FALSE(quick_settings_widget->IsActive());
-  EXPECT_EQ(message_center_widget->GetFocusManager()->GetFocusedView(),
-            GetLastMessageCenterFocusable());
-
-  // Keep shift tabbing until we reach the first focusable element in the
-  // message center bubble.
-  while (message_center_widget->GetFocusManager()->GetFocusedView() !=
-         GetFirstMessageCenterFocusable()) {
-    DoShiftTab();
-  }
-
-  // Shift tab at the first element in the message center bubble should move
-  // focus to the last element in the quick settings bubble.
-  DoShiftTab();
-  EXPECT_TRUE(quick_settings_widget->IsActive());
-  EXPECT_FALSE(message_center_widget->IsActive());
-  EXPECT_EQ(quick_settings_widget->GetFocusManager()->GetFocusedView(),
-            GetLastQuickSettingsFocusable());
-}
-
-TEST_F(UnifiedMessageCenterBubbleTest, CollapseState) {
+TEST_P(ParameterizedMessageCenterBubbleTest, CollapseState) {
   AddNotification();
   AddNotification();
 
@@ -368,7 +355,7 @@
   EXPECT_FALSE(IsMessageCenterCollapsed());
 }
 
-TEST_F(UnifiedMessageCenterBubbleTest, FocusCycleWithNoNotifications) {
+TEST_P(ParameterizedMessageCenterBubbleTest, FocusCycleWithNoNotifications) {
   GetPrimaryUnifiedSystemTray()->ShowBubble();
 
   views::Widget* quick_settings_widget =
@@ -399,39 +386,6 @@
             GetFirstQuickSettingsFocusable());
 }
 
-// Tests with NotificationsRefresh enabled and disabled.
-class ParameterizedMessageCenterBubbleTest
-    : public UnifiedMessageCenterBubbleTest,
-      public testing::WithParamInterface<bool> {
- public:
-  ParameterizedMessageCenterBubbleTest() = default;
-
-  ParameterizedMessageCenterBubbleTest(
-      const ParameterizedMessageCenterBubbleTest&) = delete;
-  ParameterizedMessageCenterBubbleTest& operator=(
-      const ParameterizedMessageCenterBubbleTest&) = delete;
-
-  ~ParameterizedMessageCenterBubbleTest() override = default;
-
-  // AshTestBase:
-  void SetUp() override {
-    scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
-    scoped_feature_list_->InitWithFeatureState(features::kNotificationsRefresh,
-                                               IsNotificationsRefreshEnabled());
-
-    UnifiedMessageCenterBubbleTest::SetUp();
-  }
-
-  bool IsNotificationsRefreshEnabled() const { return GetParam(); }
-
- private:
-  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
-};
-
-INSTANTIATE_TEST_SUITE_P(All,
-                         ParameterizedMessageCenterBubbleTest,
-                         testing::Bool() /* IsNotificationsRefreshEnabled() */);
-
 TEST_P(ParameterizedMessageCenterBubbleTest, BubbleBounds) {
   // Set display size where the message center is not collapsed.
   UpdateDisplay("0+0-1280×1024");
diff --git a/ash/system/message_center/unified_message_list_view.cc b/ash/system/message_center/unified_message_list_view.cc
index 0f5f737..35c824c7 100644
--- a/ash/system/message_center/unified_message_list_view.cc
+++ b/ash/system/message_center/unified_message_list_view.cc
@@ -5,6 +5,7 @@
 #include "ash/system/message_center/unified_message_list_view.h"
 #include <string>
 
+#include "ash/bubble/bubble_constants.h"
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/metrics_util.h"
 #include "ash/system/message_center/ash_notification_view.h"
@@ -138,8 +139,8 @@
                   : views::CreateSolidSidedBorder(
                         0, 0, kUnifiedNotificationSeparatorThickness, 0,
                         message_center_style::kSeperatorColor));
-    const int top_radius = is_top ? kUnifiedTrayCornerRadius : 0;
-    const int bottom_radius = is_bottom ? kUnifiedTrayCornerRadius : 0;
+    const int top_radius = is_top ? kBubbleCornerRadius : 0;
+    const int bottom_radius = is_bottom ? kBubbleCornerRadius : 0;
     message_view_->UpdateCornerRadius(top_radius, bottom_radius);
     control_view_->UpdateCornerRadius(top_radius, bottom_radius);
   }
diff --git a/ash/system/message_center/unified_message_list_view_unittest.cc b/ash/system/message_center/unified_message_list_view_unittest.cc
index e76121d..1cf97e3 100644
--- a/ash/system/message_center/unified_message_list_view_unittest.cc
+++ b/ash/system/message_center/unified_message_list_view_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/message_center/unified_message_list_view.h"
 
+#include "ash/bubble/bubble_constants.h"
 #include "ash/constants/ash_features.h"
 #include "ash/system/message_center/message_center_constants.h"
 #include "ash/system/tray/tray_constants.h"
@@ -305,7 +306,7 @@
   // Check rounded corners when the feature is not enabled (when the feature is
   // enabled we round corners in the scroll view).
   if (!IsNotificationsRefreshEnabled())
-    EXPECT_EQ(kUnifiedTrayCornerRadius, GetMessageViewAt(2)->bottom_radius());
+    EXPECT_EQ(kBubbleCornerRadius, GetMessageViewAt(2)->bottom_radius());
 
   EXPECT_LT(0, message_list_view()->GetPreferredSize().height());
 }
@@ -322,8 +323,8 @@
   // Check rounded corners when the feature is not enabled (when the feature is
   // enabled we round corners in the scroll view).
   if (!IsNotificationsRefreshEnabled()) {
-    EXPECT_EQ(kUnifiedTrayCornerRadius, GetMessageViewAt(0)->top_radius());
-    EXPECT_EQ(kUnifiedTrayCornerRadius, GetMessageViewAt(0)->bottom_radius());
+    EXPECT_EQ(kBubbleCornerRadius, GetMessageViewAt(0)->top_radius());
+    EXPECT_EQ(kBubbleCornerRadius, GetMessageViewAt(0)->bottom_radius());
   }
 
   int previous_message_list_view_height =
@@ -365,7 +366,7 @@
   // Check rounded corners when the feature is not enabled (when the feature is
   // enabled, we round corners in the scroll view).
   if (!IsNotificationsRefreshEnabled())
-    EXPECT_EQ(kUnifiedTrayCornerRadius, GetMessageViewAt(1)->bottom_radius());
+    EXPECT_EQ(kBubbleCornerRadius, GetMessageViewAt(1)->bottom_radius());
 }
 
 TEST_P(ParameterizedUnifiedMessageListViewTest, RemoveNotification) {
@@ -391,8 +392,8 @@
   // Check rounded corners when the feature is not enabled (when the feature is
   // enabled, we round corners in the scroll view).
   if (!IsNotificationsRefreshEnabled()) {
-    EXPECT_EQ(kUnifiedTrayCornerRadius, GetMessageViewAt(0)->top_radius());
-    EXPECT_EQ(kUnifiedTrayCornerRadius, GetMessageViewAt(0)->bottom_radius());
+    EXPECT_EQ(kBubbleCornerRadius, GetMessageViewAt(0)->top_radius());
+    EXPECT_EQ(kBubbleCornerRadius, GetMessageViewAt(0)->bottom_radius());
   }
 
   MessageCenter::Get()->RemoveNotification(id1, true /* by_user */);
diff --git a/ash/system/privacy_screen/privacy_screen_toast_controller.cc b/ash/system/privacy_screen/privacy_screen_toast_controller.cc
index 166e75d..f8096fa 100644
--- a/ash/system/privacy_screen/privacy_screen_toast_controller.cc
+++ b/ash/system/privacy_screen/privacy_screen_toast_controller.cc
@@ -5,6 +5,7 @@
 #include "ash/system/privacy_screen/privacy_screen_toast_controller.h"
 
 #include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/bubble/bubble_constants.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/system/status_area_widget.h"
@@ -54,7 +55,7 @@
   // Decrease bottom and right insets to compensate for the adjustment of
   // the respective edges in Shelf::GetSystemTrayAnchorRect().
   init_params.insets = GetTrayBubbleInsets();
-  init_params.corner_radius = kUnifiedTrayCornerRadius;
+  init_params.corner_radius = kBubbleCornerRadius;
   init_params.has_shadow = false;
   init_params.translucent = true;
 
diff --git a/ash/system/time/calendar_event_list_item_view.cc b/ash/system/time/calendar_event_list_item_view.cc
index 07a15632..b855f2a2 100644
--- a/ash/system/time/calendar_event_list_item_view.cc
+++ b/ash/system/time/calendar_event_list_item_view.cc
@@ -98,6 +98,7 @@
       time_range_(new views::Label()) {
   SetLayoutManager(std::make_unique<views::FillLayout>());
   GetViewAccessibility().OverrideName(GetClassName());
+  SetFocusBehavior(FocusBehavior::ALWAYS);
 
   summary_->SetText(base::UTF8ToUTF16(event.summary()));
   SetUpLabel(summary_);
diff --git a/ash/system/time/calendar_event_list_view.h b/ash/system/time/calendar_event_list_view.h
index a0c6f9b..edd990e 100644
--- a/ash/system/time/calendar_event_list_view.h
+++ b/ash/system/time/calendar_event_list_view.h
@@ -34,6 +34,7 @@
 
  private:
   friend class CalendarViewEventListViewTest;
+  friend class CalendarViewTest;
 
   // Updates the event list entries.
   void UpdateListItems();
diff --git a/ash/system/time/calendar_view.cc b/ash/system/time/calendar_view.cc
index 9f1e49a..41675b5 100644
--- a/ash/system/time/calendar_view.cc
+++ b/ash/system/time/calendar_view.cc
@@ -306,6 +306,8 @@
   scroll_view_->SetDrawOverflowIndicator(false);
   scroll_view_->SetVerticalScrollBarMode(
       views::ScrollView::ScrollBarMode::kHiddenButEnabled);
+  scroll_view_->GetViewAccessibility().OverrideName(GetClassName());
+  scroll_view_->SetFocusBehavior(FocusBehavior::ALWAYS);
   on_contents_scrolled_subscription_ =
       scroll_view_->AddContentsScrolledCallback(base::BindRepeating(
           &CalendarView::OnContentsScrolled, base::Unretained(this)));
@@ -329,10 +331,12 @@
 }
 
 CalendarView::~CalendarView() {
-  // Removes child views including month views to remove its dependency from
-  // `CalendarViewController`, since month views are destructed after the
-  // controller.
+  // Removes child views including month views and event list to remove their
+  // dependency from `CalendarViewController`, since these views are destructed
+  // after the controller.
   content_view_->RemoveAllChildViews();
+  if (event_list_container_)
+    event_list_container_->RemoveAllChildViews();
 }
 
 void CalendarView::Init() {
@@ -410,7 +414,7 @@
 }
 
 void CalendarView::ResetToToday() {
-  if (event_list_)
+  if (event_list_container_)
     return;
 
   calendar_view_controller_->UpdateMonth(base::Time::Now());
@@ -516,6 +520,11 @@
   }
 
   content_view_->SetFocusBehavior(FocusBehavior::ALWAYS);
+
+  // Also disable the focus behavior of the `event_list_container_`, since now
+  // the focusing ring is out of the date cells.
+  if (event_list_container_)
+    event_list_container_->SetFocusBehavior(FocusBehavior::NEVER);
 }
 
 void CalendarView::OnViewBoundsChanged(views::View* observed_view) {
@@ -535,12 +544,24 @@
   if (observed_view != content_view_ || IsDateCellViewFocused())
     return;
 
-  // When focusing on the `content_view_`, we decide which is the to-be-focued
-  // cell based on the current position.
+  auto* focus_manager = GetFocusManager();
   previous_month_->EnableFocus();
   current_month_->EnableFocus();
   next_month_->EnableFocus();
-  auto* focus_manager = GetFocusManager();
+
+  // If the event list is showing, focus on the first cell in the current row or
+  // today's cell if today is in this row.
+  if (event_list_container_) {
+    focus_manager->SetFocusedView(
+        current_month_->focused_cells()[calendar_view_controller_
+                                            ->GetExpandedRowIndex()]);
+    content_view_->SetFocusBehavior(FocusBehavior::NEVER);
+    event_list_container_->SetFocusBehavior(FocusBehavior::ALWAYS);
+    return;
+  }
+
+  // When focusing on the `content_view_`, we decide which is the to-be-focued
+  // cell based on the current position.
   const int position = scroll_view_->GetVisibleRect().y();
   const int row_height = calendar_view_controller_->row_height();
 
@@ -647,9 +668,12 @@
 }
 
 void CalendarView::OpenEventList() {
-  if (event_list_)
+  if (event_list_container_)
     return;
 
+  scroll_view_->SetVerticalScrollBarMode(
+      views::ScrollView::ScrollBarMode::kHiddenButEnabled);
+
   if (!calendar_view_controller_->IsSelectedDateInCurrentMonth())
     ScrollDownOneMonth();
   base::AutoReset<bool> is_resetting_scrolling(&is_resetting_scroll_, true);
@@ -660,8 +684,20 @@
                                     calendar_view_controller_->row_height());
   scroll_view_->SetVerticalScrollBarMode(
       views::ScrollView::ScrollBarMode::kDisabled);
-  event_list_ = AddChildView(
+
+  // The event list is in a container, which will be used for escaping the
+  // focusing from the date cells.
+  event_list_container_ = AddChildView(std::make_unique<views::View>());
+  event_list_container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical));
+  event_list_container_->GetViewAccessibility().OverrideName(GetClassName());
+  event_list_container_->SetFocusBehavior(
+      IsDateCellViewFocused() ? FocusBehavior::ALWAYS : FocusBehavior::NEVER);
+
+  event_list_ = event_list_container_->AddChildView(
       std::make_unique<CalendarEventListView>(calendar_view_controller_.get()));
+  event_list_->GetViewAccessibility().OverrideName(GetClassName());
+  event_list_->SetFocusBehavior(FocusBehavior::ALWAYS);
   calendar_view_controller_->OnEventListOpened();
 }
 
@@ -669,7 +705,8 @@
   scroll_view_->ClipHeightTo(0, INT_MAX);
   scroll_view_->SetVerticalScrollBarMode(
       views::ScrollView::ScrollBarMode::kHiddenButEnabled);
-  RemoveChildViewT(event_list_);
+  RemoveChildViewT(event_list_container_);
+  event_list_container_ = nullptr;
   event_list_ = nullptr;
   calendar_view_controller_->OnEventListClosed();
 }
@@ -767,7 +804,7 @@
   if (is_resetting_scroll_)
     return;
 
-  if (event_list_) {
+  if (event_list_container_) {
     ScrollOneRowWithAnimation(is_scrolling_up);
     return;
   }
@@ -921,13 +958,23 @@
   // goes to the next focusable button in the header.
   if (key_event->type() == ui::EventType::ET_KEY_PRESSED &&
       views::FocusManager::IsTabTraversalKeyEvent(*key_event)) {
-    // Set focus on null pointer first. Otherwise the it will auto
-    // `AdvanceFocus` when the focused cell is on blur.
-    focus_manager->SetFocusedView(nullptr);
+    // Set focus on `scroll_view_`/`event_list_` or null pointer to escape the
+    // focusing on the date cell.
+    if (key_event->IsShiftDown()) {
+      scroll_view_->RequestFocus();
+    } else if (event_list_container_) {
+      event_list_container_->RequestFocus();
+      event_list_container_->SetFocusBehavior(FocusBehavior::NEVER);
+    } else {
+      focus_manager->SetFocusedView(nullptr);
+    }
+
     current_month_->DisableFocus();
     previous_month_->DisableFocus();
     next_month_->DisableFocus();
+
     TrayDetailedView::OnEvent(event);
+
     content_view_->SetFocusBehavior(FocusBehavior::ALWAYS);
     return;
   }
@@ -943,6 +990,10 @@
     case ui::VKEY_UP:
     case ui::VKEY_DOWN: {
       auto* current_focusable_view = focus_manager->GetFocusedView();
+      // Enable the scroll bar mode, in case it is disabled when the event list
+      // is showing.
+      scroll_view_->SetVerticalScrollBarMode(
+          views::ScrollView::ScrollBarMode::kHiddenButEnabled);
 
       // Moving 7 (`kDateInOneWeek`) steps will focus on the cell which is right
       // above or below the current cell, since each row has 7 days.
@@ -994,13 +1045,36 @@
         }
       }
       focus_manager->SetFocusedView(current_focusable_view);
+      // After focusing on the new cell the view should have scrolled already
+      // if needed, disable the scroll bar mode if the even list is showing.
+      if (event_list_container_)
+        scroll_view_->SetVerticalScrollBarMode(
+            views::ScrollView::ScrollBarMode::kDisabled);
+      const int current_height =
+          scroll_view_->GetVisibleRect().y() - PositionOfCurrentMonth();
+      calendar_view_controller_->set_expanded_row_index(
+          current_height / calendar_view_controller_->row_height());
       return;
     }
     case ui::VKEY_LEFT:
     case ui::VKEY_RIGHT: {
+      // Enable the scroll bar mode, in case it is disabled when the event list
+      // is showing.
+      scroll_view_->SetVerticalScrollBarMode(
+          views::ScrollView::ScrollBarMode::kHiddenButEnabled);
       bool is_reverse = base::i18n::IsRTL() ? key_code == ui::VKEY_RIGHT
                                             : key_code == ui::VKEY_LEFT;
       focus_manager->AdvanceFocus(/*reverse=*/is_reverse);
+      // After focusing on the new cell the view should have scrolled already
+      // if needed, disable the scroll bar mode if the even list is showing.
+      if (event_list_container_)
+        scroll_view_->SetVerticalScrollBarMode(
+            views::ScrollView::ScrollBarMode::kDisabled);
+
+      const int current_height =
+          scroll_view_->GetVisibleRect().y() - PositionOfCurrentMonth();
+      calendar_view_controller_->set_expanded_row_index(
+          current_height / calendar_view_controller_->row_height());
       return;
     }
     default:
diff --git a/ash/system/time/calendar_view.h b/ash/system/time/calendar_view.h
index 3de16a9..90bebb4 100644
--- a/ash/system/time/calendar_view.h
+++ b/ash/system/time/calendar_view.h
@@ -210,6 +210,7 @@
   views::Button* settings_button_ = nullptr;
   IconButton* up_button_ = nullptr;
   IconButton* down_button_ = nullptr;
+  views::View* event_list_container_ = nullptr;
   CalendarEventListView* event_list_ = nullptr;
 
   // If it `is_resetting_scroll_`, we don't calculate the scroll position and we
diff --git a/ash/system/time/calendar_view_unittest.cc b/ash/system/time/calendar_view_unittest.cc
index c43a15fe..b29ae916 100644
--- a/ash/system/time/calendar_view_unittest.cc
+++ b/ash/system/time/calendar_view_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "ash/shell.h"
 #include "ash/style/icon_button.h"
+#include "ash/system/time/calendar_event_list_view.h"
 #include "ash/system/time/calendar_month_view.h"
 #include "ash/system/time/calendar_unittest_utils.h"
 #include "ash/system/time/calendar_utils.h"
@@ -60,12 +61,16 @@
   }
 
   CalendarView* calendar_view() { return calendar_view_; }
-  views::ScrollView* scroll_view_() { return calendar_view_->scroll_view_; }
+  views::ScrollView* scroll_view() { return calendar_view_->scroll_view_; }
 
   views::View* previous_label() { return calendar_view_->previous_label_; }
   views::View* current_label() { return calendar_view_->current_label_; }
   views::View* next_label() { return calendar_view_->next_label_; }
 
+  views::ScrollView::ScrollBarMode GetScrollBarMode() {
+    return scroll_view()->GetVerticalScrollBarMode();
+  }
+
   // The position of the `next_month_`.
   int NextMonthPosition() {
     return previous_label()->GetPreferredSize().height() +
@@ -116,6 +121,9 @@
   views::Button* settings_button() { return calendar_view_->settings_button_; }
   IconButton* up_button() { return calendar_view_->up_button_; }
   IconButton* down_button() { return calendar_view_->down_button_; }
+  views::ImageButton* close_button() {
+    return calendar_view_->event_list_->close_button_;
+  }
 
   void ScrollUpOneMonth() { calendar_view_->ScrollUpOneMonthAndAutoScroll(); }
   void ScrollDownOneMonth() {
@@ -128,6 +136,11 @@
     generator.PressKey(ui::KeyboardCode::VKEY_TAB, ui::EventFlags::EF_NONE);
   }
 
+  void PressEnter() {
+    ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
+    generator.PressKey(ui::KeyboardCode::VKEY_RETURN, ui::EventFlags::EF_NONE);
+  }
+
   void PressUp() {
     ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
     generator.PressKey(ui::KeyboardCode::VKEY_UP, ui::EventFlags::EF_NONE);
@@ -242,8 +255,8 @@
   EXPECT_EQ(u"2021", header_year()->GetText());
 
   // Scrolls to the next month.
-  scroll_view_()->ScrollToPosition(scroll_view_()->vertical_scroll_bar(),
-                                   NextMonthPosition());
+  scroll_view()->ScrollToPosition(scroll_view()->vertical_scroll_bar(),
+                                  NextMonthPosition());
 
   EXPECT_EQ(u"October", GetPreviousLabelText());
   EXPECT_EQ(u"November", GetCurrentLabelText());
@@ -251,8 +264,8 @@
   EXPECT_EQ(u"November", month_header()->GetText());
   EXPECT_EQ(u"2021", header_year()->GetText());
 
-  scroll_view_()->ScrollToPosition(scroll_view_()->vertical_scroll_bar(),
-                                   NextMonthPosition());
+  scroll_view()->ScrollToPosition(scroll_view()->vertical_scroll_bar(),
+                                  NextMonthPosition());
 
   EXPECT_EQ(u"November", GetPreviousLabelText());
   EXPECT_EQ(u"December", GetCurrentLabelText());
@@ -260,8 +273,8 @@
   EXPECT_EQ(u"December", month_header()->GetText());
   EXPECT_EQ(u"2021", header_year()->GetText());
 
-  scroll_view_()->ScrollToPosition(scroll_view_()->vertical_scroll_bar(),
-                                   NextMonthPosition());
+  scroll_view()->ScrollToPosition(scroll_view()->vertical_scroll_bar(),
+                                  NextMonthPosition());
 
   EXPECT_EQ(u"December", GetPreviousLabelText());
   EXPECT_EQ(u"January2022", GetCurrentLabelText());
@@ -403,8 +416,9 @@
   EXPECT_EQ(reset_to_today_button(), focus_manager->GetFocusedView());
 
   PressTab();  // Settings button.
-  PressTab();  // Moves to down button.
-  PressTab();  // Moves to up button.
+  PressTab();  // Moves to the down button.
+  PressTab();  // Moves to the up button.
+  PressTab();  // Moves to the scroll view.
 
   // Moves to the the 7th date cell, which is the date of "today".
   PressTab();
@@ -419,6 +433,7 @@
   PressTab();  // Moves to settings button.
   PressTab();  // Moves to down button.
   PressTab();  // Moves to up button.
+  PressTab();  // Moves to the scroll view.
 
   // Moves to the the 7th date cell, which is the date of "today".
   PressTab();
@@ -446,6 +461,7 @@
   PressTab();  // Settings button.
   PressTab();  // Moves to down button.
   PressTab();  // Moves to up button.
+  PressTab();  // Moves to the scroll view.
 
   auto* focus_manager = calendar_view()->GetFocusManager();
   // Moves to the the 7th date cell, which is the date of "today".
@@ -517,6 +533,7 @@
   PressTab();  // Settings button.
   PressTab();  // Moves to down button.
   PressTab();  // Moves to up button.
+  PressTab();  // Moves to the scroll view.
 
   auto* focus_manager = calendar_view()->GetFocusManager();
   // Moves to the the 7th date cell, which is the date of "today".
@@ -561,6 +578,66 @@
   EXPECT_EQ(u"April", GetCurrentLabelText());
 }
 
+TEST_F(CalendarViewTest, ExpandableViewFocusing) {
+  base::Time date;
+  // Create a monthview based on Jun,7th 2021.
+  ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));
+
+  // Set time override.
+  SetFakeNow(date);
+  base::subtle::ScopedTimeClockOverrides time_override(
+      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
+      /*thread_ticks_override=*/nullptr);
+
+  CreateCalendarView();
+
+  EXPECT_EQ(views::ScrollView::ScrollBarMode::kHiddenButEnabled,
+            GetScrollBarMode());
+
+  // Generates a tab key press.
+  PressTab();  // Focusing on the back button.
+  PressTab();  // Today's button.
+  PressTab();  // Settings button.
+  PressTab();  // Moves to down button.
+  PressTab();  // Moves to up button.
+  PressTab();  // Moves to the scroll view.
+
+  EXPECT_EQ(views::ScrollView::ScrollBarMode::kHiddenButEnabled,
+            GetScrollBarMode());
+
+  auto* focus_manager = calendar_view()->GetFocusManager();
+  // Moves to the the 7th date cell, which is the date of "today".
+  PressTab();
+  EXPECT_EQ(u"7",
+            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
+                ->GetText());
+  EXPECT_EQ(u"June", GetCurrentLabelText());
+
+  // Opens the event list.
+  PressEnter();
+  EXPECT_EQ(views::ScrollView::ScrollBarMode::kDisabled, GetScrollBarMode());
+
+  // Tapping on up arrow keys should go to the previous month, which mens the
+  // scroll bar is enabled during the key pressed.
+  PressUp();
+  EXPECT_EQ(u"31",
+            static_cast<views::LabelButton*>(focus_manager->GetFocusedView())
+                ->GetText());
+  EXPECT_EQ(u"May", GetCurrentLabelText());
+  EXPECT_EQ(views::ScrollView::ScrollBarMode::kDisabled, GetScrollBarMode());
+
+  // Moves to the event list.
+  PressTab();
+  EXPECT_EQ(close_button(), focus_manager->GetFocusedView());
+
+  // Goes back to back button.
+  PressTab();
+
+  // Moves to the next focusable view. Today's button.
+  PressTab();
+  EXPECT_EQ(reset_to_today_button(), focus_manager->GetFocusedView());
+}
+
 // A test class for testing animation. This class cannot set fake now since it's
 // using `MOCK_TIME` to test the animations.
 class CalendarViewAnimationTest : public AshTestBase {
diff --git a/ash/system/tray/tray_bubble_view.cc b/ash/system/tray/tray_bubble_view.cc
index ab5dd5c..0aa4834 100644
--- a/ash/system/tray/tray_bubble_view.cc
+++ b/ash/system/tray/tray_bubble_view.cc
@@ -9,6 +9,7 @@
 
 #include "ash/accelerators/accelerator_controller_impl.h"
 #include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/bubble/bubble_constants.h"
 #include "ash/public/cpp/accelerators.h"
 #include "ash/public/cpp/style/color_provider.h"
 #include "ash/shell.h"
@@ -273,8 +274,7 @@
     DCHECK(!init_params.has_shadow);
     SetPaintToLayer(ui::LAYER_SOLID_COLOR);
 
-    layer()->SetRoundedCornerRadius(
-        gfx::RoundedCornersF{kUnifiedTrayCornerRadius});
+    layer()->SetRoundedCornerRadius(gfx::RoundedCornersF{kBubbleCornerRadius});
     layer()->SetFillsBoundsOpaquely(false);
     layer()->SetIsFastRoundedCorner(true);
     layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h
index 6d0e662..311e82c 100644
--- a/ash/system/tray/tray_constants.h
+++ b/ash/system/tray/tray_constants.h
@@ -121,7 +121,6 @@
 constexpr int kStackedNotificationBarCollapsedHeight = 40;
 constexpr int kNotificationIconStackThreshold = 28;
 constexpr int kUnifiedSliderViewSpacing = 12;
-constexpr int kUnifiedMenuPadding = 8;
 constexpr int kUnifiedMessageCenterBubbleSpacing = 8;
 constexpr int kUnifiedNotificationCenterSpacing = 16;
 constexpr int kUnifiedTrayBatteryIconSize = 20;
@@ -133,7 +132,6 @@
 constexpr int kUnifiedTraySpacingBetweenIcons = 6;
 constexpr int kUnifiedTrayBatteryWidth = 12;
 constexpr int kUnifiedTrayBatteryBottomPadding = 1;
-constexpr int kUnifiedTrayCornerRadius = 16;
 constexpr int kUnifiedTrayContentPadding = 12;
 constexpr int kUnifiedTopShortcutSpacing = 16;
 constexpr int kUnifiedNotificationHiddenLineHeight = 20;
diff --git a/ash/system/tray/tray_utils.cc b/ash/system/tray/tray_utils.cc
index 6dfe54ac..62ad4bc 100644
--- a/ash/system/tray/tray_utils.cc
+++ b/ash/system/tray/tray_utils.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "ash/bubble/bubble_constants.h"
 #include "ash/public/cpp/shelf_config.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/shelf/shelf.h"
@@ -75,8 +76,8 @@
   // Decrease bottom and right insets to compensate for the adjustment of
   // the respective edges in Shelf::GetSystemTrayAnchorRect().
   gfx::Insets insets = gfx::Insets(
-      kUnifiedMenuPadding, kUnifiedMenuPadding, kUnifiedMenuPadding - 1,
-      kUnifiedMenuPadding - (base::i18n::IsRTL() ? 0 : 1));
+      kBubbleMenuPadding, kBubbleMenuPadding, kBubbleMenuPadding - 1,
+      kBubbleMenuPadding - (base::i18n::IsRTL() ? 0 : 1));
 
   // The work area in tablet mode always uses the in-app shelf height, which is
   // shorter than the standard shelf height. In this state, we need to add back
@@ -127,13 +128,13 @@
   switch (shelf->alignment()) {
     case ShelfAlignment::kBottom:
     case ShelfAlignment::kBottomLocked:
-      insets.set_bottom(kUnifiedMenuPadding);
+      insets.set_bottom(kBubbleMenuPadding);
       break;
     case ShelfAlignment::kLeft:
-      insets.set_left(kUnifiedMenuPadding);
+      insets.set_left(kBubbleMenuPadding);
       break;
     case ShelfAlignment::kRight:
-      insets.set_right(kUnifiedMenuPadding);
+      insets.set_right(kBubbleMenuPadding);
       break;
   }
   return insets;
diff --git a/ash/system/unified/notification_hidden_view.cc b/ash/system/unified/notification_hidden_view.cc
index bdf4709b..50eb6a07 100644
--- a/ash/system/unified/notification_hidden_view.cc
+++ b/ash/system/unified/notification_hidden_view.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/unified/notification_hidden_view.h"
 
+#include "ash/bubble/bubble_constants.h"
 #include "ash/login/login_screen_controller.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -52,7 +53,7 @@
       views::CreateEmptyBorder(kUnifiedNotificationHiddenPadding));
 
   container_->SetBackground(views::CreateRoundedRectBackground(
-      GetBackgroundColor(), kUnifiedTrayCornerRadius));
+      GetBackgroundColor(), kBubbleCornerRadius));
 
   auto* layout =
       container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
diff --git a/ash/system/unified/unified_slider_bubble_controller.cc b/ash/system/unified/unified_slider_bubble_controller.cc
index 6aa296f..3c0bf10 100644
--- a/ash/system/unified/unified_slider_bubble_controller.cc
+++ b/ash/system/unified/unified_slider_bubble_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/unified/unified_slider_bubble_controller.h"
 
+#include "ash/bubble/bubble_constants.h"
 #include "ash/constants/ash_features.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller_impl.h"
@@ -196,7 +197,7 @@
   // Decrease bottom and right insets to compensate for the adjustment of
   // the respective edges in Shelf::GetSystemTrayAnchorRect().
   init_params.insets = GetTrayBubbleInsets();
-  init_params.corner_radius = kUnifiedTrayCornerRadius;
+  init_params.corner_radius = kBubbleCornerRadius;
   init_params.has_shadow = false;
   init_params.translucent = true;
 
diff --git a/ash/system/unified/unified_system_tray_bubble.cc b/ash/system/unified/unified_system_tray_bubble.cc
index 066d7fd..74da714 100644
--- a/ash/system/unified/unified_system_tray_bubble.cc
+++ b/ash/system/unified/unified_system_tray_bubble.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/unified/unified_system_tray_bubble.h"
 
+#include "ash/bubble/bubble_constants.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/system/message_center/unified_message_center_bubble.h"
@@ -82,7 +83,7 @@
   init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
   init_params.anchor_rect = tray->shelf()->GetSystemTrayAnchorRect();
   init_params.insets = GetTrayBubbleInsets();
-  init_params.corner_radius = kUnifiedTrayCornerRadius;
+  init_params.corner_radius = kBubbleCornerRadius;
   init_params.has_shadow = false;
   init_params.close_on_deactivate = false;
   init_params.reroute_event_handler = true;
@@ -242,7 +243,7 @@
       WorkAreaInsets::ForWindow(tray_->shelf()->GetWindow()->GetRootWindow());
   int free_space_height_above_anchor =
       bottom - work_area->user_work_area_bounds().y();
-  return free_space_height_above_anchor - kUnifiedMenuPadding * 2;
+  return free_space_height_above_anchor - kBubbleMenuPadding * 2;
 }
 
 bool UnifiedSystemTrayBubble::FocusOut(bool reverse) {
diff --git a/ash/webui/personalization_app/resources/trusted/personalization_reducers.ts b/ash/webui/personalization_app/resources/trusted/personalization_reducers.ts
index 489c2b6..5dd2240 100644
--- a/ash/webui/personalization_app/resources/trusted/personalization_reducers.ts
+++ b/ash/webui/personalization_app/resources/trusted/personalization_reducers.ts
@@ -7,11 +7,12 @@
  * pure function that returns a new state object if anything has changed.
  * @see [redux tutorial]{@link https://redux.js.org/tutorials/fundamentals/part-3-state-actions-reducers}
  */
-
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {FilePath} from 'chrome://resources/mojo/mojo/public/mojom/base/file_path.mojom-webui.js';
 
+import {isNonEmptyArray} from '../common/utils.js';
+
 import {ActionName} from './personalization_actions.js';
 import {Actions} from './personalization_actions.js';
 import {CurrentWallpaper, WallpaperCollection, WallpaperImage} from './personalization_app.mojom-webui.js';
@@ -352,7 +353,8 @@
 }
 
 function errorReducer(
-    state: string|null, action: Actions, _: PersonalizationState): string|null {
+    state: string|null, action: Actions,
+    globalState: PersonalizationState): string|null {
   switch (action.name) {
     case ActionName.END_SELECT_IMAGE:
       const {success} = action;
@@ -366,6 +368,25 @@
         return state;
       }
       return state || loadTimeData.getString('loadWallpaperError');
+    // Show network error toast if local images are available but online
+    // collections are failed to load. As local images and online collections
+    // are loaded asynchronously, we need to check the above condition for both
+    // SET_LOCAL_IMAGES and SET_COLLECTIONS actions.
+    case ActionName.SET_LOCAL_IMAGES:
+      const {images} = action;
+      if (isNonEmptyArray(images) && !globalState.loading.collections &&
+          !isNonEmptyArray(globalState.backdrop.collections)) {
+        return state || loadTimeData.getString('networkError');
+      }
+      return state;
+    case ActionName.SET_COLLECTIONS:
+      const {collections} = action;
+      if (!globalState.loading.local.images &&
+          isNonEmptyArray(globalState.local.images) &&
+          !isNonEmptyArray(collections)) {
+        return state || loadTimeData.getString('networkError');
+      }
+      return state;
     case ActionName.DISMISS_ERROR:
       if (!state) {
         console.warn(
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.js b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.js
index d721760e..e6b0492 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.js
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.js
@@ -88,7 +88,6 @@
        */
       collections_: {
         type: Array,
-        observer: 'onCollectionsChanged_',
       },
 
       /** @private */
@@ -157,7 +156,15 @@
        */
       localImages_: {
         type: Array,
-        observer: 'onLocalImagesChanged_',
+      },
+
+      /**
+       * Whether the local image list is currently loading.
+       * @type {boolean}
+       * @private
+       */
+      localImagesLoading_: {
+        type: Boolean,
       },
 
       /**
@@ -183,16 +190,18 @@
         type: Boolean,
         // Call computed functions with their dependencies as arguments so that
         // polymer knows when to re-run the computation.
-        computed: 'computeHasError_(collections_, collectionsLoading_)',
+        computed: 'computeHasError_(collections_, collectionsLoading_, localImages_, localImagesLoading_)',
       },
     };
   }
 
   static get observers() {
     return [
+      'onCollectionsChanged_(collections_, collectionsLoading_)',
       'onCollectionImagesChanged_(images_, imagesLoading_)',
       'onGooglePhotosChanged_(googlePhotos_, googlePhotosLoading_)',
       'onGooglePhotosCountChanged_(googlePhotosCount_, googlePhotosCountLoading_)',
+      'onLocalImagesChanged_(localImages_, localImagesLoading_)',
       'onLocalImageDataChanged_(localImages_, localImageData_, localImageDataLoading_)',
     ];
   }
@@ -225,6 +234,7 @@
     this.watch('images_', state => state.backdrop.images);
     this.watch('imagesLoading_', state => state.loading.images);
     this.watch('localImages_', state => state.local.images);
+    this.watch('localImagesLoading_', state => state.loading.local.images);
     this.watch('localImageData_', state => state.local.data);
     this.watch('localImageDataLoading_', state => state.loading.local.data);
     this.updateFromStore();
@@ -247,12 +257,39 @@
   /**
    * @param {?Array<!WallpaperCollection>}
    *     collections
-   * @param {boolean} loading
+   * @param {boolean} collectionsLoading
+   * @param {Array<!mojoBase.mojom.FilePath>}
+   *     localImages
+   * @param {boolean} localImagesLoading
    * @return {boolean}
    * @private
    */
-  computeHasError_(collections, loading) {
-    return !loading && !isNonEmptyArray(collections);
+  computeHasError_(
+      collections, collectionsLoading, localImages, localImagesLoading) {
+    return this.localImagesError_(localImages, localImagesLoading) &&
+        this.collectionsError_(collections, collectionsLoading);
+  }
+
+  /**
+   * @param {?Array<!WallpaperCollection>}
+   *     collections
+   * @param {boolean} collectionsLoading
+   * @return {boolean}
+   * @private
+   */
+  collectionsError_(collections, collectionsLoading) {
+    return !collectionsLoading && !isNonEmptyArray(collections);
+  }
+
+  /**
+   * @param {Array<!mojoBase.mojom.FilePath>}
+   *     localImages
+   * @param {boolean} localImagesLoading
+   * @return {boolean}
+   * @private
+   */
+  localImagesError_(localImages, localImagesLoading) {
+    return !localImagesLoading && !isNonEmptyArray(localImages);
   }
 
   /**
@@ -261,8 +298,10 @@
    *     collections
    * @private
    */
-  async onCollectionsChanged_(collections) {
-    if (isNonEmptyArray(collections)) {
+  async onCollectionsChanged_(collections, collectionsLoading) {
+    // Check whether collections are loaded before sending to
+    // the iframe. Collections could be null/empty array.
+    if (!collectionsLoading) {
       const iframe = await this.iframePromise_;
       sendCollectionsFunction(iframe.contentWindow, collections);
     }
@@ -341,15 +380,16 @@
 
   /**
    * Send updated local images list to the iframe.
-   * @param {?Array<!mojoBase.mojom.FilePath>} value
+   * @param {?Array<!mojoBase.mojom.FilePath>} localImages
+   * @param {boolean} localImagesLoading
    * @private
    */
-  async onLocalImagesChanged_(value) {
+  async onLocalImagesChanged_(localImages, localImagesLoading) {
     this.didSendLocalImageData_ = false;
-    if (Array.isArray(value)) {
+    if (!localImagesLoading && Array.isArray(localImages)) {
       const iframe = await this.iframePromise_;
       sendLocalImagesFunction(
-          /** @type {!Window} */ (iframe.contentWindow), value);
+          /** @type {!Window} */ (iframe.contentWindow), localImages);
     }
   }
 
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts
index c6597f1..6b7b412 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts
@@ -19,7 +19,7 @@
  */
 
 /** Fetch wallpaper collections and save them to the store. */
-async function fetchCollections(
+export async function fetchCollections(
     provider: WallpaperProviderInterface,
     store: PersonalizationStore): Promise<void> {
   let {collections} = await provider.fetchCollections();
@@ -126,7 +126,7 @@
 }
 
 /** Get list of local images from disk and save it to the store. */
-async function getLocalImages(
+export async function getLocalImages(
     provider: WallpaperProviderInterface,
     store: PersonalizationStore): Promise<void> {
   store.dispatch(action.beginLoadLocalImagesAction());
diff --git a/ash/webui/personalization_app/resources/untrusted/iframe_api.ts b/ash/webui/personalization_app/resources/untrusted/iframe_api.ts
index 4d60569..13ce9a7 100644
--- a/ash/webui/personalization_app/resources/untrusted/iframe_api.ts
+++ b/ash/webui/personalization_app/resources/untrusted/iframe_api.ts
@@ -70,8 +70,10 @@
   const data: constants.Events = event.data;
   switch (data.type) {
     case constants.EventType.SEND_COLLECTIONS: {
-      assert(isNonEmptyArray(data.collections), 'Expected collections array');
-      return data.collections;
+      assert(
+          isNullOrArray(data.collections),
+          'Expected collections array or null');
+      return data.collections ?? [];
     }
     case constants.EventType.SEND_GOOGLE_PHOTOS_COUNT: {
       assert(isNullOrBigint(data.count), 'Expected photos count');
diff --git a/ash/webui/shimless_rma/backend/shimless_rma_service.cc b/ash/webui/shimless_rma/backend/shimless_rma_service.cc
index e07b5d8..a781d78 100644
--- a/ash/webui/shimless_rma/backend/shimless_rma_service.cc
+++ b/ash/webui/shimless_rma/backend/shimless_rma_service.cc
@@ -1029,15 +1029,21 @@
     AbortRmaCallback callback,
     bool reboot,
     absl::optional<rmad::AbortRmaReply> response) {
+  const bool rma_not_required =
+      critical_error_occurred_ ||
+      (response && response->error() == rmad::RMAD_ERROR_RMA_NOT_REQUIRED);
+  // Send status before shutting down or restarting Chrome session.
   if (!response) {
     LOG(ERROR) << "Failed to call rmad::AbortRma";
     std::move(callback).Run(rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+  } else if (rma_not_required) {
+    std::move(callback).Run(rmad::RMAD_ERROR_OK);
   } else {
     std::move(callback).Run(response->error());
   }
-  // Only reboot or exit to login if abort was successful or a critical error
-  // has occurred.
-  if (critical_error_occurred_ || response->error() == rmad::RMAD_ERROR_OK) {
+  // Only reboot or exit to login if abort was successful (state will be
+  // RMAD_ERROR_RMA_NOT_REQUIRED) or a critical error has occurred.
+  if (rma_not_required) {
     if (reboot) {
       VLOG(1) << "Rebooting...";
       chromeos::PowerManagerClient::Get()->RequestRestart(
diff --git a/ash/webui/shimless_rma/resources/mojo_interface_provider.js b/ash/webui/shimless_rma/resources/mojo_interface_provider.js
index 43fd629..6790c63 100644
--- a/ash/webui/shimless_rma/resources/mojo_interface_provider.js
+++ b/ash/webui/shimless_rma/resources/mojo_interface_provider.js
@@ -42,7 +42,7 @@
 
   service.setAsyncOperationDelayMs(500);
 
-  service.setAbortRmaResult(RmadErrorCode.kOk);
+  service.setAbortRmaResult(RmadErrorCode.kRmaNotRequired);
 
   service.automaticallyTriggerHardwareVerificationStatusObservation();
 
diff --git a/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.html b/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.html
index b373475..d08b98a 100644
--- a/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.html
+++ b/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.html
@@ -2,6 +2,26 @@
   #dialogBody {
     overflow-wrap: anywhere;
   }
+
+  cr-input {
+    --cr-input-error-display: none;
+  }
+
+  #inputValidationLabel {
+    color: var(--google-grey-600);
+  }
+
+  :host([rsu-code-invalid_]) #inputValidationLabel {
+    color: red;
+  }
+
+  #rsuCodeLengthLabel {
+    float: right;
+  }
+
+  #inputContainer {
+    width: 275px;
+  }
 </style>
 
 <base-page orientation="column">
@@ -10,13 +30,19 @@
     <div>
       <span inner-h-t-m-l="[[rsuInstructionsText_]]"></span>
     </div>
-    <cr-input
-        on-change="onRsuCodeChanged_"
-        label="[[i18n('rsuCodeLabelText')]]"
-        placeholder="[[i18n('rsuCodePlaceHolderText')]]"
-        id="rsuCode"
-        value="{{rsuCode_}}">
-    </cr-input>
+    <div id="inputContainer">
+      <cr-input
+          id="rsuCode"
+          value="{{rsuCode_}}"
+          pattern="[[rsuCodeValidationRegex_]]"
+          invalid="{{rsuCodeInvalid_}}"
+          auto-validate>
+      </cr-input>
+      <div id="inputValidationLabel">
+        [[i18n('rsuCodeLabelText')]]
+        <span id="rsuCodeLengthLabel">[[rsuCodeLengthLabel_]]</span>
+      </div>
+    </div>
   </div>
   <div slot="body">
     <canvas id="qrCodeCanvas" width="[[canvasSize_]]" height="[[canvasSize_]]">
diff --git a/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.js b/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.js
index ec91716..23a0407 100644
--- a/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.js
+++ b/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.js
@@ -69,6 +69,7 @@
       rsuCode_: {
         type: String,
         value: '',
+        observer: 'onRsuCodeChanged_',
       },
 
       /** @protected */
@@ -83,6 +84,26 @@
         value: '',
         computed: 'computeRsuChallengeLinkText_(rsuHwid_, rsuChallenge_)',
       },
+
+      /** @protected */
+      rsuCodeValidationRegex_: {
+        type: String,
+        value: '.{1,8}',
+        readOnly: true,
+      },
+
+      /** @protected {boolean} */
+      rsuCodeInvalid_: {
+        type: Boolean,
+        value: false,
+        reflectToAttribute: true,
+      },
+
+      /** @protected */
+      rsuCodeLengthLabel_: {
+        type: String,
+        computed: 'computeRsuCodeLengthLabel_(rsuCode_)',
+      },
     };
   }
 
@@ -139,10 +160,8 @@
   }
 
   /**
-   * @private
    * @return {boolean}
-   * TODO(gavindodd): Add basic validation for the format of RSU code.
-   * Can this use cr-input autovalidate?
+   * @private
    */
   rsuCodeIsPlausible_() {
     return !!this.rsuCode_ && this.rsuCode_.length == 8;
@@ -202,6 +221,14 @@
   closeDialog_() {
     this.shadowRoot.querySelector('#rsuChallengeDialog').close();
   }
+
+  /**
+   * @return {string}
+   * @private
+   */
+  computeRsuCodeLengthLabel_() {
+    return this.rsuCode_.length + '/8';
+  }
 }
 
 customElements.define(
diff --git a/ash/webui/shimless_rma/shimless_rma.cc b/ash/webui/shimless_rma/shimless_rma.cc
index c42fe40..2ac3c051 100644
--- a/ash/webui/shimless_rma/shimless_rma.cc
+++ b/ash/webui/shimless_rma/shimless_rma.cc
@@ -140,7 +140,6 @@
       {"rsuChallengeDialogTitleText",
        IDS_SHIMLESS_RMA_RSU_CHALLENGE_DIALOG_TITLE},
       {"rsuCodeLabelText", IDS_SHIMLESS_RMA_RSU_CODE_LABEL},
-      {"rsuCodePlaceHolderText", IDS_SHIMLESS_RMA_RSU_CODE_PLACEHOLDER},
       {"rsuChallengeDialogDoneButtonLabel",
        IDS_SHIMLESS_RMA_RSU_CHALLENGE_DIALOG_DONE_BUTTON},
       // Manual WP disable complete
diff --git a/ash/wm/desks/desk.cc b/ash/wm/desks/desk.cc
index ae04b63..06ea15f 100644
--- a/ash/wm/desks/desk.cc
+++ b/ash/wm/desks/desk.cc
@@ -498,9 +498,6 @@
   DCHECK(target_root);
   DCHECK(base::Contains(windows_, window));
   DCHECK(this != target_desk);
-  // The desks bar should not be allowed to move individually to another desk.
-  // Only as part of `MoveWindowsToDesk()` when the desk is removed.
-  DCHECK_NE(window->GetId(), kShellWindowId_DesksBarWindow);
 
   {
     ScopedWindowPositionerDisabler window_positioner_disabler;
diff --git a/ash/wm/desks/desks_bar_view.cc b/ash/wm/desks/desks_bar_view.cc
index b5c4752..5923d36 100644
--- a/ash/wm/desks/desks_bar_view.cc
+++ b/ash/wm/desks/desks_bar_view.cc
@@ -31,6 +31,7 @@
 #include "ash/wm/desks/templates/desks_templates_presenter.h"
 #include "ash/wm/desks/templates/desks_templates_util.h"
 #include "ash/wm/desks/zero_state_button.h"
+#include "ash/wm/haptics_util.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_grid.h"
 #include "ash/wm/overview/overview_highlight_controller.h"
@@ -43,6 +44,7 @@
 #include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/events/devices/device_data_manager.h"
+#include "ui/events/devices/haptic_touchpad_effects.h"
 #include "ui/events/devices/input_device.h"
 #include "ui/events/event_observer.h"
 #include "ui/events/types/event_type.h"
@@ -537,7 +539,7 @@
   // Initialize and start drag.
   gfx::PointF location = event.target()->GetScreenLocationF(event);
   InitDragDesk(mini_view, location);
-  StartDragDesk(mini_view, location);
+  StartDragDesk(mini_view, location, event.IsMouseEvent());
 }
 
 void DesksBarView::HandleDragEvent(DeskMiniView* mini_view,
@@ -553,7 +555,7 @@
   // continue drag.
   switch (drag_proxy_->state()) {
     case DeskDragProxy::State::kInitialized:
-      StartDragDesk(mini_view, location);
+      StartDragDesk(mini_view, location, event.IsMouseEvent());
       break;
     case DeskDragProxy::State::kStarted:
       ContinueDragDesk(mini_view, location);
@@ -606,7 +608,8 @@
 }
 
 void DesksBarView::StartDragDesk(DeskMiniView* mini_view,
-                                 const gfx::PointF& location_in_screen) {
+                                 const gfx::PointF& location_in_screen,
+                                 bool is_mouse_dragging) {
   DCHECK(drag_view_);
   DCHECK(drag_proxy_);
   DCHECK_EQ(mini_view, drag_view_);
@@ -620,6 +623,13 @@
   drag_proxy_->InitAndScaleAndMoveToX(location_in_screen.x());
 
   Shell::Get()->cursor_manager()->SetCursor(ui::mojom::CursorType::kGrabbing);
+
+  // Fire a haptic event if necessary.
+  if (is_mouse_dragging) {
+    haptics_util::PlayHapticTouchpadEffect(
+        ui::HapticTouchpadEffect::kTick,
+        ui::HapticTouchpadEffectStrength::kMedium);
+  }
 }
 
 void DesksBarView::ContinueDragDesk(DeskMiniView* mini_view,
diff --git a/ash/wm/desks/desks_bar_view.h b/ash/wm/desks/desks_bar_view.h
index a2e34f9..7a892de 100644
--- a/ash/wm/desks/desks_bar_view.h
+++ b/ash/wm/desks/desks_bar_view.h
@@ -136,9 +136,11 @@
   // Finalize any unfinished drag & drop. Initialize a new drag proxy.
   void InitDragDesk(DeskMiniView* mini_view,
                     const gfx::PointF& location_in_screen);
-  // Start to drag. Scale up the drag proxy.
+  // Start to drag. Scale up the drag proxy. `is_mouse_dragging` is true when
+  // triggered by mouse/trackpad, false when triggered by touch.
   void StartDragDesk(DeskMiniView* mini_view,
-                     const gfx::PointF& location_in_screen);
+                     const gfx::PointF& location_in_screen,
+                     bool is_mouse_dragging);
   // Reorder desks according to the drag proxy's location.
   void ContinueDragDesk(DeskMiniView* mini_view,
                         const gfx::PointF& location_in_screen);
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index 6413733..70aae8b 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -118,8 +118,11 @@
 void AppendWindowsToOverview(const std::vector<aura::Window*>& windows) {
   DCHECK(Shell::Get()->overview_controller()->InOverviewSession());
 
+  // TODO(dandersson): See if we can remove this code and just let
+  // OverviewSession do its thing when the windows are moved to the new desk.
   auto* overview_session =
       Shell::Get()->overview_controller()->overview_session();
+  overview_session->set_auto_add_windows_enabled(false);
   for (auto* window :
        Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk)) {
     if (!base::Contains(windows, window) ||
@@ -129,6 +132,7 @@
 
     overview_session->AppendItem(window, /*reposition=*/true, /*animate=*/true);
   }
+  overview_session->set_auto_add_windows_enabled(true);
 }
 
 // Removes all the items that currently exist in overview.
@@ -560,6 +564,13 @@
     return;
   }
 
+  if (source == DesksSwitchSource::kLaunchTemplate) {
+    // Desk switch due to launching a template will immediately activate the new
+    // desk without animation.
+    ActivateDeskInternal(desk, /*update_window_activation=*/true);
+    return;
+  }
+
   // When switching desks we want to update window activation when leaving
   // overview or if nothing was active prior to switching desks. This will
   // ensure that after switching desks, we will try to focus a candidate window.
@@ -973,13 +984,35 @@
   desk->SetName(desk_name, /*set_by_user=*/true);
   // Force update user prefs because `SetName()` does not trigger it.
   desks_restore_util::UpdatePrimaryUserDeskNamesPrefs();
+
+  // We're staying in overview mode, so move desks bar window and the save
+  // template button to the new desk. They would otherwise disappear when the
+  // new desk is activated.
+  DCHECK(active_desk_);
+
+  // Since we're going to move certain windows from the currently active desk,
+  // this is going to implicitly modify that list. We therefore grab a copy of
+  // it to avoid issues with concurrent iteration and modification of the list.
+  auto active_desk_windows = active_desk_->windows();
+  for (aura::Window* window : active_desk_windows) {
+    if (window->GetId() == kShellWindowId_DesksBarWindow ||
+        window->GetId() == kShellWindowId_SaveDeskAsTemplateWindow) {
+      aura::Window* destination_container =
+          desk->GetDeskContainerForRoot(window->GetRootWindow());
+      destination_container->AddChild(window);
+    }
+  }
+
+  if (auto* session = Shell::Get()->overview_controller()->overview_session()) {
+    session->HideDesksTemplatesGrids();
+    for (auto& grid : session->grid_list())
+      grid->RemoveAllItemsForDesksTemplatesLaunch();
+  }
+
   ActivateDesk(desk, DesksSwitchSource::kLaunchTemplate);
-  DCHECK(animation_);
-  animation_->set_finished_callback(base::BindOnce(
-      [](base::OnceCallback<void(bool)> passed_callback) {
-        std::move(passed_callback).Run(/*success=*/true);
-      },
-      std::move(callback)));
+  DCHECK(!animation_);
+
+  std::move(callback).Run(/*success=*/true);
 }
 
 bool DesksController::OnSingleInstanceAppLaunchingFromTemplate(
@@ -1018,7 +1051,7 @@
           existing_app_instance_window)) {
     DCHECK(src_desk);
     DCHECK_NE(src_desk, active_desk_);
-    DCHECK(!Shell::Get()->overview_controller()->InOverviewSession());
+
     base::AutoReset<bool> in_progress(&are_desks_being_modified_, true);
     src_desk->MoveWindowToDesk(existing_app_instance_window, active_desk_,
                                existing_app_instance_window->GetRootWindow(),
diff --git a/ash/wm/desks/templates/desks_templates_animations.cc b/ash/wm/desks/templates/desks_templates_animations.cc
index b2d22b6..80b9926 100644
--- a/ash/wm/desks/templates/desks_templates_animations.cc
+++ b/ash/wm/desks/templates/desks_templates_animations.cc
@@ -8,17 +8,18 @@
 #include "ui/views/animation/animation_builder.h"
 #include "ui/views/view.h"
 
-// The time duration for widgets to fade in.
-constexpr int kFadeInDelay = 0;
-constexpr int kFadeInDuration = 100;
-
 namespace ash {
 
+namespace {
+
+// The time duration for widgets to fade in.
+constexpr int kFadeInDurationMs = 100;
+
+// The time duration for widgets to fade out.
+constexpr int kFadeOutDurationMs = 100;
+
 // Fade in animation using AnimationBuilder.
-void FadeInView(ui::Layer* layer,
-                int delay_in_ms,
-                int duration_in_ms,
-                gfx::Tween::Type tween_type = gfx::Tween::LINEAR) {
+void FadeInLayer(ui::Layer* layer, int duration_in_ms) {
   views::AnimationBuilder()
       .SetPreemptionStrategy(
           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
@@ -27,12 +28,30 @@
       .SetOpacity(layer, 0.0f)
       .Then()
       .SetDuration(base::Milliseconds(duration_in_ms))
-      .SetOpacity(layer, 1.0f, tween_type);
+      .SetOpacity(layer, 1.0f, gfx::Tween::LINEAR);
 }
 
-void PerformFadeInDesksTemplatesGridView(ui::Layer* layer) {
-  // TODO(sophiewen): Perform fade out of other overview items.
-  FadeInView(layer, kFadeInDelay, kFadeInDuration);
+// Fade out animation using AnimationBuilder.
+void FadeOutLayer(ui::Layer* layer, int duration_in_ms) {
+  views::AnimationBuilder()
+      .SetPreemptionStrategy(
+          ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
+      .Once()
+      .SetDuration(base::TimeDelta())
+      .SetOpacity(layer, 1.0f)
+      .Then()
+      .SetDuration(base::Milliseconds(duration_in_ms))
+      .SetOpacity(layer, 0.0f, gfx::Tween::LINEAR);
+}
+
+}  // namespace
+
+void PerformFadeInLayer(ui::Layer* layer) {
+  FadeInLayer(layer, kFadeInDurationMs);
+}
+
+void PerformFadeOutLayer(ui::Layer* layer) {
+  FadeOutLayer(layer, kFadeOutDurationMs);
 }
 
 }  // namespace ash
diff --git a/ash/wm/desks/templates/desks_templates_animations.h b/ash/wm/desks/templates/desks_templates_animations.h
index 1c8555f..efa7675 100644
--- a/ash/wm/desks/templates/desks_templates_animations.h
+++ b/ash/wm/desks/templates/desks_templates_animations.h
@@ -13,7 +13,10 @@
 
 // Animates the desks templates grid when it is shown, fading out current
 // overview items and widgets, and fading in the grid.
-void PerformFadeInDesksTemplatesGridView(ui::Layer* layer);
+void PerformFadeInLayer(ui::Layer* layer);
+
+// Animates linear fade out of overview items.
+void PerformFadeOutLayer(ui::Layer* layer);
 
 }  // namespace ash
 
diff --git a/ash/wm/desks/templates/desks_templates_grid_view.cc b/ash/wm/desks/templates/desks_templates_grid_view.cc
index 7f87c79..a591bda9 100644
--- a/ash/wm/desks/templates/desks_templates_grid_view.cc
+++ b/ash/wm/desks/templates/desks_templates_grid_view.cc
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/wm/desks/templates/desks_templates_animations.h"
 #include "ash/wm/desks/templates/desks_templates_item_view.h"
 #include "ash/wm/desks/templates/desks_templates_presenter.h"
 #include "ui/aura/window.h"
@@ -15,7 +16,6 @@
 #include "ui/compositor/layer.h"
 #include "ui/events/event_handler.h"
 #include "ui/views/layout/table_layout.h"
-#include "ui/views/widget/unique_widget_ptr.h"
 #include "ui/views/widget/widget.h"
 
 namespace ash {
@@ -61,11 +61,16 @@
 
 DesksTemplatesGridView::DesksTemplatesGridView() = default;
 
-DesksTemplatesGridView::~DesksTemplatesGridView() = default;
+DesksTemplatesGridView::~DesksTemplatesGridView() {
+  if (widget_window_) {
+    widget_window_->RemovePreTargetHandler(event_handler_.get());
+    widget_window_->RemoveObserver(this);
+  }
+}
 
 // static
-views::UniqueWidgetPtr DesksTemplatesGridView::CreateDesksTemplatesGridWidget(
-    aura::Window* root) {
+std::unique_ptr<views::Widget>
+DesksTemplatesGridView::CreateDesksTemplatesGridWidget(aura::Window* root) {
   DCHECK(root);
   DCHECK(root->IsRootWindow());
 
@@ -73,6 +78,7 @@
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
   params.activatable = views::Widget::InitParams::Activatable::kYes;
   params.accept_events = true;
+  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   // The parent should be a container that covers all the windows but is below
   // some other system UI features such as system tray and capture mode and also
   // below the system modal dialogs.
@@ -81,8 +87,7 @@
   params.parent = root->GetChildById(kShellWindowId_ShelfBubbleContainer);
   params.name = "DesksTemplatesGridWidget";
 
-  views::UniqueWidgetPtr widget(
-      std::make_unique<views::Widget>(std::move(params)));
+  auto widget = std::make_unique<views::Widget>(std::move(params));
   widget->SetContentsView(std::make_unique<DesksTemplatesGridView>());
 
   // Not opaque since we want to view the contents of the layer behind.
@@ -187,13 +192,15 @@
   // this window.
   event_handler_ = std::make_unique<DesksTemplatesEventHandler>(this);
   widget_window_ = GetWidget()->GetNativeWindow();
+  widget_window_->AddObserver(this);
   widget_window_->AddPreTargetHandler(event_handler_.get());
 }
 
-void DesksTemplatesGridView::RemovedFromWidget() {
+void DesksTemplatesGridView::OnWindowDestroying(aura::Window* window) {
+  DCHECK_EQ(window, widget_window_);
   DCHECK(event_handler_);
-  DCHECK(widget_window_);
   widget_window_->RemovePreTargetHandler(event_handler_.get());
+  widget_window_->RemoveObserver(this);
   event_handler_.reset();
   widget_window_ = nullptr;
 }
diff --git a/ash/wm/desks/templates/desks_templates_grid_view.h b/ash/wm/desks/templates/desks_templates_grid_view.h
index 2d079d5..da7ca030 100644
--- a/ash/wm/desks/templates/desks_templates_grid_view.h
+++ b/ash/wm/desks/templates/desks_templates_grid_view.h
@@ -7,12 +7,12 @@
 
 #include <vector>
 
+#include "ui/aura/window_observer.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/view.h"
 
 namespace views {
 class TableLayout;
-class UniqueWidgetPtr;
 }  // namespace views
 
 namespace ash {
@@ -23,7 +23,7 @@
 
 // A view that acts as the content view of the desks templates widget.
 // TODO(richui): Add details and ASCII.
-class DesksTemplatesGridView : public views::View {
+class DesksTemplatesGridView : public views::View, public aura::WindowObserver {
  public:
   METADATA_HEADER(DesksTemplatesGridView);
 
@@ -36,7 +36,7 @@
   // overview mode. This does not show the widget.
   // TODO(sammiequon): We might want this view to be part of the DesksWidget
   // depending on the animations.
-  static views::UniqueWidgetPtr CreateDesksTemplatesGridWidget(
+  static std::unique_ptr<views::Widget> CreateDesksTemplatesGridWidget(
       aura::Window* root);
 
   const std::vector<DesksTemplatesItemView*>& grid_items() const {
@@ -54,7 +54,9 @@
 
   // views::View:
   void AddedToWidget() override;
-  void RemovedFromWidget() override;
+
+  // aura::WindowObserver:
+  void OnWindowDestroying(aura::Window* window) override;
 
  private:
   friend class DesksTemplatesEventHandler;
diff --git a/ash/wm/desks/templates/desks_templates_unittest.cc b/ash/wm/desks/templates/desks_templates_unittest.cc
index 9382eea..cb4c7521 100644
--- a/ash/wm/desks/templates/desks_templates_unittest.cc
+++ b/ash/wm/desks/templates/desks_templates_unittest.cc
@@ -726,36 +726,29 @@
   ASSERT_EQ(1ul, GetAllEntries().size());
 
   // Click on the grid item to launch the template.
-  {
-    DeskSwitchAnimationWaiter waiter;
-    ClickOnView(GetItemViewFromTemplatesGrid(/*grid_item_index=*/0));
-    WaitForDesksTemplatesUI();
-    waiter.Wait();
-  }
+  ClickOnView(GetItemViewFromTemplatesGrid(/*grid_item_index=*/0));
+  WaitForDesksTemplatesUI();
 
   // Verify that we have created and activated a new desk.
   EXPECT_EQ(2ul, desks_controller->desks().size());
   EXPECT_EQ(1, desks_controller->GetActiveDeskIndex());
 
-  // Launching a template creates and activates a new desk, which also results
-  // in exiting overview mode, so we check to make sure overview is closed.
-  EXPECT_FALSE(InOverviewSession());
+  // Launching a template creates and activates a new desk without exiting
+  // overview mode, so we check that we're still in overview.
+  EXPECT_TRUE(InOverviewSession());
 
   // This section tests clicking on the "Use template" button to launch the
   // template.
+  ToggleOverview();
   OpenOverviewAndShowTemplatesGrid();
-  {
-    DeskSwitchAnimationWaiter waiter;
-    DesksTemplatesItemView* item_view = GetItemViewFromTemplatesGrid(
-        /*grid_item_index=*/0);
-    ClickOnView(DesksTemplatesItemViewTestApi(item_view).launch_button());
-    WaitForDesksTemplatesUI();
-    waiter.Wait();
-  }
+  DesksTemplatesItemView* item_view = GetItemViewFromTemplatesGrid(
+      /*grid_item_index=*/0);
+  ClickOnView(DesksTemplatesItemViewTestApi(item_view).launch_button());
+  WaitForDesksTemplatesUI();
 
   EXPECT_EQ(3ul, desks_controller->desks().size());
   EXPECT_EQ(2, desks_controller->GetActiveDeskIndex());
-  EXPECT_FALSE(InOverviewSession());
+  EXPECT_TRUE(InOverviewSession());
 }
 
 // Tests that the order of DesksTemplatesItemView is in order.
@@ -1308,16 +1301,13 @@
   OpenOverviewAndSaveTemplate(Shell::Get()->GetPrimaryRootWindow());
   ASSERT_EQ(1ul, GetAllEntries().size());
 
-  // Click on the grid item to launch the template. We should exit overview and
-  // there should be no crash.
-  DeskSwitchAnimationWaiter waiter;
+  // Click on the grid item to launch the template. We should remain in overview
+  // and there should be no crash.
   ClickOnView(GetItemViewFromTemplatesGrid(/*grid_item_index=*/0));
-  // Launching a template fetches it from the desk model asynchronously. Make
-  // sure the async call is done before waiting.
+  // Launching a template fetches it from the desk model asynchronously.
   WaitForDesksTemplatesUI();
-  waiter.Wait();
 
-  EXPECT_FALSE(InOverviewSession());
+  EXPECT_TRUE(InOverviewSession());
 }
 
 // Tests that there is no crash if we launch a template after deleting the
@@ -1339,16 +1329,12 @@
   // a template" button was not moved when the active desk was removed.
   RemoveDesk(desks_controller->active_desk());
 
-  // Click on the grid item to launch the template. We should exit overview and
-  // there should be no crash.
-  DeskSwitchAnimationWaiter waiter;
+  // Click on the grid item to launch the template. There should be no crash.
   ClickOnView(GetItemViewFromTemplatesGrid(/*grid_item_index=*/0));
-  // Launching a template fetches it from the desk model asynchronously. Make
-  // sure the async call is done before waiting.
+  // Launching a template fetches it from the desk model asynchronously.
   WaitForDesksTemplatesUI();
-  waiter.Wait();
 
-  EXPECT_FALSE(InOverviewSession());
+  EXPECT_TRUE(InOverviewSession());
 }
 
 // Tests that if we open the desks templates grid a second time during an
@@ -1717,10 +1703,8 @@
   ASSERT_EQ(1ul, GetAllEntries().size());
 
   // Click on the grid item to launch the template.
-  DeskSwitchAnimationWaiter waiter;
   ClickOnView(GetItemViewFromTemplatesGrid(/*grid_item_index=*/0));
   WaitForDesksTemplatesUI();
-  waiter.Wait();
 
   // Verify that we have created and activated a new desk.
   EXPECT_EQ(2ul, desks_controller->desks().size());
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index c28ecac9..6b2b3247 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -766,6 +766,12 @@
   }
 }
 
+void OverviewGrid::RemoveAllItemsForDesksTemplatesLaunch() {
+  for (auto& item : window_list_)
+    item->RestoreWindow(/*reset_tranform=*/true);
+  window_list_.clear();
+}
+
 void OverviewGrid::AddDropTargetForDraggingFromThisGrid(
     OverviewItem* dragged_item) {
   DCHECK(!drop_target_widget_);
@@ -1416,6 +1422,16 @@
   return dragged_item_over_bar;
 }
 
+int OverviewGrid::GetDeskIndexFromScreenLocation(
+    const gfx::Point& screen_location) {
+  auto* desks_controller = DesksController::Get();
+  for (auto* mini_view : desks_bar_view_->mini_views()) {
+    if (mini_view->IsPointOnMiniView(screen_location))
+      return desks_controller->GetDeskIndex(mini_view->desk());
+  }
+  return -1;
+}
+
 bool OverviewGrid::MaybeDropItemOnDeskMiniViewOrNewDeskButton(
     const gfx::Point& screen_location,
     OverviewItem* drag_item) {
@@ -1734,7 +1750,7 @@
   // Fade in the widget from its current opacity.
   // TODO(crbug.com/1277160): Consider adding animate flag to determine whether
   // to disable animations.
-  PerformFadeInDesksTemplatesGridView(desks_templates_grid_widget_->GetLayer());
+  PerformFadeInLayer(desks_templates_grid_widget_->GetLayer());
 
   UpdateSaveDeskAsTemplateButton();
 
@@ -1750,8 +1766,11 @@
   for (auto& overview_mode_item : window_list_)
     overview_mode_item->RevertHideForDesksTemplatesGrid();
 
-  if (exit_overview) {
-    desks_templates_grid_widget_->CloseNow();
+  if (exit_overview && overview_session_->enter_exit_overview_type() !=
+                           OverviewEnterExitType::kImmediateExit) {
+    FadeOutWidgetFromOverview(
+        std::move(desks_templates_grid_widget_),
+        OVERVIEW_ANIMATION_EXIT_OVERVIEW_MODE_DESKS_TEMPLATES_GRID_FADE_OUT);
     return;
   }
 
diff --git a/ash/wm/overview/overview_grid.h b/ash/wm/overview/overview_grid.h
index 491673f..d2cf22e 100644
--- a/ash/wm/overview/overview_grid.h
+++ b/ash/wm/overview/overview_grid.h
@@ -19,11 +19,9 @@
 #include "ash/wm/splitview/split_view_observer.h"
 #include "ash/wm/window_state.h"
 #include "base/containers/flat_set.h"
-#include "base/memory/weak_ptr.h"
 #include "ui/aura/window.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
-#include "ui/views/widget/unique_widget_ptr.h"
 
 namespace views {
 class Widget;
@@ -141,6 +139,12 @@
                   bool item_destroying,
                   bool reposition);
 
+  // Removes all overview items and restores the respective windows. This is
+  // used when launching a desks template. While this will empty the grid, it
+  // will *not* invoke `OverviewSession::OnGridEmpty()` since the grid is about
+  // to get filled with new windows.
+  void RemoveAllItemsForDesksTemplatesLaunch();
+
   // Adds a drop target for |dragged_item|, at the index immediately following
   // |dragged_item|. Repositions all items except |dragged_item|, so that the
   // drop target takes the place of |dragged_item|. Does not animate the
@@ -286,6 +290,10 @@
                               bool update_desks_bar_drag_details,
                               bool for_drop);
 
+  // Returns the desk index of the provided screen location if it belongs to
+  // any, otherwise `-1` will be returned.
+  int GetDeskIndexFromScreenLocation(const gfx::Point& screen_location);
+
   // Updates the drag details for DesksBarView to end the drag and move the
   // window of |drag_item| to another desk if it was dropped on a mini_view of
   // a desk that is different than that of the active desk or if dropped on the
@@ -577,7 +585,7 @@
   aura::Window* dragged_window_ = nullptr;
 
   // The widget that contains the view for all the existing templates.
-  views::UniqueWidgetPtr desks_templates_grid_widget_;
+  std::unique_ptr<views::Widget> desks_templates_grid_widget_;
 
   // The contents view of the above `desks_templates_grid_widget_` if created.
   DesksTemplatesGridView* desks_templates_grid_view_ = nullptr;
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index fff7468..89b6bcb 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -17,6 +17,7 @@
 #include "ash/style/default_color_constants.h"
 #include "ash/style/default_colors.h"
 #include "ash/wm/desks/desks_util.h"
+#include "ash/wm/desks/templates/desks_templates_animations.h"
 #include "ash/wm/drag_window_controller.h"
 #include "ash/wm/overview/delayed_animation_observer_impl.h"
 #include "ash/wm/overview/overview_constants.h"
@@ -187,7 +188,8 @@
     : root_window_(window->GetRootWindow()),
       transform_window_(this, window),
       overview_session_(overview_session),
-      overview_grid_(overview_grid) {
+      overview_grid_(overview_grid),
+      animation_disabler_(window) {
   CreateItemWidget();
   window->AddObserver(this);
   WindowState::Get(window)->AddObserver(this);
@@ -224,11 +226,11 @@
 void OverviewItem::RevertHideForDesksTemplatesGrid() {
   // `item_widget_` may be null during shutdown if the window is minimized.
   if (item_widget_)
-    item_widget_->GetLayer()->SetOpacity(1.0f);
+    PerformFadeInLayer(item_widget_->GetLayer());
 
   for (aura::Window* transient_child :
        GetTransientTreeIterator(transform_window_.window())) {
-    transient_child->layer()->SetOpacity(1.0f);
+    PerformFadeInLayer(transient_child->layer());
   }
 
   item_widget_event_blocker_.reset();
@@ -241,7 +243,8 @@
   RestoreWindow(/*reset_transform=*/true);
 }
 
-void OverviewItem::RestoreWindow(bool reset_transform) {
+void OverviewItem::RestoreWindow(bool reset_transform,
+                                 bool was_desks_templates_grid_showing) {
   // TODO(oshima): SplitViewController has its own logic to adjust the
   // target state in |SplitViewController::OnOverviewModeEnding|.
   // Unify the mechanism to control it and remove ifs.
@@ -252,7 +255,9 @@
   }
 
   overview_item_view_->OnOverviewItemWindowRestoring();
-  transform_window_.RestoreWindow(reset_transform);
+  transform_window_.RestoreWindow(
+      reset_transform,
+      /*transform_back=*/!was_desks_templates_grid_showing);
 
   if (transform_window_.IsMinimized()) {
     const auto enter_exit_type = overview_session_->enter_exit_overview_type();
diff --git a/ash/wm/overview/overview_item.h b/ash/wm/overview/overview_item.h
index 88028bf..a70f41ce 100644
--- a/ash/wm/overview/overview_item.h
+++ b/ash/wm/overview/overview_item.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "ash/ash_export.h"
+#include "ash/scoped_animation_disabler.h"
 #include "ash/wm/overview/overview_session.h"
 #include "ash/wm/overview/scoped_overview_transform_window.h"
 #include "ash/wm/window_state_observer.h"
@@ -59,15 +60,17 @@
   void OnMovingWindowToAnotherDesk();
 
   // Restores and animates the managed window to its non overview mode state.
-  // If |reset_transform| equals false, the window's transform will not be
-  // reset to identity transform when exiting overview mode. It's needed when
-  // dragging an Arc app window in overview mode to put it in split screen. In
-  // this case the restore of its transform needs to be deferred until the Arc
-  // app window is snapped successfully, otherwise the animation will look very
-  // ugly (the Arc app window enlarges itself to maximized window bounds and
-  // then shrinks to its snapped window bounds). Note if the window's transform
-  // is not reset here, it must be reset by someone else at some point.
-  void RestoreWindow(bool reset_transform);
+  // Doesn't animate if |was_desks_templates_grid_showing| is true. If
+  // |reset_transform| equals false, the window's transform will not be reset to
+  // identity transform when exiting overview mode. It's needed when dragging an
+  // Arc app window in overview mode to put it in split screen. In this case the
+  // restore of its transform needs to be deferred until the Arc app window is
+  // snapped successfully, otherwise the animation will look very ugly (the Arc
+  // app window enlarges itself to maximized window bounds and then shrinks to
+  // its snapped window bounds). Note if the window's transform is not reset
+  // here, it must be reset by someone else at some point.
+  void RestoreWindow(bool reset_transform,
+                     bool was_desks_templates_grid_showing = false);
 
   // Ensures that a possibly minimized window becomes visible after restore.
   void EnsureVisible();
@@ -445,6 +448,10 @@
   std::unique_ptr<aura::ScopedWindowEventTargetingBlocker>
       item_widget_event_blocker_;
 
+  // Disable animations on the contained window while it is being managed by the
+  // overview item.
+  ScopedAnimationDisabler animation_disabler_;
+
   base::WeakPtrFactory<OverviewItem> weak_ptr_factory_{this};
 };
 
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index f98e745..7452172d 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -32,6 +32,7 @@
 #include "ash/wm/desks/templates/desks_templates_dialog_controller.h"
 #include "ash/wm/desks/templates/desks_templates_presenter.h"
 #include "ash/wm/desks/templates/desks_templates_util.h"
+#include "ash/wm/haptics_util.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_delegate.h"
@@ -56,10 +57,12 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/events/devices/haptic_touchpad_effects.h"
 #include "ui/events/event.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/coordinate_conversion.h"
+#include "ui/wm/core/window_util.h"
 
 namespace ash {
 
@@ -257,6 +260,8 @@
   Shell::Get()->accessibility_controller()->TriggerAccessibilityAlert(
       AccessibilityAlert::WINDOW_OVERVIEW_MODE_ENTERED);
 
+  desks_controller_observation_.Observe(DesksController::Get());
+
   ignore_activations_ = false;
 }
 
@@ -264,10 +269,24 @@
 // may cause other, unrelated classes, to make indirect calls to
 // restoring_minimized_windows() on a partially destructed object.
 void OverviewSession::Shutdown() {
+  bool was_desks_templates_grid_showing = false;
+  for (auto& grid : grid_list_) {
+    if (grid->IsShowingDesksTemplatesGrid()) {
+      was_desks_templates_grid_showing = true;
+      break;
+    }
+  }
+
   // This should have been set already when the process of ending overview mode
   // began. See OverviewController::OnSelectionEnded().
   DCHECK(is_shutting_down_);
 
+  desks_controller_observation_.Reset();
+  if (observing_desk_) {
+    for (auto* root : Shell::GetAllRootWindows())
+      observing_desk_->GetDeskContainerForRoot(root)->RemoveObserver(this);
+  }
+
   Shell::Get()->RemovePreTargetHandler(this);
   Shell::Get()->RemoveShellObserver(this);
 
@@ -303,8 +322,10 @@
               : nullptr,
           OverviewTransition::kExit, /*target_bounds=*/{});
     }
-    for (const auto& overview_item : overview_grid->window_list())
-      overview_item->RestoreWindow(/*reset_transform=*/true);
+    for (const auto& overview_item : overview_grid->window_list()) {
+      overview_item->RestoreWindow(/*reset_transform=*/true,
+                                   was_desks_templates_grid_showing);
+    }
     remaining_items += overview_grid->size();
   }
 
@@ -321,10 +342,12 @@
     overview_grid->Shutdown(enter_exit_overview_type_);
 
   DCHECK(num_items_ >= remaining_items);
-  UMA_HISTOGRAM_COUNTS_100("Ash.Overview.OverviewClosedItems",
-                           num_items_ - remaining_items);
-  UMA_HISTOGRAM_MEDIUM_TIMES("Ash.Overview.TimeInOverview",
-                             base::Time::Now() - overview_start_time_);
+  if (!was_desks_templates_grid_showing) {
+    UMA_HISTOGRAM_COUNTS_100("Ash.Overview.OverviewClosedItems",
+                             num_items_ - remaining_items);
+    UMA_HISTOGRAM_MEDIUM_TIMES("Ash.Overview.TimeInOverview",
+                               base::Time::Now() - overview_start_time_);
+  }
 
   grid_list_.clear();
 
@@ -549,6 +572,7 @@
           ->IsDividerAnimating()) {
     return;
   }
+
   highlight_controller_->SetFocusHighlightVisibility(false);
   window_drag_controller_ = std::make_unique<OverviewWindowDragController>(
       this, item, is_touch_dragging);
@@ -558,6 +582,13 @@
     grid->OnSelectorItemDragStarted(item);
     grid->UpdateSaveDeskAsTemplateButton();
   }
+
+  // Fire a haptic event if necessary.
+  if (!is_touch_dragging) {
+    haptics_util::PlayHapticTouchpadEffect(
+        ui::HapticTouchpadEffect::kTick,
+        ui::HapticTouchpadEffectStrength::kMedium);
+  }
 }
 
 void OverviewSession::Drag(OverviewItem* item,
@@ -964,6 +995,35 @@
   UpdateNoWindowsWidgetOnEachGrid();
 }
 
+void OverviewSession::HideDesksTemplatesGrids() {
+  // Before hiding the templates grid, we need to explicitly activate the focus
+  // window. Otherwise, some other window may get activated as the templates
+  // grid is hidden, and this could in turn lead to exiting overview mode.
+  wm::ActivateWindow(GetOverviewFocusWindow());
+
+  for (auto& grid : grid_list_)
+    grid->HideDesksTemplatesGrid(/*exit_overview=*/false);
+}
+
+void OverviewSession::OnDeskAdded(const Desk* desk) {}
+void OverviewSession::OnDeskRemoved(const Desk* desk) {}
+void OverviewSession::OnDeskReordered(int old_index, int new_index) {}
+
+void OverviewSession::OnDeskActivationChanged(const Desk* activated,
+                                              const Desk* deactivated) {
+  observing_desk_ = activated;
+
+  for (auto* root : Shell::GetAllRootWindows()) {
+    activated->GetDeskContainerForRoot(root)->AddObserver(this);
+    deactivated->GetDeskContainerForRoot(root)->RemoveObserver(this);
+  }
+}
+
+void OverviewSession::OnDeskSwitchAnimationLaunching() {}
+void OverviewSession::OnDeskSwitchAnimationFinished() {}
+void OverviewSession::OnDeskNameChanged(const Desk* desk,
+                                        const std::u16string& new_name) {}
+
 void OverviewSession::OnDisplayAdded(const display::Display& display) {
   if (EndOverview(OverviewEndAction::kDisplayAdded))
     return;
@@ -994,6 +1054,25 @@
   active_window_before_overview_ = nullptr;
 }
 
+void OverviewSession::OnWindowAdded(aura::Window* new_window) {
+  if (!auto_add_windows_enabled_)
+    return;
+
+  // We track if we are in the process of adding an item to avoid recursively
+  // adding items.
+  if (is_adding_new_item_)
+    return;
+  base::AutoReset<bool> adding_new_item_resetter(&is_adding_new_item_, true);
+
+  // Avoid adding overview items for certain windows.
+  if (!WindowState::Get(new_window) ||
+      window_util::ShouldExcludeForOverview(new_window)) {
+    return;
+  }
+
+  AppendItem(new_window, /*reposition=*/true, /*animate*/ true);
+}
+
 void OverviewSession::OnKeyEvent(ui::KeyEvent* event) {
   // If app list is open when overview is active (it can happen in clamshell
   // mode, when we snap an overview window to one side of the screen and then
diff --git a/ash/wm/overview/overview_session.h b/ash/wm/overview/overview_session.h
index 9f3e7fe7..30512b7c 100644
--- a/ash/wm/overview/overview_session.h
+++ b/ash/wm/overview/overview_session.h
@@ -15,6 +15,7 @@
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/tablet_mode_observer.h"
 #include "ash/shell_observer.h"
+#include "ash/wm/desks/desks_controller.h"
 #include "ash/wm/overview/overview_types.h"
 #include "ash/wm/overview/scoped_overview_hide_windows.h"
 #include "ash/wm/splitview/split_view_controller.h"
@@ -57,7 +58,8 @@
                                    public ui::EventHandler,
                                    public ShellObserver,
                                    public SplitViewObserver,
-                                   public TabletModeObserver {
+                                   public TabletModeObserver,
+                                   public DesksController::Observer {
  public:
   using WindowList = std::vector<aura::Window*>;
 
@@ -279,6 +281,18 @@
   // Shows the desks templates grids on all displays. If `was_zero_state` is
   // true then we will expand the desks bars.
   void ShowDesksTemplatesGrids(bool was_zero_state);
+  void HideDesksTemplatesGrids();
+
+  // DesksController::Observer:
+  void OnDeskAdded(const Desk* desk) override;
+  void OnDeskRemoved(const Desk* desk) override;
+  void OnDeskReordered(int old_index, int new_index) override;
+  void OnDeskActivationChanged(const Desk* activated,
+                               const Desk* deactivated) override;
+  void OnDeskSwitchAnimationLaunching() override;
+  void OnDeskSwitchAnimationFinished() override;
+  void OnDeskNameChanged(const Desk* desk,
+                         const std::u16string& new_name) override;
 
   // display::DisplayObserver:
   void OnDisplayAdded(const display::Display& display) override;
@@ -287,6 +301,7 @@
 
   // aura::WindowObserver:
   void OnWindowDestroying(aura::Window* window) override;
+  void OnWindowAdded(aura::Window* new_window) override;
 
   // ui::EventHandler:
   void OnKeyEvent(ui::KeyEvent* event) override;
@@ -338,6 +353,10 @@
     return desks_templates_presenter_.get();
   }
 
+  void set_auto_add_windows_enabled(bool enabled) {
+    auto_add_windows_enabled_ = enabled;
+  }
+
  private:
   friend class DesksAcceleratorsTest;
   friend class OverviewTestBase;
@@ -448,8 +467,22 @@
   // Boolean to indicate whether chromeVox is enabled or not.
   bool chromevox_enabled_;
 
+  // When non-null, windows changes on this desk are observed.
+  const Desk* observing_desk_ = nullptr;
+
+  // This is true *while* an overview item is being dynamically added. It is
+  // used to avoid recursively adding overview items.
+  bool is_adding_new_item_ = false;
+
+  // When true, windows added to the observed desk are automatically added to
+  // the overview session.
+  bool auto_add_windows_enabled_ = true;
+
   base::ScopedObservation<TabletModeController, TabletModeObserver>
       tablet_mode_observation_{this};
+
+  base::ScopedObservation<DesksController, DesksController::Observer>
+      desks_controller_observation_{this};
 };
 
 }  // namespace ash
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index 622c2da..ad65fbb 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -438,7 +438,6 @@
   WMEvent minimize_event(WM_EVENT_MINIMIZE);
   window_state->OnWMEvent(&minimize_event);
   EXPECT_FALSE(window->IsVisible());
-  EXPECT_EQ(0.f, window->layer()->GetTargetOpacity());
   EXPECT_EQ(WindowStateType::kMinimized,
             WindowState::Get(window.get())->GetStateType());
   ToggleOverview();
diff --git a/ash/wm/overview/overview_types.h b/ash/wm/overview/overview_types.h
index 41b6f88..90b7e700 100644
--- a/ash/wm/overview/overview_types.h
+++ b/ash/wm/overview/overview_types.h
@@ -44,6 +44,8 @@
   OVERVIEW_ANIMATION_NO_RECENTS_FADE,
   // Used to fade in all windows when window drag starts or during window drag.
   OVERVIEW_ANIMATION_OPACITY_ON_WINDOW_DRAG,
+  // Used to fade out the desks templates grid when exiting overview mode.
+  OVERVIEW_ANIMATION_EXIT_OVERVIEW_MODE_DESKS_TEMPLATES_GRID_FADE_OUT,
 };
 
 enum class OverviewTransition {
diff --git a/ash/wm/overview/overview_window_drag_controller.cc b/ash/wm/overview/overview_window_drag_controller.cc
index 92397209..bd5b892 100644
--- a/ash/wm/overview/overview_window_drag_controller.cc
+++ b/ash/wm/overview/overview_window_drag_controller.cc
@@ -16,6 +16,7 @@
 #include "ash/wm/desks/desk_preview_view.h"
 #include "ash/wm/desks/desks_bar_view.h"
 #include "ash/wm/desks/desks_util.h"
+#include "ash/wm/haptics_util.h"
 #include "ash/wm/overview/overview_constants.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_grid.h"
@@ -37,6 +38,7 @@
 #include "ui/aura/window_observer.h"
 #include "ui/compositor/layer.h"
 #include "ui/display/display.h"
+#include "ui/events/devices/haptic_touchpad_effects.h"
 #include "ui/wm/core/coordinate_conversion.h"
 
 namespace ash {
@@ -552,11 +554,27 @@
       // Update the mini views borders by checking if |location_in_screen|
       // intersects. Only update the borders if the dragged item is not visible
       // on all desks.
-      overview_grid->IntersectsWithDesksBar(
+      bool intersects_with_desks_bar = overview_grid->IntersectsWithDesksBar(
           gfx::ToRoundedPoint(location_in_screen),
           /*update_desks_bar_drag_details=*/
           !DraggedItemIsVisibleOnAllDesks(item_), /*for_drop=*/false);
 
+      // Fire a haptic event if necessary.
+      if (intersects_with_desks_bar && !is_touch_dragging_) {
+        const int desk_index = overview_grid->GetDeskIndexFromScreenLocation(
+            gfx::ToRoundedPoint(location_in_screen));
+        if (last_desk_index_ != desk_index) {
+          last_desk_index_ = desk_index;
+          if (desk_index != -1) {
+            haptics_util::PlayHapticTouchpadEffect(
+                ui::HapticTouchpadEffect::kTick,
+                ui::HapticTouchpadEffectStrength::kMedium);
+          }
+        }
+      } else {
+        last_desk_index_ = -1;
+      }
+
       float value = 0.f;
       if (centerpoint.y() < desks_bar_data.desks_bar_bounds.y() ||
           centerpoint.y() > desks_bar_data.desks_bar_bounds.bottom()) {
diff --git a/ash/wm/overview/overview_window_drag_controller.h b/ash/wm/overview/overview_window_drag_controller.h
index 6f72f3a..ecd2bfc 100644
--- a/ash/wm/overview/overview_window_drag_controller.h
+++ b/ash/wm/overview/overview_window_drag_controller.h
@@ -232,6 +232,12 @@
   // Set to true once the bounds of |item_| change.
   bool did_move_ = false;
 
+  // The last desk index that the screen location points to during drag. Please
+  // note that this would be set to `-1` when it does not intersect with desks
+  // bar. It is used to keep track of haptic feedback since we do not want
+  // duplicate event for the same desk during drag.
+  int last_desk_index_ = -1;
+
   // Records the presentation time of window drag operation in overview mode.
   std::unique_ptr<PresentationTimeRecorder> presentation_time_recorder_;
 
diff --git a/ash/wm/overview/scoped_overview_animation_settings.cc b/ash/wm/overview/scoped_overview_animation_settings.cc
index 8def01b0..4e3ea0d0 100644
--- a/ash/wm/overview/scoped_overview_animation_settings.cc
+++ b/ash/wm/overview/scoped_overview_animation_settings.cc
@@ -71,6 +71,8 @@
       return kTransition;
     case OVERVIEW_ANIMATION_OPACITY_ON_WINDOW_DRAG:
       return kFadeInOnWindowDrag;
+    case OVERVIEW_ANIMATION_EXIT_OVERVIEW_MODE_DESKS_TEMPLATES_GRID_FADE_OUT:
+      return kFadeOut;
   }
   NOTREACHED();
   return base::TimeDelta();
@@ -161,6 +163,11 @@
       animation_settings_->SetPreemptionStrategy(
           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
       break;
+    case OVERVIEW_ANIMATION_EXIT_OVERVIEW_MODE_DESKS_TEMPLATES_GRID_FADE_OUT:
+      animation_settings_->SetTweenType(gfx::Tween::LINEAR);
+      animation_settings_->SetPreemptionStrategy(
+          ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+      break;
   }
   animation_settings_->SetTransitionDuration(
       GetAnimationDuration(animation_type));
diff --git a/ash/wm/overview/scoped_overview_transform_window.cc b/ash/wm/overview/scoped_overview_transform_window.cc
index 43cbaa9..50c2707 100644
--- a/ash/wm/overview/scoped_overview_transform_window.cc
+++ b/ash/wm/overview/scoped_overview_transform_window.cc
@@ -257,12 +257,13 @@
   return OverviewGridWindowFillMode::kNormal;
 }
 
-void ScopedOverviewTransformWindow::RestoreWindow(bool reset_transform) {
+void ScopedOverviewTransformWindow::RestoreWindow(bool reset_transform,
+                                                  bool animate_back) {
   // Shadow controller may be null on shutdown.
   if (Shell::Get()->shadow_controller())
     Shell::Get()->shadow_controller()->UpdateShadowForWindow(window_);
 
-  if (IsMinimized()) {
+  if (IsMinimized() || !animate_back) {
     // Minimized windows may have had their transforms altered by swiping up
     // from the shelf.
     SetTransform(window_, gfx::Transform());
diff --git a/ash/wm/overview/scoped_overview_transform_window.h b/ash/wm/overview/scoped_overview_transform_window.h
index 9626b46..0907741 100644
--- a/ash/wm/overview/scoped_overview_transform_window.h
+++ b/ash/wm/overview/scoped_overview_transform_window.h
@@ -94,11 +94,12 @@
   // ancestors, in which case returns 0.
   int GetTopInset() const;
 
-  // Restores and animates the managed window to its non overview mode state.
+  // Restores and animates the managed window to its non overview mode state. If
+  // `animate_back` is false, the window will just be restored and not animated.
   // If |reset_transform| equals false, the window's transform will not be reset
   // to identity transform when exiting the overview mode. See
   // OverviewItem::RestoreWindow() for details why we need this.
-  void RestoreWindow(bool reset_transform);
+  void RestoreWindow(bool reset_transform, bool animate_back = true);
 
   // Prepares for overview mode by doing any necessary actions before entering.
   void PrepareForOverview();
diff --git a/base/android/build_info.cc b/base/android/build_info.cc
index be91430..6bc3695 100644
--- a/base/android/build_info.cc
+++ b/base/android/build_info.cc
@@ -79,7 +79,8 @@
       target_sdk_version_(GetIntParam(params, 21)),
       is_debug_android_(GetIntParam(params, 22)),
       is_tv_(GetIntParam(params, 23)),
-      version_incremental_(StrDupParam(params, 24)) {}
+      version_incremental_(StrDupParam(params, 24)),
+      hardware_(StrDupParam(params, 25)) {}
 
 // static
 BuildInfo* BuildInfo::GetInstance() {
diff --git a/base/android/build_info.h b/base/android/build_info.h
index f8e2c73..6717e80 100644
--- a/base/android/build_info.h
+++ b/base/android/build_info.h
@@ -138,6 +138,8 @@
 
   const char* version_incremental() const { return version_incremental_; }
 
+  const char* hardware() const { return hardware_; }
+
  private:
   friend struct BuildInfoSingletonTraits;
 
@@ -173,6 +175,7 @@
   const bool is_debug_android_;
   const bool is_tv_;
   const char* const version_incremental_;
+  const char* const hardware_;
 };
 
 }  // namespace android
diff --git a/base/android/java/src/org/chromium/base/BuildInfo.java b/base/android/java/src/org/chromium/base/BuildInfo.java
index 198c970f..4d286ce 100644
--- a/base/android/java/src/org/chromium/base/BuildInfo.java
+++ b/base/android/java/src/org/chromium/base/BuildInfo.java
@@ -91,6 +91,7 @@
                 isDebugAndroid() ? "1" : "0",
                 buildInfo.isTV ? "1" : "0",
                 Build.VERSION.INCREMENTAL,
+                Build.HARDWARE,
         };
     }
 
diff --git a/base/android/java/src/org/chromium/base/library_loader/Linker.java b/base/android/java/src/org/chromium/base/library_loader/Linker.java
index ca5e337..95ddded 100644
--- a/base/android/java/src/org/chromium/base/library_loader/Linker.java
+++ b/base/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -399,6 +399,33 @@
     void setApkFilePath(String path) {}
 
     /**
+     * Tells whether atomic replacement of RELRO after library load should be performed. It is only
+     * supported by the ModernLinker. The latter should give up with RELRO on the retry that uses
+     * the RelroSharingMode.NO_SHARING.  This method should be called after loading the library.
+     */
+    @GuardedBy("mLock")
+    private boolean shouldAtomicallyReplaceRelroAfterLoad() {
+        // This is a demonstration of the unfortunate tight coupling between the Linker, and the
+        // implementation details in loadLibraryImplLocked() for each of the two subclasses.
+        // Decoupling it would be nontrivial given the reuse of |mLock| in the subclasses.
+        // Improvements of this kind will soon become unnecessary because the LegacyLinker will go
+        // away with the deprecation of the LegacyLinker in Android M.
+        if (mLinkerWasWaitingSynchronously) {
+            // The LegacyLinker was blocked waiting for |mRemoteLibInfo| to arrive, used it and
+            // nullified immediately.
+            return false;
+        }
+        if (mRemoteLibInfo != null && mState == State.DONE) {
+            // Only the ModernLinker can end up in the State.DONE while mRemoteLibInfo is not
+            // nullified yet. With an invalid load address it is impossible to locate the RELRO
+            // region in the current process. This could happen when the library loaded successfully
+            // only after the fallback to no sharing.
+            return mRemoteLibInfo.mLoadAddress != 0;
+        }
+        return false;
+    }
+
+    /**
      * Loads the native library using a given mode.
      *
      * @param library The library name to load.
@@ -414,7 +441,7 @@
                 Log.i(TAG, "Attempt to replace RELRO: waswaiting=%b, remotenonnull=%b, state=%d",
                         mLinkerWasWaitingSynchronously, mRemoteLibInfo != null, mState);
             }
-            if (!mLinkerWasWaitingSynchronously && mRemoteLibInfo != null && mState == State.DONE) {
+            if (shouldAtomicallyReplaceRelroAfterLoad()) {
                 atomicReplaceRelroLocked(/* relroAvailableImmediately= */ true);
             }
         } finally {
diff --git a/base/android/linker/modern_linker_jni.cc b/base/android/linker/modern_linker_jni.cc
index 97300cb..03925c59 100644
--- a/base/android/linker/modern_linker_jni.cc
+++ b/base/android/linker/modern_linker_jni.cc
@@ -532,6 +532,12 @@
     return false;
   }
 
+  if (other_lib_info.load_address_ == 0) {
+    LOG_ERROR("Load address reset. Second attempt to load the library?");
+    s_relro_sharing_status = RelroSharingStatus::EXTERNAL_LOAD_ADDRESS_RESET;
+    return false;
+  }
+
   if (!FindRelroAndLibraryRangesInElf()) {
     LOG_ERROR("Could not find RELRO from externally provided address: 0x%p",
               reinterpret_cast<void*>(other_lib_info.load_address_));
diff --git a/base/android/linker/modern_linker_jni.h b/base/android/linker/modern_linker_jni.h
index e39217fd..42299e0 100644
--- a/base/android/linker/modern_linker_jni.h
+++ b/base/android/linker/modern_linker_jni.h
@@ -28,7 +28,8 @@
   NO_SHMEM_FUNCTIONS = 5,
   REMAP_FAILED = 6,
   CORRUPTED_IN_JAVA = 7,
-  COUNT = 8,
+  EXTERNAL_LOAD_ADDRESS_RESET = 8,
+  COUNT = 9,
 };
 
 struct SharedMemoryFunctions;
diff --git a/build/android/PRESUBMIT.py b/build/android/PRESUBMIT.py
index b4f9359..422f360 100644
--- a/build/android/PRESUBMIT.py
+++ b/build/android/PRESUBMIT.py
@@ -28,12 +28,6 @@
           input_api,
           output_api,
           pylintrc='pylintrc',
-          # Temporarily disabled until pylint-2.6: crbug.com/1100664
-          disabled_warnings=[
-              'no-member',
-              'superfluous-parens',
-              'no-name-in-module',
-              'import-error'],
           files_to_skip=[
               r'.*_pb2\.py',
               # The following are all temporary due to: crbug.com/1100664
@@ -56,7 +50,7 @@
               J('..', '..', 'third_party', 'colorama', 'src'),
               J('..', '..', 'build'),
           ],
-          version='2.6'))
+          version='2.7'))
   tests.extend(
       input_api.canned_checks.GetPylint(
           input_api,
@@ -69,7 +63,7 @@
               r'.*create_unwind_table_tests\.py',
           ],
           extra_paths_list=[J('gyp'), J('gn')],
-          version='2.6'))
+          version='2.7'))
 
   tests.extend(
       input_api.canned_checks.GetPylint(
@@ -80,7 +74,7 @@
               r'.*create_unwind_table_tests\.py',
           ],
           extra_paths_list=[J('gyp'), J('gn')],
-          version='2.6'))
+          version='2.7'))
   # yapf: enable
 
   # Disabled due to http://crbug.com/410936
diff --git a/build/android/convert_dex_profile.py b/build/android/convert_dex_profile.py
index 006cf7a..85b0aa5 100755
--- a/build/android/convert_dex_profile.py
+++ b/build/android/convert_dex_profile.py
@@ -225,6 +225,7 @@
 class MalformedLineException(Exception):
   def __init__(self, message, line_number):
     super().__init__(message)
+    self.message = message
     self.line_number = line_number
 
   def __str__(self):
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index 4e028033..6f22b5f7 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -1374,10 +1374,8 @@
   """Determines whether a test or a list of tests is a WPR RecordReplay Test."""
   if not isinstance(test, list):
     test = [test]
-  return any([
-      WPR_RECORD_REPLAY_TEST_FEATURE_ANNOTATION in t['annotations'].get(
-          FEATURE_ANNOTATION, {}).get('value', ()) for t in test
-  ])
+  return any(WPR_RECORD_REPLAY_TEST_FEATURE_ANNOTATION in t['annotations'].get(
+      FEATURE_ANNOTATION, {}).get('value', ()) for t in test)
 
 
 def _GetWPRArchivePath(test):
@@ -1408,8 +1406,8 @@
   """Determines if a test or list of tests has a RenderTest amongst them."""
   if not isinstance(test, list):
     test = [test]
-  return any([RENDER_TEST_FEATURE_ANNOTATION in t['annotations'].get(
-              FEATURE_ANNOTATION, {}).get('value', ()) for t in test])
+  return any(RENDER_TEST_FEATURE_ANNOTATION in t['annotations'].get(
+      FEATURE_ANNOTATION, {}).get('value', ()) for t in test)
 
 
 def _GenerateRenderTestHtml(image_name, failure_link, golden_link, diff_link):
@@ -1521,7 +1519,7 @@
     True if one of the results in |results| has the same name as
     |full_test_name|, otherwise False.
   """
-  return any([r for r in results if r.GetName() == full_test_name])
+  return any(r for r in results if r.GetName() == full_test_name)
 
 
 def _ShouldReportNoMatchingResult(full_test_name):
diff --git a/build/android/pylib/utils/instrumentation_tracing.py b/build/android/pylib/utils/instrumentation_tracing.py
index edab29d..32e1ae1 100644
--- a/build/android/pylib/utils/instrumentation_tracing.py
+++ b/build/android/pylib/utils/instrumentation_tracing.py
@@ -76,7 +76,7 @@
   if module_name in included:
     includes = True
   elif to_include:
-    includes = any([pattern.match(module_name) for pattern in to_include])
+    includes = any(pattern.match(module_name) for pattern in to_include)
   else:
     includes = True
 
diff --git a/build/android/pylib/utils/maven_downloader.py b/build/android/pylib/utils/maven_downloader.py
index 109e52b..b80b1a15 100755
--- a/build/android/pylib/utils/maven_downloader.py
+++ b/build/android/pylib/utils/maven_downloader.py
@@ -121,7 +121,7 @@
       if ret_code != 0:
         raise Exception('Command "{}" failed'.format(' '.join(cmd)))
     except OSError as e:
-      if e.errno == os.errno.ENOENT:
+      if e.errno == errno.ENOENT:
         raise Exception('mvn command not found. Please install Maven.') from e
       raise
 
diff --git a/build/android/resource_sizes.py b/build/android/resource_sizes.py
index 60704ca3d..2171edf 100755
--- a/build/android/resource_sizes.py
+++ b/build/android/resource_sizes.py
@@ -35,7 +35,7 @@
 _AAPT_PATH = lazy.WeakConstant(lambda: build_tools.GetPath('aapt'))
 _ANDROID_UTILS_PATH = os.path.join(host_paths.DIR_SOURCE_ROOT, 'build',
                                    'android', 'gyp')
-_BUILD_UTILS_PATH = os.path.join(host_paths.BUILD_PATH, 'util')
+_BUILD_UTILS_PATH = os.path.join(host_paths.DIR_SOURCE_ROOT, 'build', 'util')
 _READOBJ_PATH = os.path.join(constants.ANDROID_NDK_ROOT, 'toolchains', 'llvm',
                              'prebuilt', 'linux-x86_64', 'bin', 'llvm-readobj')
 
@@ -50,8 +50,8 @@
   from util import zipalign  # pylint: disable=import-error
 
 with host_paths.SysPath(_BUILD_UTILS_PATH, 0):
-  from lib.results import result_sink
-  from lib.results import result_types
+  from lib.results import result_sink  # pylint: disable=import-error
+  from lib.results import result_types  # pylint: disable=import-error
 
 zipalign.ApplyZipFileZipAlignFix()
 
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index e45fa2c8..7a1825e 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -129,8 +129,8 @@
   # TODO(gbiv): We disable optimizations by default on most platforms because
   # the space overhead is too great. We should use some mixture of profiles and
   # optimization settings to better tune the size increase.
-  thin_lto_enable_optimizations = (is_chromeos_ash || is_android || is_win ||
-                                   is_linux || is_mac) && is_official_build
+  thin_lto_enable_optimizations =
+      (is_chromeos_ash || is_android || is_win || is_linux) && is_official_build
 
   # Initialize all local variables with a pattern. This flag will fill
   # uninitialized floating-point types (and 32-bit pointers) with 0xFF and the
@@ -654,11 +654,10 @@
   if (!is_debug && use_thin_lto && is_a_target_toolchain) {
     assert(use_lld, "LTO is only supported with lld")
 
-    cflags += [ "-flto=thin" ]
-    if (!is_mac) {
-      # TODO(lgrey): Enable unit splitting for Mac when supported.
-      cflags += [ "-fsplit-lto-unit" ]
-    }
+    cflags += [
+      "-flto=thin",
+      "-fsplit-lto-unit",
+    ]
 
     # Limit the size of the ThinLTO cache to the lesser of 10% of
     # available disk space, 40GB and 100000 files.
diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni
index 3520e428..a41df19 100644
--- a/build/config/compiler/compiler.gni
+++ b/build/config/compiler/compiler.gni
@@ -74,8 +74,7 @@
   use_thin_lto =
       is_cfi ||
       (is_clang && is_official_build && chrome_pgo_phase != 1 &&
-       (is_linux || is_win || is_mac ||
-        (is_android && target_os != "chromeos") ||
+       (is_linux || is_win || (is_android && target_os != "chromeos") ||
         ((is_chromeos_ash || is_chromeos_lacros) && is_chromeos_device)))
 
   # If true, use Goma for ThinLTO code generation where applicable.
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 04f7e11..f58729d 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-7.20211214.1.1
+7.20211214.2.1
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 04f7e11..f58729d 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-7.20211214.1.1
+7.20211214.2.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 04f7e11..f58729d 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-7.20211214.1.1
+7.20211214.2.1
diff --git a/cc/test/test_options_provider.cc b/cc/test/test_options_provider.cc
index d9f07cd..82d5a8c2 100644
--- a/cc/test/test_options_provider.cc
+++ b/cc/test/test_options_provider.cc
@@ -22,12 +22,19 @@
     return true;
   }
 
+  // SkStrikeServer::DiscardableHandleManager::isHandleDeleted implementation.
+  bool isHandleDeleted(SkDiscardableHandleId) override { return false; }
+
   // SkStrikeClient::DiscardableHandleManager implementation.
   bool deleteHandle(SkDiscardableHandleId handle_id) override {
     CHECK_LT(handle_id, next_handle_id_);
     return false;
   }
 
+  // SkStrikeClient::DiscardableHandleManager implementation.
+  void notifyCacheMiss(SkStrikeClient::CacheMissType type,
+                       int fontSize) override {}
+
  private:
   SkDiscardableHandleId next_handle_id_ = 1u;
 };
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 3a50c88..4b2632d 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -93,8 +93,6 @@
 # on Chrome and the main DLLs it uses, it will transitively assert that those
 # targets also have no deps on disallowed things.
 group("assert_no_deps") {
-  # TODO(issuetracker.google.com/199337060): remove testonly
-  testonly = enable_weston_test
   deps = []
 
   if (is_android) {
@@ -114,8 +112,6 @@
 }
 if (!is_android && !is_mac) {
   group("chrome") {
-    # TODO(issuetracker.google.com/199337060): remove testonly
-    testonly = enable_weston_test
     public_deps = [ ":chrome_initial" ]
     data_deps = [ ":chrome_initial" ]
 
@@ -156,9 +152,8 @@
     data_deps = []
 
     if (is_chromeos_ash) {
-      # TODO(issuetracker.google.com/199337060): remove testonly
-      testonly = enable_weston_test
       data_deps += [ "//sandbox/linux:chrome_sandbox" ]
+      deps += [ "//components/exo/wayland:weston_test_stub" ]
     }
 
     if (also_build_lacros_chrome_for_architecture != "") {
@@ -1383,7 +1378,6 @@
   }
 
   if (is_chromeos_ash) {
-    testonly = enable_weston_test
     public_deps += [
       "//ash/constants",
       "//chrome/browser/chromeos",
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceCoordinator.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceCoordinator.java
index ff79bb2..a33674d 100644
--- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceCoordinator.java
+++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceCoordinator.java
@@ -280,7 +280,7 @@
                 new StartSurfaceMediator(controller, mTabModelSelector, mPropertyModel,
                         mIsStartSurfaceEnabled ? this::initializeSecondaryTasksSurface : null,
                         mIsStartSurfaceEnabled, mActivity, mBrowserControlsManager,
-                        this::isActivityFinishingOrDestroyed, excludeMVTiles,
+                        this::isActivityFinishingOrDestroyed, excludeMVTiles, excludeQueryTiles,
                         startSurfaceOneshotSupplier, hadWarmStart, jankTracker);
 
         // Show feed loading image.
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
index df97897535a..c5a97e2 100644
--- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
+++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
@@ -15,6 +15,7 @@
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.MORE_TABS_CLICK_LISTENER;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.MV_TILES_CONTAINER_TOP_MARGIN;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.MV_TILES_VISIBLE;
+import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.QUERY_TILES_VISIBLE;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.RESET_TASK_SURFACE_HEADER_SCROLL_POSITION;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.TAB_SWITCHER_TITLE_TOP_MARGIN;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.TASKS_SURFACE_BODY_TOP_MARGIN;
@@ -98,6 +99,7 @@
     private final boolean mIsStartSurfaceEnabled;
     private final ObserverList<StartSurface.StateObserver> mStateObservers = new ObserverList<>();
     private final boolean mHadWarmStart;
+    private final boolean mExcludeQueryTiles;
 
     // Boolean histogram used to record whether cached
     // ChromePreferenceKeys.FEED_ARTICLES_LIST_VISIBLE is consistent with
@@ -164,8 +166,8 @@
             boolean isStartSurfaceEnabled, Context context,
             BrowserControlsStateProvider browserControlsStateProvider,
             ActivityStateChecker activityStateChecker, boolean excludeMVTiles,
-            OneshotSupplier<StartSurface> startSurfaceSupplier, boolean hadWarmStart,
-            JankTracker jankTracker) {
+            boolean excludeQueryTiles, OneshotSupplier<StartSurface> startSurfaceSupplier,
+            boolean hadWarmStart, JankTracker jankTracker) {
         mController = controller;
         mTabModelSelector = tabModelSelector;
         mPropertyModel = propertyModel;
@@ -175,6 +177,7 @@
         mBrowserControlsStateProvider = browserControlsStateProvider;
         mActivityStateChecker = activityStateChecker;
         mExcludeMVTiles = excludeMVTiles;
+        mExcludeQueryTiles = excludeQueryTiles;
         mStartSurfaceSupplier = startSurfaceSupplier;
         mHadWarmStart = hadWarmStart;
         mJankTracker = jankTracker;
@@ -346,6 +349,7 @@
         // Secondary tasks surface is used for more Tabs or incognito mode single pane, where MV
         // tiles and voice recognition button should be invisible.
         mSecondaryTasksSurfacePropertyModel.set(MV_TILES_VISIBLE, false);
+        mSecondaryTasksSurfacePropertyModel.set(QUERY_TILES_VISIBLE, false);
         mSecondaryTasksSurfacePropertyModel.set(IS_VOICE_RECOGNITION_BUTTON_VISIBLE, false);
         mSecondaryTasksSurfacePropertyModel.set(IS_LENS_BUTTON_VISIBLE, false);
     }
@@ -491,6 +495,8 @@
             setTabCarouselVisibility(
                     hasNormalTab && !mIsIncognito && !mHideTabCarouselForNewSurface);
             setMVTilesVisibility(!mIsIncognito && !mHideMVForNewSurface);
+            // TODO(qinmin): show query tiles when flag is enabled.
+            setQueryTilesVisibility(false);
             setFakeBoxVisibility(!mIsIncognito);
             setSecondaryTasksSurfaceVisibility(mIsIncognito, /* skipUpdateController = */ false);
 
@@ -506,6 +512,7 @@
         } else if (mStartSurfaceState == StartSurfaceState.SHOWN_TABSWITCHER) {
             setTabCarouselVisibility(false);
             setMVTilesVisibility(false);
+            setQueryTilesVisibility(false);
             setFakeBoxVisibility(false);
             setSecondaryTasksSurfaceVisibility(
                     /* isVisible= */ true, /* skipUpdateController = */ false);
@@ -875,6 +882,11 @@
         mPropertyModel.set(MV_TILES_VISIBLE, isVisible);
     }
 
+    private void setQueryTilesVisibility(boolean isVisible) {
+        if (mExcludeQueryTiles || isVisible == mPropertyModel.get(QUERY_TILES_VISIBLE)) return;
+        mPropertyModel.set(QUERY_TILES_VISIBLE, isVisible);
+    }
+
     private void setFakeBoxVisibility(boolean isVisible) {
         if (mPropertyModel == null) return;
         mPropertyModel.set(IS_FAKE_SEARCH_BOX_VISIBLE, isVisible);
diff --git a/chrome/android/features/start_surface/internal/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceMediatorUnitTest.java b/chrome/android/features/start_surface/internal/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceMediatorUnitTest.java
index daf212db..f4f3730 100644
--- a/chrome/android/features/start_surface/internal/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceMediatorUnitTest.java
+++ b/chrome/android/features/start_surface/internal/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceMediatorUnitTest.java
@@ -1200,7 +1200,8 @@
                 isStartSurfaceEnabled ? mSecondaryTasksSurfaceInitializer : null,
                 isStartSurfaceEnabled, ContextUtils.getApplicationContext(),
                 mBrowserControlsStateProvider, mActivityStateChecker, excludeMVTiles,
-                mStartSurfaceSupplier, hadWarmStart, new DummyJankTracker());
+                true /* excludeQueryTiles */, mStartSurfaceSupplier, hadWarmStart,
+                new DummyJankTracker());
         return mediator;
     }
 }
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index 314a3fe..2c70d09 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -68,6 +68,7 @@
     "java/res/layout/new_tab_tile_card_item.xml",
     "java/res/layout/price_card.xml",
     "java/res/layout/price_tracking_dialog_layout.xml",
+    "java/res/layout/query_tiles_layout.xml",
     "java/res/layout/selectable_tab_grid_card_item.xml",
     "java/res/layout/selectable_tab_list_card_item.xml",
     "java/res/layout/single_tab_view_layout.xml",
diff --git a/chrome/android/features/tab_ui/java/res/layout/query_tiles_layout.xml b/chrome/android/features/tab_ui/java/res/layout/query_tiles_layout.xml
new file mode 100644
index 0000000..f07d489
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/res/layout/query_tiles_layout.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<!-- Query suggestion tiles. -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/query_tiles_layout"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:paddingBottom="@dimen/tasks_view_items_vertical_spacing">
+</FrameLayout>
diff --git a/chrome/android/features/tab_ui/java/res/layout/tasks_view_layout.xml b/chrome/android/features/tab_ui/java/res/layout/tasks_view_layout.xml
index fa62d807..3a40bb6 100644
--- a/chrome/android/features/tab_ui/java/res/layout/tasks_view_layout.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/tasks_view_layout.xml
@@ -33,6 +33,16 @@
             app:layout_scrollFlags="scroll">
             <include layout="@layout/fake_search_box_layout"/>
         </LinearLayout>
+        <FrameLayout android:id="@+id/query_tiles_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="6dp"
+            android:paddingStart="6dp"
+            android:paddingEnd="6dp"
+            android:visibility="gone"
+            app:layout_scrollFlags="scroll">
+            <include layout="@layout/query_tiles_layout" />
+        </FrameLayout>
         <HorizontalScrollView android:id="@+id/mv_tiles_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceCoordinator.java
index 2fcf955..158393f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceCoordinator.java
@@ -26,6 +26,8 @@
 import org.chromium.chrome.browser.ntp.IncognitoCookieControlsManager;
 import org.chromium.chrome.browser.omnibox.OmniboxStub;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.query_tiles.QueryTileSection;
+import org.chromium.chrome.browser.query_tiles.QueryTileSection.QueryInfo;
 import org.chromium.chrome.browser.share.ShareDelegate;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
@@ -53,6 +55,7 @@
     private final PropertyModelChangeProcessor mPropertyModelChangeProcessor;
     private final TasksSurfaceMediator mMediator;
     private MostVisitedListCoordinator mMostVisitedList;
+    private QueryTileSection mQueryTileSection;
     private final PropertyModel mPropertyModel;
     private final @TabSwitcherType int mTabSwitcherType;
     private final SnackbarManager mSnackbarManager;
@@ -132,10 +135,16 @@
             mMostVisitedList.initialize();
         }
         if (hasQueryTiles) {
-            // TODO(qinmin): show QueryTiles.
+            QueryTileSection queryTileSection =
+                    new QueryTileSection(mView.findViewById(R.id.query_tiles_layout),
+                            Profile.getLastUsedRegularProfile(), this::performSearchQuery);
         }
     }
 
+    private void performSearchQuery(QueryInfo queryInfo) {
+        // TODO(qinmin): load the search result page.
+    }
+
     /**
      * TasksSurface implementation.
      */
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceProperties.java
index 3c6a3714..2b4a4860 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceProperties.java
@@ -66,6 +66,8 @@
             .WritableObjectPropertyKey<View.OnClickListener> MORE_TABS_CLICK_LISTENER =
             new PropertyModel.WritableObjectPropertyKey<>();
     public static final PropertyModel.WritableBooleanPropertyKey MV_TILES_VISIBLE = IS_VISIBLE;
+    public static final PropertyModel.WritableBooleanPropertyKey QUERY_TILES_VISIBLE =
+            new PropertyModel.WritableBooleanPropertyKey();
     public static final PropertyModel
             .WritableObjectPropertyKey<View.OnClickListener> VOICE_SEARCH_BUTTON_CLICK_LISTENER =
             new PropertyModel.WritableObjectPropertyKey<>();
@@ -87,7 +89,7 @@
             INCOGNITO_COOKIE_CONTROLS_TOGGLE_ENFORCEMENT, INCOGNITO_COOKIE_CONTROLS_MANAGER,
             INCOGNITO_LEARN_MORE_CLICK_LISTENER, FAKE_SEARCH_BOX_CLICK_LISTENER,
             FAKE_SEARCH_BOX_TEXT_WATCHER, LENS_BUTTON_CLICK_LISTENER, MORE_TABS_CLICK_LISTENER,
-            MV_TILES_VISIBLE, VOICE_SEARCH_BUTTON_CLICK_LISTENER, TASKS_SURFACE_BODY_TOP_MARGIN,
-            MV_TILES_CONTAINER_TOP_MARGIN, TAB_SWITCHER_TITLE_TOP_MARGIN,
-            RESET_TASK_SURFACE_HEADER_SCROLL_POSITION};
+            MV_TILES_VISIBLE, QUERY_TILES_VISIBLE, VOICE_SEARCH_BUTTON_CLICK_LISTENER,
+            TASKS_SURFACE_BODY_TOP_MARGIN, MV_TILES_CONTAINER_TOP_MARGIN,
+            TAB_SWITCHER_TITLE_TOP_MARGIN, RESET_TASK_SURFACE_HEADER_SCROLL_POSITION};
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksView.java
index 7587403..3b17beb 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksView.java
@@ -165,6 +165,13 @@
     }
 
     /**
+     * Set the visibility of the Most Visited Tiles.
+     */
+    void setQueryTilesVisibility(int visibility) {
+        findViewById(R.id.query_tiles_container).setVisibility(visibility);
+    }
+
+    /**
      * Set the {@link android.view.View.OnClickListener} for More Tabs.
      */
     void setMoreTabsOnClickListener(@Nullable View.OnClickListener listener) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksViewBinder.java
index 857d0a4..0f75b7a 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksViewBinder.java
@@ -24,6 +24,7 @@
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.MORE_TABS_CLICK_LISTENER;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.MV_TILES_CONTAINER_TOP_MARGIN;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.MV_TILES_VISIBLE;
+import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.QUERY_TILES_VISIBLE;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.RESET_TASK_SURFACE_HEADER_SCROLL_POSITION;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.TAB_SWITCHER_TITLE_TOP_MARGIN;
 import static org.chromium.chrome.browser.tasks.TasksSurfaceProperties.TASKS_SURFACE_BODY_TOP_MARGIN;
@@ -91,6 +92,8 @@
             view.setMoreTabsOnClickListener(model.get(MORE_TABS_CLICK_LISTENER));
         } else if (propertyKey == MV_TILES_VISIBLE) {
             view.setMostVisitedVisibility(model.get(MV_TILES_VISIBLE) ? View.VISIBLE : View.GONE);
+        } else if (propertyKey == QUERY_TILES_VISIBLE) {
+            view.setQueryTilesVisibility(model.get(QUERY_TILES_VISIBLE) ? View.VISIBLE : View.GONE);
         } else if (propertyKey == VOICE_SEARCH_BUTTON_CLICK_LISTENER) {
             view.getSearchBoxCoordinator().addVoiceSearchButtonClickListener(
                     model.get(VOICE_SEARCH_BUTTON_CLICK_LISTENER));
diff --git a/chrome/android/java/res/layout/context_menu_header.xml b/chrome/android/java/res/layout/context_menu_header.xml
index dcfef31a..3edb8ed3 100644
--- a/chrome/android/java/res/layout/context_menu_header.xml
+++ b/chrome/android/java/res/layout/context_menu_header.xml
@@ -19,6 +19,8 @@
         android:id="@+id/menu_header_image_container"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/context_menu_header_vertical_padding"
+        android:layout_marginBottom="@dimen/context_menu_bottom_padding"
         android:layout_marginEnd="16dp">
 
         <!-- Circle background for when we have a favicon or monogram -->
@@ -27,9 +29,7 @@
             android:background="@drawable/tile_view_icon_background_modern"
             android:layout_width="@dimen/context_menu_header_circle_bg_diameter"
             android:layout_height="@dimen/context_menu_header_circle_bg_diameter"
-            android:layout_marginTop="@dimen/context_menu_header_circle_bg_vertical_margin"
-            android:layout_marginStart="@dimen/context_menu_header_circle_bg_lateral_margin"
-            android:layout_marginEnd="@dimen/context_menu_header_circle_bg_lateral_margin"
+            android:layout_margin="@dimen/context_menu_header_circle_bg_margin"
             android:visibility="invisible" />
 
         <ImageView
@@ -38,8 +38,6 @@
             android:layout_height="@dimen/context_menu_header_image_max_size"
             android:scaleType="centerInside"
             android:importantForAccessibility="no"
-            android:layout_marginTop="@dimen/context_menu_header_vertical_padding"
-            android:layout_marginBottom="@dimen/context_menu_bottom_padding"
             android:src="@color/thumbnail_placeholder_on_primary_bg" />
     </FrameLayout>
 
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 5f65048..6cbd92c 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -336,13 +336,15 @@
     <dimen name="context_menu_header_vertical_padding">16dp</dimen>
     <dimen name="context_menu_header_image_max_size">60dp</dimen>
     <dimen name="context_menu_header_circle_bg_diameter">48dp</dimen>
-    <dimen name="context_menu_header_circle_bg_lateral_margin">6dp</dimen>
-    <dimen name="context_menu_header_circle_bg_vertical_margin">22dp</dimen>
+    <dimen name="context_menu_header_circle_bg_margin">6dp</dimen>
     <dimen name="context_menu_header_monogram_text_size">16dp</dimen>
     <dimen name="context_menu_header_monogram_size">26dp</dimen>
     <dimen name="context_menu_small_width">268dp</dimen>
     <dimen name="context_menu_small_lateral_margin">30dp</dimen>
 
+    <dimen name="context_menu_popup_header_image_max_size">36dp</dimen>
+    <dimen name="context_menu_popup_header_monogram_size">16dp</dimen>
+
     <!-- Defaults for TabbedModeFirstRunActivity values. -->
     <item type="dimen" name="dialog_fixed_width_major">100%</item>
     <item type="dimen" name="dialog_fixed_width_minor">100%</item>
@@ -392,6 +394,10 @@
     <dimen name="overflow_menu_update_min_height">40sp</dimen>
     <dimen name="overflow_menu_update_padding">12dp</dimen>
 
+    <!-- Settings dimensions -->
+    <dimen name="settings_wide_display_min_padding">16dp</dimen>
+
+
     <!-- Sharing Hub dimensions -->
     <dimen name="sharing_hub_preview_icon_padding">12dp</dimen>
     <dimen name="sharing_hub_preview_icon_rounded_corner_radius">4dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
index 3d1bb03..8e29a685 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
@@ -414,7 +414,7 @@
             loadingStateChanged(isCurrentTabNotNull && currentTab.isLoading());
 
             MenuItem bookmarkMenuItemShortcut = actionBar.findItem(R.id.bookmark_this_page_id);
-            updateBookmarkMenuItemShortcut(bookmarkMenuItemShortcut, currentTab);
+            updateBookmarkMenuItemShortcut(bookmarkMenuItemShortcut, currentTab, /*fromCCT=*/false);
 
             MenuItem offlineMenuItem = actionBar.findItem(R.id.offline_page_id);
             offlineMenuItem.setEnabled(isCurrentTabNotNull && shouldEnableDownloadPage(currentTab));
@@ -1009,8 +1009,8 @@
      * @param currentTab Current tab being displayed.
      */
     protected void updateBookmarkMenuItemShortcut(
-            MenuItem bookmarkMenuItemShortcut, @Nullable Tab currentTab) {
-        if (BookmarkFeatures.isBookmarkMenuItemAsDedicatedRowEnabled()) {
+            MenuItem bookmarkMenuItemShortcut, @Nullable Tab currentTab, boolean fromCCT) {
+        if (!fromCCT && BookmarkFeatures.isBookmarkMenuItemAsDedicatedRowEnabled()) {
             bookmarkMenuItemShortcut.setVisible(false);
             return;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
index 9bd7b6f..502fcfc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
@@ -30,7 +30,7 @@
 import android.widget.CompoundButton;
 import android.widget.EditText;
 import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
+import android.widget.RelativeLayout.LayoutParams;
 import android.widget.Spinner;
 import android.widget.TextView;
 
@@ -47,8 +47,11 @@
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.browser_ui.widget.AlwaysDismissedDialog;
 import org.chromium.components.browser_ui.widget.FadingEdgeScrollView;
+import org.chromium.components.browser_ui.widget.FadingEdgeScrollView.EdgeType;
 import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.components.browser_ui.widget.animation.Interpolators;
+import org.chromium.components.browser_ui.widget.displaystyle.UiConfig;
+import org.chromium.components.browser_ui.widget.displaystyle.ViewResizer;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 
 import java.util.ArrayList;
@@ -104,6 +107,9 @@
     private Runnable mDeleteRunnable;
     private boolean mIsDismissed;
     private Profile mProfile;
+    @Nullable
+    private UiConfig mUiConfig;
+
     /**
      * Builds the editor dialog.
      *
@@ -223,12 +229,11 @@
         // The top shadow is handled by the toolbar, so hide the one used in the field editor.
         FadingEdgeScrollView scrollView =
                 (FadingEdgeScrollView) mLayout.findViewById(R.id.scroll_view);
-        scrollView.setEdgeVisibility(
-                FadingEdgeScrollView.EdgeType.NONE, FadingEdgeScrollView.EdgeType.FADING);
+        scrollView.setEdgeVisibility(EdgeType.NONE, EdgeType.FADING);
 
         // The shadow's top margin doesn't get picked up in the xml; set it programmatically.
         View shadow = mLayout.findViewById(R.id.shadow);
-        RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) shadow.getLayoutParams();
+        LayoutParams params = (LayoutParams) shadow.getLayoutParams();
         params.topMargin = toolbar.getLayoutParams().height;
         shadow.setLayoutParams(params);
         scrollView.getViewTreeObserver().addOnScrollChangedListener(
@@ -444,6 +449,25 @@
         mDataView.addView(mFooter);
     }
 
+    /**
+     * When this layout has a wide display style, it will be width constrained to
+     * {@link UiConfig#WIDE_DISPLAY_STYLE_MIN_WIDTH_DP}. If the current screen width is greater than
+     * UiConfig#WIDE_DISPLAY_STYLE_MIN_WIDTH_DP, the settings layout will be visually centered
+     * by adding padding to both sides.
+     */
+    public void onConfigurationChanged() {
+        if (mUiConfig == null) {
+            if (mDataView != null) {
+                int minWidePaddingPixels = mActivity.getResources().getDimensionPixelSize(
+                        R.dimen.settings_wide_display_min_padding);
+                mUiConfig = new UiConfig(mDataView);
+                ViewResizer.createAndAttach(mDataView, mUiConfig, 0, minWidePaddingPixels);
+            }
+        } else {
+            mUiConfig.updateDisplayStyle();
+        }
+    }
+
     private void removeTextChangedListenersAndInputFilters() {
         if (mCardInput != null) {
             mCardInput.removeTextChangedListener(mCardNumberFormatter);
@@ -555,6 +579,7 @@
         prepareEditor();
         prepareFooter();
         prepareButtons();
+        onConfigurationChanged();
 
         // Temporarily hide the content to avoid blink before animation starts.
         mLayout.setVisibility(View.INVISIBLE);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java
index bce2f7a..c412851f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.autofill.settings;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -12,6 +13,8 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceFragmentCompat;
@@ -39,8 +42,7 @@
         implements PersonalDataManager.PersonalDataManagerObserver {
     private static EditorObserverForTest sObserverForTest;
     static final String PREF_NEW_PROFILE = "new_profile";
-
-    EditorDialog mLastEditorDialogForTest;
+    private @Nullable EditorDialog mEditorDialog;
 
     @Override
     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
@@ -55,6 +57,14 @@
     }
 
     @Override
+    public void onConfigurationChanged(@NonNull Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (mEditorDialog != null) {
+            mEditorDialog.onConfigurationChanged();
+        }
+    }
+
+    @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         menu.clear();
         MenuItem help =
@@ -170,8 +180,7 @@
     public void onDisplayPreferenceDialog(Preference preference) {
         if (preference instanceof AutofillProfileEditorPreference) {
             String guid = ((AutofillProfileEditorPreference) preference).getGUID();
-            EditorDialog editorDialog = prepareEditorDialog(guid);
-            mLastEditorDialogForTest = editorDialog;
+            mEditorDialog = prepareEditorDialog(guid);
             AutofillAddress autofillAddress = null;
             if (guid != null) {
                 AutofillProfile profile = PersonalDataManager.getInstance().getProfile(guid);
@@ -179,7 +188,7 @@
                     autofillAddress = new AutofillAddress(getActivity(), profile);
                 }
             }
-            editAddress(editorDialog, autofillAddress);
+            editAddress(mEditorDialog, autofillAddress);
             return;
         }
 
@@ -228,7 +237,8 @@
         return getPreferenceManager().getContext();
     }
 
+    @VisibleForTesting
     EditorDialog getEditorDialogForTest() {
-        return mLastEditorDialogForTest;
+        return mEditorDialog;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
index 4b3126af..b94137a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
@@ -60,6 +60,9 @@
 
     private static final int INVALID_ITEM_ID = -1;
 
+    /** Experiment params for {@link ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE}. */
+    static final String HIDE_HEADER_IMAGE_PARAM = "hide_header_image";
+
     private WebContents mWebContents;
     private WebContentsObserver mWebContentsObserver;
     private ContextMenuChipController mChipController;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderCoordinator.java
index 4afb017..4e853655 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderCoordinator.java
@@ -5,9 +5,13 @@
 package org.chromium.chrome.browser.contextmenu;
 
 import android.app.Activity;
+import android.content.Context;
 import android.text.SpannableString;
 import android.text.TextUtils;
 
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.omnibox.ChromeAutocompleteSchemeClassifier;
 import org.chromium.chrome.browser.performance_hints.PerformanceHintsObserver.PerformanceClass;
@@ -24,25 +28,62 @@
 
     ContextMenuHeaderCoordinator(Activity activity, @PerformanceClass int performanceClass,
             ContextMenuParams params, Profile profile, ContextMenuNativeDelegate nativeDelegate) {
-        mModel = buildModel(ContextMenuUtils.getTitle(params), getUrl(activity, params, profile));
+        mModel = buildModel(
+                activity, ContextMenuUtils.getTitle(params), getUrl(activity, params, profile));
         mMediator = new ContextMenuHeaderMediator(
                 activity, mModel, performanceClass, params, profile, nativeDelegate);
     }
 
-    private PropertyModel buildModel(String title, CharSequence url) {
-        boolean hideHeaderImage =
+    @VisibleForTesting
+    static PropertyModel buildModel(Context context, String title, CharSequence url) {
+        boolean usePopupContextMenu =
                 ChromeFeatureList.isEnabled(ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE);
-        return new PropertyModel.Builder(ContextMenuHeaderProperties.ALL_KEYS)
-                .with(ContextMenuHeaderProperties.TITLE, title)
-                .with(ContextMenuHeaderProperties.TITLE_MAX_LINES, TextUtils.isEmpty(url) ? 2 : 1)
-                .with(ContextMenuHeaderProperties.URL, url)
-                .with(ContextMenuHeaderProperties.URL_MAX_LINES, TextUtils.isEmpty(title) ? 2 : 1)
-                .with(ContextMenuHeaderProperties.URL_PERFORMANCE_CLASS,
-                        PerformanceClass.PERFORMANCE_UNKNOWN)
-                .with(ContextMenuHeaderProperties.IMAGE, null)
-                .with(ContextMenuHeaderProperties.CIRCLE_BG_VISIBLE, false)
-                .with(ContextMenuHeaderProperties.HIDE_HEADER_IMAGE, hideHeaderImage)
-                .build();
+        boolean hideHeaderImage = ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
+                ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE,
+                ContextMenuCoordinator.HIDE_HEADER_IMAGE_PARAM, false);
+
+        int monogramSizeDimen = usePopupContextMenu
+                ? R.dimen.context_menu_popup_header_monogram_size
+                : R.dimen.context_menu_header_monogram_size;
+
+        PropertyModel model =
+                new PropertyModel.Builder(ContextMenuHeaderProperties.ALL_KEYS)
+                        .with(ContextMenuHeaderProperties.TITLE, title)
+                        .with(ContextMenuHeaderProperties.TITLE_MAX_LINES,
+                                TextUtils.isEmpty(url) ? 2 : 1)
+                        .with(ContextMenuHeaderProperties.URL, url)
+                        .with(ContextMenuHeaderProperties.URL_MAX_LINES,
+                                TextUtils.isEmpty(title) ? 2 : 1)
+                        .with(ContextMenuHeaderProperties.URL_PERFORMANCE_CLASS,
+                                PerformanceClass.PERFORMANCE_UNKNOWN)
+                        .with(ContextMenuHeaderProperties.IMAGE, null)
+                        .with(ContextMenuHeaderProperties.CIRCLE_BG_VISIBLE, false)
+                        .with(ContextMenuHeaderProperties.HIDE_HEADER_IMAGE, hideHeaderImage)
+                        .with(ContextMenuHeaderProperties.MONOGRAM_SIZE_PIXEL,
+                                context.getResources().getDimensionPixelSize(monogramSizeDimen))
+                        .build();
+
+        if (usePopupContextMenu) {
+            int maxImageSize = context.getResources().getDimensionPixelSize(
+                    R.dimen.context_menu_popup_header_image_max_size);
+
+            // Popup context menu leaves the same size for image and monogram.
+            model.set(
+                    ContextMenuHeaderProperties.OVERRIDE_HEADER_IMAGE_MAX_SIZE_PIXEL, maxImageSize);
+            model.set(
+                    ContextMenuHeaderProperties.OVERRIDE_HEADER_CIRCLE_BG_SIZE_PIXEL, maxImageSize);
+            model.set(ContextMenuHeaderProperties.OVERRIDE_HEADER_CIRCLE_BG_MARGIN_PIXEL, 0);
+        } else {
+            // Use invalid override instead of 0, so view binder will not override layout params.
+            model.set(ContextMenuHeaderProperties.OVERRIDE_HEADER_IMAGE_MAX_SIZE_PIXEL,
+                    ContextMenuHeaderProperties.INVALID_OVERRIDE);
+            model.set(ContextMenuHeaderProperties.OVERRIDE_HEADER_CIRCLE_BG_SIZE_PIXEL,
+                    ContextMenuHeaderProperties.INVALID_OVERRIDE);
+            model.set(ContextMenuHeaderProperties.OVERRIDE_HEADER_CIRCLE_BG_MARGIN_PIXEL,
+                    ContextMenuHeaderProperties.INVALID_OVERRIDE);
+        }
+
+        return model;
     }
 
     private CharSequence getUrl(Activity activity, ContextMenuParams params, Profile profile) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderMediator.java
index 47ce1aa..3de9df6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderMediator.java
@@ -99,8 +99,7 @@
             }
         }
 
-        final int size = mContext.getResources().getDimensionPixelSize(
-                R.dimen.context_menu_header_monogram_size);
+        final int size = mModel.get(ContextMenuHeaderProperties.MONOGRAM_SIZE_PIXEL);
 
         icon = Bitmap.createScaledBitmap(icon, size, size, true);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderProperties.java
index 2f3c0d4..e304de1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderProperties.java
@@ -13,6 +13,9 @@
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
 class ContextMenuHeaderProperties {
+    /** Invalid value for OVERRIDE_*_PIXEL resources */
+    static final int INVALID_OVERRIDE = -1;
+
     public static final WritableObjectPropertyKey<String> TITLE = new WritableObjectPropertyKey<>();
     public static final WritableIntPropertyKey TITLE_MAX_LINES = new WritableIntPropertyKey();
     public static final WritableObjectPropertyKey<CharSequence> URL =
@@ -28,7 +31,34 @@
     public static final PropertyModel.WritableBooleanPropertyKey HIDE_HEADER_IMAGE =
             new PropertyModel.WritableBooleanPropertyKey();
 
+    /** Size in pixel of monogram / favicon on link context menu header . */
+    public static final WritableIntPropertyKey MONOGRAM_SIZE_PIXEL = new WritableIntPropertyKey();
+
+    /**
+     * Size in pixel of header image & monogram. When value is not {@link #INVALID_OVERRIDE}, this
+     * number will override the image size defined in the layout.
+     */
+    public static final WritableIntPropertyKey OVERRIDE_HEADER_IMAGE_MAX_SIZE_PIXEL =
+            new WritableIntPropertyKey();
+
+    /**
+     * Size in pixel of circle background behind the monogram. When value is not {@link
+     * #INVALID_OVERRIDE}, this number will override the image size defined in the layout.
+     */
+    public static final WritableIntPropertyKey OVERRIDE_HEADER_CIRCLE_BG_SIZE_PIXEL =
+            new WritableIntPropertyKey();
+
+    /**
+     * Size in pixel of the margin around the circle background behind the monogram. When value is
+     * not {@link #INVALID_OVERRIDE}, this number will override the image size defined in the
+     * layout.
+     */
+    public static final WritableIntPropertyKey OVERRIDE_HEADER_CIRCLE_BG_MARGIN_PIXEL =
+            new WritableIntPropertyKey();
+
     public static final PropertyKey[] ALL_KEYS = {TITLE, TITLE_MAX_LINES, URL,
             TITLE_AND_URL_CLICK_LISTENER, URL_MAX_LINES, IMAGE, CIRCLE_BG_VISIBLE,
-            URL_PERFORMANCE_CLASS, HIDE_HEADER_IMAGE};
+            URL_PERFORMANCE_CLASS, HIDE_HEADER_IMAGE, MONOGRAM_SIZE_PIXEL,
+            OVERRIDE_HEADER_IMAGE_MAX_SIZE_PIXEL, OVERRIDE_HEADER_CIRCLE_BG_SIZE_PIXEL,
+            OVERRIDE_HEADER_CIRCLE_BG_MARGIN_PIXEL};
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderViewBinder.java
index 38d7b05..c7cf9d15 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderViewBinder.java
@@ -7,6 +7,8 @@
 import android.graphics.Bitmap;
 import android.text.TextUtils;
 import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewGroup.MarginLayoutParams;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -73,6 +75,38 @@
                     model.get(ContextMenuHeaderProperties.HIDE_HEADER_IMAGE);
             view.findViewById(R.id.menu_header_image_container)
                     .setVisibility(hideHeaderImage ? View.GONE : View.VISIBLE);
+        } else if (propertyKey
+                == ContextMenuHeaderProperties.OVERRIDE_HEADER_IMAGE_MAX_SIZE_PIXEL) {
+            int maxSizeOverride =
+                    model.get(ContextMenuHeaderProperties.OVERRIDE_HEADER_IMAGE_MAX_SIZE_PIXEL);
+            if (ContextMenuHeaderProperties.INVALID_OVERRIDE != maxSizeOverride) {
+                View image = view.findViewById(R.id.menu_header_image);
+                LayoutParams lp = image.getLayoutParams();
+                lp.width = maxSizeOverride;
+                lp.height = maxSizeOverride;
+                image.setLayoutParams(lp);
+            }
+        } else if (propertyKey
+                == ContextMenuHeaderProperties.OVERRIDE_HEADER_CIRCLE_BG_SIZE_PIXEL) {
+            int sizeOverride =
+                    model.get(ContextMenuHeaderProperties.OVERRIDE_HEADER_CIRCLE_BG_SIZE_PIXEL);
+            if (ContextMenuHeaderProperties.INVALID_OVERRIDE != sizeOverride) {
+                View circleBg = view.findViewById(R.id.circle_background);
+                LayoutParams lp = circleBg.getLayoutParams();
+                lp.width = sizeOverride;
+                lp.height = sizeOverride;
+                circleBg.setLayoutParams(lp);
+            }
+        } else if (propertyKey
+                == ContextMenuHeaderProperties.OVERRIDE_HEADER_CIRCLE_BG_MARGIN_PIXEL) {
+            int marginOverride =
+                    model.get(ContextMenuHeaderProperties.OVERRIDE_HEADER_CIRCLE_BG_MARGIN_PIXEL);
+            if (ContextMenuHeaderProperties.INVALID_OVERRIDE != marginOverride) {
+                View circleBg = view.findViewById(R.id.circle_background);
+                MarginLayoutParams mlp = (MarginLayoutParams) circleBg.getLayoutParams();
+                mlp.setMargins(marginOverride, marginOverride, marginOverride, marginOverride);
+                circleBg.setLayoutParams(mlp);
+            }
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java
index d24fd9a..fba32e3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java
@@ -186,7 +186,7 @@
 
             MenuItem bookmarkItem = menu.findItem(R.id.bookmark_this_page_id);
             if (bookmarkItemVisible) {
-                updateBookmarkMenuItemShortcut(bookmarkItem, currentTab);
+                updateBookmarkMenuItemShortcut(bookmarkItem, currentTab, /*fromCCT=*/true);
             } else {
                 bookmarkItem.setVisible(false);
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
index 862a2d6..05d04d70 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
@@ -8,13 +8,16 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Configuration;
 import android.graphics.Color;
 import android.os.Build;
 import android.os.Bundle;
 import android.view.Menu;
 import android.view.MenuItem;
+import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.widget.Toolbar;
 import androidx.fragment.app.Fragment;
@@ -59,6 +62,8 @@
 import org.chromium.components.browser_ui.settings.FragmentSettingsLauncher;
 import org.chromium.components.browser_ui.settings.SettingsLauncher;
 import org.chromium.components.browser_ui.site_settings.SiteSettingsPreferenceFragment;
+import org.chromium.components.browser_ui.widget.displaystyle.UiConfig;
+import org.chromium.components.browser_ui.widget.displaystyle.ViewResizer;
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.UiUtils;
@@ -85,7 +90,6 @@
          */
         boolean onBackPressed();
     }
-
     static final String EXTRA_SHOW_FRAGMENT = "show_fragment";
     static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = "show_fragment_args";
 
@@ -106,6 +110,9 @@
 
     private BottomSheetController mBottomSheetController;
 
+    @Nullable
+    private UiConfig mUiConfig;
+
     @SuppressLint("InlinedApi")
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -140,11 +147,37 @@
             getSupportFragmentManager().beginTransaction().replace(R.id.content, fragment).commit();
         }
 
+        // Set width constraints
+        configureWideDisplayStyle();
         setStatusBarColor();
-
         initBottomSheet();
     }
 
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        // Set width constraints
+        configureWideDisplayStyle();
+    }
+
+    /**
+     * When this layout has a wide display style, it will be width constrained to
+     * {@link UiConfig#WIDE_DISPLAY_STYLE_MIN_WIDTH_DP}. If the current screen width is greater than
+     * UiConfig#WIDE_DISPLAY_STYLE_MIN_WIDTH_DP, the settings layout will be visually centered
+     * by adding padding to both sides.
+     */
+    private void configureWideDisplayStyle() {
+        if (mUiConfig == null) {
+            int minWidePaddingPixels =
+                    getResources().getDimensionPixelSize(R.dimen.settings_wide_display_min_padding);
+            View view = findViewById(R.id.content);
+            mUiConfig = new UiConfig(view);
+            ViewResizer.createAndAttach(view, mUiConfig, 0, minWidePaddingPixels);
+        } else {
+            mUiConfig.updateDisplayStyle();
+        }
+    }
+
     /** Set up the bottom sheet for this activity. */
     private void initBottomSheet() {
         ViewGroup sheetContainer = findViewById(R.id.sheet_container);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuRenderTest.java
index a5ec82fd..3e308215 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuRenderTest.java
@@ -16,6 +16,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.FeatureList;
+import org.chromium.base.FeatureList.TestValues;
 import org.chromium.base.test.params.ParameterAnnotations;
 import org.chromium.base.test.params.ParameterSet;
 import org.chromium.base.test.params.ParameterizedRunner;
@@ -23,6 +25,7 @@
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.contextmenu.ContextMenuCoordinator.ListItemType;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.util.ChromeRenderTestRule;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
@@ -56,6 +59,8 @@
     private View mView;
     private View mFrame;
 
+    private FeatureList.TestValues mTestValues;
+
     public ContextMenuRenderTest(boolean nightModeEnabled) {
         NightModeTestUtils.setUpNightModeForDummyUiActivity(nightModeEnabled);
         mRenderTestRule.setNightModeEnabled(nightModeEnabled);
@@ -64,6 +69,11 @@
     @Override
     public void setUpTest() throws Exception {
         super.setUpTest();
+
+        mTestValues = new TestValues();
+        mTestValues.addFeatureFlagOverride(ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE, false);
+        FeatureList.setTestValues(mTestValues);
+
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mListItems = new ModelList();
             mAdapter = new ModelListAdapter(mListItems);
@@ -103,6 +113,7 @@
             NightModeTestUtils.tearDownNightModeForDummyUiActivity();
             mListItems.clear();
         });
+        FeatureList.setTestValues(null);
         super.tearDownTest();
     }
 
@@ -110,37 +121,56 @@
     @LargeTest
     @Feature({"RenderTest"})
     public void testContextMenuViewWithLink() throws IOException {
-        doTestContextMenuViewWithLink("context_menu_with_link", /*hideHeaderImage=*/false);
+        doTestContextMenuViewWithLink("context_menu_with_link");
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"RenderTest"})
+    public void testContextMenuViewWithLink_Popup() throws IOException {
+        mTestValues.addFeatureFlagOverride(ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE, true);
+        doTestContextMenuViewWithLink("context_menu_with_link_popup");
     }
 
     @Test
     @LargeTest
     @Feature({"RenderTest"})
     public void testContextMenuViewWithLink_HideHeaderImage() throws IOException {
-        doTestContextMenuViewWithLink("context_menu_with_link_popup", /*hideHeaderImage=*/true);
+        mTestValues.addFeatureFlagOverride(ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE, true);
+        mTestValues.addFieldTrialParamOverride(ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE,
+                ContextMenuCoordinator.HIDE_HEADER_IMAGE_PARAM, "true");
+        doTestContextMenuViewWithLink("context_menu_with_link_no_header");
     }
 
     @Test
     @LargeTest
     @Feature({"RenderTest"})
     public void testContextMenuViewWithImageLink() throws IOException {
-        doTestContextMenuViewWithImageLink(
-                "context_menu_with_image_link", /*hideHeaderImage=*/false);
+        doTestContextMenuViewWithImageLink("context_menu_with_image_link");
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"RenderTest"})
+    public void testContextMenuViewWithImageLink_Popup() throws IOException {
+        mTestValues.addFeatureFlagOverride(ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE, true);
+        doTestContextMenuViewWithImageLink("context_menu_with_image_link_popup");
     }
 
     @Test
     @LargeTest
     @Feature({"RenderTest"})
     public void testContextMenuViewWithImageLink_HideHeaderImage() throws IOException {
-        doTestContextMenuViewWithImageLink(
-                "context_menu_with_image_link_popup", /*hideHeaderImage=*/true);
+        mTestValues.addFeatureFlagOverride(ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE, true);
+        mTestValues.addFieldTrialParamOverride(ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE,
+                ContextMenuCoordinator.HIDE_HEADER_IMAGE_PARAM, "true");
+        doTestContextMenuViewWithImageLink("context_menu_with_image_link_no_header");
     }
 
-    private void doTestContextMenuViewWithLink(String id, boolean hideHeaderImage)
-            throws IOException {
+    private void doTestContextMenuViewWithLink(String id) throws IOException {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mListItems.add(new ListItem(ListItemType.HEADER,
-                    getHeaderModel("", "www.google.com", false, hideHeaderImage)));
+            mListItems.add(
+                    new ListItem(ListItemType.HEADER, getHeaderModel("", "www.google.com", false)));
             mListItems.add(new ListItem(ListItemType.DIVIDER, new PropertyModel()));
             mListItems.add((
                     new ListItem(ListItemType.CONTEXT_MENU_ITEM, getItemModel("Open in new tab"))));
@@ -154,11 +184,10 @@
         mRenderTestRule.render(mFrame, id);
     }
 
-    private void doTestContextMenuViewWithImageLink(String id, boolean hideHeaderImage)
-            throws IOException {
+    private void doTestContextMenuViewWithImageLink(String id) throws IOException {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mListItems.add(new ListItem(ListItemType.HEADER,
-                    getHeaderModel("Capybara", "www.google.com", true, hideHeaderImage)));
+            mListItems.add(new ListItem(
+                    ListItemType.HEADER, getHeaderModel("Capybara", "www.google.com", true)));
             mListItems.add(new ListItem(ListItemType.DIVIDER, new PropertyModel()));
             mListItems.add((
                     new ListItem(ListItemType.CONTEXT_MENU_ITEM, getItemModel("Open in new tab"))));
@@ -180,28 +209,22 @@
     }
 
     private PropertyModel getHeaderModel(
-            String title, CharSequence url, boolean hasImageThumbnail, boolean hideHeaderImage) {
+            String title, CharSequence url, boolean hasImageThumbnail) {
+        PropertyModel model = ContextMenuHeaderCoordinator.buildModel(getActivity(), title, url);
         Bitmap image;
         if (hasImageThumbnail) {
             image = BitmapFactory.decodeFile(
                     UrlUtils.getIsolatedTestFilePath("chrome/test/data/android/capybara.jpg"));
         } else {
-            final int size = getActivity().getResources().getDimensionPixelSize(
-                    R.dimen.context_menu_header_monogram_size);
+            final int size = model.get(ContextMenuHeaderProperties.MONOGRAM_SIZE_PIXEL);
             image = BitmapFactory.decodeFile(UrlUtils.getIsolatedTestFilePath(
                     "chrome/test/data/android/UiCapture/cloud.png"));
             image = Bitmap.createScaledBitmap(image, size, size, true);
         }
 
-        return new PropertyModel.Builder(ContextMenuHeaderProperties.ALL_KEYS)
-                .with(ContextMenuHeaderProperties.TITLE, title)
-                .with(ContextMenuHeaderProperties.TITLE_MAX_LINES, 1)
-                .with(ContextMenuHeaderProperties.URL, url)
-                .with(ContextMenuHeaderProperties.URL_MAX_LINES, 1)
-                .with(ContextMenuHeaderProperties.IMAGE, image)
-                .with(ContextMenuHeaderProperties.CIRCLE_BG_VISIBLE, !hasImageThumbnail)
-                .with(ContextMenuHeaderProperties.HIDE_HEADER_IMAGE, hideHeaderImage)
-                .build();
+        model.set(ContextMenuHeaderProperties.IMAGE, image);
+        model.set(ContextMenuHeaderProperties.CIRCLE_BG_VISIBLE, !hasImageThumbnail);
+        return model;
     }
 
     private PropertyModel getItemModel(String title) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
index 26d22fc..af53dac 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
@@ -484,16 +484,10 @@
         mMediator.setUrlHasFocus(true);
         mMediator.setShowIconsWhenUrlFocused(true);
         Assert.assertFalse(mMediator.isStoreIconShowing());
-        Assert.assertNotEquals(mStoreIconDrawable,
-                mModel.get(StatusProperties.STATUS_ICON_RESOURCE)
-                        .getDrawable(mContext, mResources));
 
         // Try to show the store icon.
         mMediator.showStoreIcon(mWindowAndroid, "test2.com", mStoreIconDrawable, 0, true);
         Assert.assertFalse(mMediator.isStoreIconShowing());
-        Assert.assertNotEquals(mStoreIconDrawable,
-                mModel.get(StatusProperties.STATUS_ICON_RESOURCE)
-                        .getDrawable(mContext, mResources));
     }
 
     @Test
@@ -505,16 +499,10 @@
         mMediator.setUrlHasFocus(true);
         mMediator.setShowIconsWhenUrlFocused(true);
         Assert.assertFalse(mMediator.isStoreIconShowing());
-        Assert.assertNotEquals(mStoreIconDrawable,
-                mModel.get(StatusProperties.STATUS_ICON_RESOURCE)
-                        .getDrawable(mContext, mResources));
 
         // Try to show the store icon.
         mMediator.showStoreIcon(mWindowAndroid, "test.com", mStoreIconDrawable, 0, true);
         Assert.assertFalse(mMediator.isStoreIconShowing());
-        Assert.assertNotEquals(mStoreIconDrawable,
-                mModel.get(StatusProperties.STATUS_ICON_RESOURCE)
-                        .getDrawable(mContext, mResources));
     }
 
     @Test
@@ -526,16 +514,10 @@
         mMediator.setUrlHasFocus(true);
         mMediator.setShowIconsWhenUrlFocused(true);
         Assert.assertFalse(mMediator.isStoreIconShowing());
-        Assert.assertNotEquals(mStoreIconDrawable,
-                mModel.get(StatusProperties.STATUS_ICON_RESOURCE)
-                        .getDrawable(mContext, mResources));
 
         // Try to show the store icon.
         mMediator.showStoreIcon(mWindowAndroid, "test.com", mStoreIconDrawable, 0, true);
         Assert.assertTrue(mMediator.isStoreIconShowing());
-        Assert.assertEquals(mStoreIconDrawable,
-                mModel.get(StatusProperties.STATUS_ICON_RESOURCE)
-                        .getDrawable(mContext, mResources));
         Assert.assertEquals(IconTransitionType.ROTATE,
                 mModel.get(StatusProperties.STATUS_ICON_RESOURCE).getTransitionType());
         Assert.assertNotNull(
@@ -555,9 +537,6 @@
         mMediator.setUrlHasFocus(true);
         mMediator.setShowIconsWhenUrlFocused(true);
         Assert.assertFalse(mMediator.isStoreIconShowing());
-        Assert.assertNotEquals(mStoreIconDrawable,
-                mModel.get(StatusProperties.STATUS_ICON_RESOURCE)
-                        .getDrawable(mContext, mResources));
     }
 
     @Test
@@ -569,16 +548,10 @@
         mMediator.setUrlHasFocus(true);
         mMediator.setShowIconsWhenUrlFocused(true);
         Assert.assertFalse(mMediator.isStoreIconShowing());
-        Assert.assertNotEquals(mStoreIconDrawable,
-                mModel.get(StatusProperties.STATUS_ICON_RESOURCE)
-                        .getDrawable(mContext, mResources));
 
         // Try to show the store icon.
         mMediator.showStoreIcon(mWindowAndroid, "test.com", mStoreIconDrawable, 0, false);
         Assert.assertTrue(mMediator.isStoreIconShowing());
-        Assert.assertEquals(mStoreIconDrawable,
-                mModel.get(StatusProperties.STATUS_ICON_RESOURCE)
-                        .getDrawable(mContext, mResources));
         Assert.assertEquals(IconTransitionType.ROTATE,
                 mModel.get(StatusProperties.STATUS_ICON_RESOURCE).getTransitionType());
         Assert.assertNotNull(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusViewRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusViewRenderTest.java
index edd786e..748a9dd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusViewRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusViewRenderTest.java
@@ -167,7 +167,7 @@
         runOnUiThreadBlocking(() -> {
             Drawable storeIconDrawable = ResourcesCompat.getDrawable(getActivity().getResources(),
                     R.drawable.ic_storefront_blue, getActivity().getTheme());
-            StatusIconResource statusIcon = new StatusIconResource(storeIconDrawable);
+            StatusIconResource statusIcon = new PermissionIconResource(storeIconDrawable, false);
             statusIcon.setTransitionType(StatusView.IconTransitionType.ROTATE);
             mStatusModel.set(StatusProperties.STATUS_ICON_ALPHA, 1f);
             mStatusModel.set(StatusProperties.SHOW_STATUS_ICON, true);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java
index b8d9f4d..dec0c85 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java
@@ -544,14 +544,26 @@
         doReturn(true).when(mBookmarkBridge).isEditBookmarksEnabled();
 
         MenuItem bookmarkMenuItemShortcut = Mockito.mock(MenuItem.class);
-        mAppMenuPropertiesDelegate.updateBookmarkMenuItemShortcut(bookmarkMenuItemShortcut, mTab);
+        mAppMenuPropertiesDelegate.updateBookmarkMenuItemShortcut(
+                bookmarkMenuItemShortcut, mTab, /*fromCCT=*/false);
+        verify(bookmarkMenuItemShortcut).setEnabled(true);
+    }
+
+    @Test
+    public void updateBookmarkMenuItemShortcut_fromCCT() {
+        doReturn(true).when(mBookmarkBridge).isEditBookmarksEnabled();
+
+        MenuItem bookmarkMenuItemShortcut = Mockito.mock(MenuItem.class);
+        mAppMenuPropertiesDelegate.updateBookmarkMenuItemShortcut(
+                bookmarkMenuItemShortcut, mTab, /*fromCCT=*/true);
         verify(bookmarkMenuItemShortcut).setEnabled(true);
     }
 
     @Test
     public void updateBookmarkMenuItemShortcut_NullTab() {
         MenuItem bookmarkMenuItemShortcut = Mockito.mock(MenuItem.class);
-        mAppMenuPropertiesDelegate.updateBookmarkMenuItemShortcut(bookmarkMenuItemShortcut, null);
+        mAppMenuPropertiesDelegate.updateBookmarkMenuItemShortcut(
+                bookmarkMenuItemShortcut, null, /*fromCCT=*/false);
         verify(bookmarkMenuItemShortcut).setEnabled(false);
     }
 
@@ -560,7 +572,8 @@
         mBookmarkBridgeSupplier.set(null);
 
         MenuItem bookmarkMenuItemShortcut = Mockito.mock(MenuItem.class);
-        mAppMenuPropertiesDelegate.updateBookmarkMenuItemShortcut(bookmarkMenuItemShortcut, mTab);
+        mAppMenuPropertiesDelegate.updateBookmarkMenuItemShortcut(
+                bookmarkMenuItemShortcut, mTab, /*fromCCT=*/false);
         verify(bookmarkMenuItemShortcut).setEnabled(false);
     }
 
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index cd9b303..8a8c3b8 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -279,8 +279,7 @@
              process_type == switches::kCloudPrintServiceProcess) {
     score = content::kMiscOomScore;
 #if BUILDFLAG(ENABLE_NACL)
-  } else if (process_type == switches::kNaClLoaderProcess ||
-             process_type == switches::kNaClLoaderNonSfiProcess) {
+  } else if (process_type == switches::kNaClLoaderProcess) {
     score = content::kPluginOomScore;
 #endif
   } else if (process_type == switches::kZygoteProcess || process_type.empty()) {
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 2039bbe4..d006192a 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -10141,6 +10141,10 @@
         </message>
       </if>
 
+      <message name="IDS_A11Y_OMNIBOX_CHIP_HINT" desc="A navigational hint given to screenreader users, informing them that a setting which was just announced is accessible via the address bar.">
+        Change this setting in the address bar.
+      </message>
+
       <!-- Protected media identifier permission infobar -->
       <message name="IDS_MANAGE_PASSWORDS_CONFIRM_GENERATED_TEXT" desc="A message that the browser shows after saving a password it has autogenerated for the user. This message appears in a bubble and contains a link to all the user's saved autogenerated passwords. The link text is a separate string in the translation console and appears here as placeholder text.">
         View and manage saved passwords in your <ph name="SAVED_PASSWORDS_STORE">$1<ex>Google Account</ex></ph>
diff --git a/chrome/app/generated_resources_grd/IDS_A11Y_OMNIBOX_CHIP_HINT.png.sha1 b/chrome/app/generated_resources_grd/IDS_A11Y_OMNIBOX_CHIP_HINT.png.sha1
new file mode 100644
index 0000000..b010dfcc
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_A11Y_OMNIBOX_CHIP_HINT.png.sha1
@@ -0,0 +1 @@
+0f50b173e0333bf9bf385130bcd54fae904b7bb9
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 118f020..3210670 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2418,7 +2418,6 @@
     "//ui/webui/resources/js/browser_command:mojo_bindings",
   ]
   if (is_chromeos_ash) {
-    testonly = enable_weston_test
     sources += [
       "apps/digital_goods/digital_goods_factory_impl.cc",
       "apps/digital_goods/digital_goods_factory_impl.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 8332885..4433abc0 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2637,6 +2637,26 @@
      nullptr}};
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+const FeatureEntry::FeatureParam kMaxOverlays1 = {features::kMaxOverlaysParam,
+                                                  "1"};
+const FeatureEntry::FeatureParam kMaxOverlays2 = {features::kMaxOverlaysParam,
+                                                  "2"};
+const FeatureEntry::FeatureParam kMaxOverlays3 = {features::kMaxOverlaysParam,
+                                                  "3"};
+const FeatureEntry::FeatureParam kMaxOverlays4 = {features::kMaxOverlaysParam,
+                                                  "4"};
+const FeatureEntry::FeatureParam kMaxOverlays5 = {features::kMaxOverlaysParam,
+                                                  "5"};
+const FeatureEntry::FeatureParam kMaxOverlays6 = {features::kMaxOverlaysParam,
+                                                  "6"};
+
+const FeatureEntry::FeatureVariation kUseMultipleOverlaysVariations[] = {
+    {"1", &kMaxOverlays1, 1, nullptr}, {"2", &kMaxOverlays2, 1, nullptr},
+    {"3", &kMaxOverlays3, 1, nullptr}, {"4", &kMaxOverlays4, 1, nullptr},
+    {"5", &kMaxOverlays5, 1, nullptr}, {"6", &kMaxOverlays6, 1, nullptr}};
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 // RECORDING USER METRICS FOR FLAGS:
 // -----------------------------------------------------------------------------
 // The first line of the entry is the internal name.
@@ -5499,19 +5519,6 @@
      flag_descriptions::kSharingSendViaSyncDescription, kOsAll,
      FEATURE_VALUE_TYPE(kSharingSendViaSync)},
 
-#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
-    defined(OS_FUCHSIA)
-    {"sharing-hub-desktop-app-menu",
-     flag_descriptions::kSharingHubDesktopAppMenuName,
-     flag_descriptions::kSharingHubDesktopAppMenuDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(sharing_hub::kSharingHubDesktopAppMenu)},
-    {"sharing-hub-desktop-omnibox",
-     flag_descriptions::kSharingHubDesktopOmniboxName,
-     flag_descriptions::kSharingHubDesktopOmniboxDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(sharing_hub::kSharingHubDesktopOmnibox)},
-#endif  // defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) ||
-        // defined(OS_FUCHSIA)
-
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     {"ash-enable-pip-rounded-corners",
      flag_descriptions::kAshEnablePipRoundedCornersName,
@@ -7690,6 +7697,14 @@
      FEATURE_VALUE_TYPE(features::kImproveAccessibilityTreeUsingLocalML)},
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    {"use-multiple-overlays", flag_descriptions::kUseMultipleOverlaysName,
+     flag_descriptions::kUseMultipleOverlaysDescription, kOsCrOS,
+     FEATURE_WITH_PARAMS_VALUE_TYPE(features::kUseMultipleOverlays,
+                                    kUseMultipleOverlaysVariations,
+                                    "UseMultipleOverlays")},
+#endif
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/ash/arc/tracing/arc_app_performance_tracing_test_helper.cc b/chrome/browser/ash/arc/tracing/arc_app_performance_tracing_test_helper.cc
index 033afa3..becfe754 100644
--- a/chrome/browser/ash/arc/tracing/arc_app_performance_tracing_test_helper.cc
+++ b/chrome/browser/ash/arc/tracing/arc_app_performance_tracing_test_helper.cc
@@ -44,7 +44,8 @@
 
 // static
 views::Widget* ArcAppPerformanceTracingTestHelper::CreateArcWindow(
-    const std::string& window_app_id) {
+    const std::string& window_app_id,
+    exo::Surface* shell_root_surface) {
   views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
   params.bounds = gfx::Rect(5, 5, 20, 20);
   params.context = nullptr;
@@ -53,7 +54,9 @@
   // Set ARC id before showing the window to be recognized in
   // AppServiceAppWindowShelfController.
   exo::SetShellApplicationId(widget->GetNativeWindow(), window_app_id);
-  exo::SetShellRootSurface(widget->GetNativeWindow(), new exo::Surface());
+  exo::SetShellRootSurface(widget->GetNativeWindow(), shell_root_surface
+                                                          ? shell_root_surface
+                                                          : new exo::Surface());
   widget->Show();
   widget->Activate();
   return widget;
diff --git a/chrome/browser/ash/arc/tracing/arc_app_performance_tracing_test_helper.h b/chrome/browser/ash/arc/tracing/arc_app_performance_tracing_test_helper.h
index ceea492..57220aa5 100644
--- a/chrome/browser/ash/arc/tracing/arc_app_performance_tracing_test_helper.h
+++ b/chrome/browser/ash/arc/tracing/arc_app_performance_tracing_test_helper.h
@@ -14,6 +14,7 @@
 class Profile;
 
 namespace exo {
+class Surface;
 class WMHelper;
 }
 
@@ -39,7 +40,12 @@
   virtual ~ArcAppPerformanceTracingTestHelper();
 
   // Creates app window as ARC++ window.
-  static views::Widget* CreateArcWindow(const std::string& window_app_id);
+  // Caller retains ownership of |shell_root_surface|.
+  // If |shell_root_surface| is not given or is nullptr, one will be created,
+  // which should be cleaned up by the surface tree destruction.
+  static views::Widget* CreateArcWindow(
+      const std::string& window_app_id,
+      exo::Surface* shell_root_surface = nullptr);
 
   void SetUp(Profile* profile);
   void TearDown();
diff --git a/chrome/browser/ash/arc/tracing/arc_app_performance_tracing_unittest.cc b/chrome/browser/ash/arc/tracing/arc_app_performance_tracing_unittest.cc
index 2194d79a..f29ce7d 100644
--- a/chrome/browser/ash/arc/tracing/arc_app_performance_tracing_unittest.cc
+++ b/chrome/browser/ash/arc/tracing/arc_app_performance_tracing_unittest.cc
@@ -85,6 +85,7 @@
   }
 
   void TearDown() override {
+    shell_root_surface_.reset();
     tracing_helper_.TearDown();
     arc_test_.TearDown();
     BrowserWithTestWindowTest::TearDown();
@@ -93,9 +94,10 @@
  protected:
   // Ensures that tracing is active.
   views::Widget* StartArcFocusAppTracing() {
+    shell_root_surface_ = std::make_unique<exo::Surface>();
     views::Widget* const arc_widget =
         ArcAppPerformanceTracingTestHelper::CreateArcWindow(
-            "org.chromium.arc.1");
+            "org.chromium.arc.1", shell_root_surface_.get());
     DCHECK(arc_widget && arc_widget->GetNativeWindow());
     tracing_helper().GetTracing()->OnWindowActivated(
         wm::ActivationChangeObserver::ActivationReason::ACTIVATION_CLIENT,
@@ -122,6 +124,7 @@
   }
 
  private:
+  std::unique_ptr<exo::Surface> shell_root_surface_;
   ArcAppPerformanceTracingTestHelper tracing_helper_;
   ArcAppTest arc_test_;
 };
@@ -137,8 +140,10 @@
   EXPECT_FALSE(tracing_helper().GetTracingSession());
 
   // Create window second.
+  exo::Surface shell_root_surface1;
   views::Widget* const arc_widget1 =
-      ArcAppPerformanceTracingTestHelper::CreateArcWindow("org.chromium.arc.1");
+      ArcAppPerformanceTracingTestHelper::CreateArcWindow("org.chromium.arc.1",
+                                                          &shell_root_surface1);
   ASSERT_TRUE(arc_widget1);
   ASSERT_TRUE(arc_widget1->GetNativeWindow());
   tracing_helper().GetTracing()->OnWindowActivated(
@@ -149,8 +154,10 @@
   EXPECT_FALSE(tracing_helper().GetTracingSession()->tracing_active());
 
   // Test reverse order, create window first.
+  exo::Surface shell_root_surface2;
   views::Widget* const arc_widget2 =
-      ArcAppPerformanceTracingTestHelper::CreateArcWindow("org.chromium.arc.2");
+      ArcAppPerformanceTracingTestHelper::CreateArcWindow("org.chromium.arc.2",
+                                                          &shell_root_surface2);
   ASSERT_TRUE(arc_widget2);
   ASSERT_TRUE(arc_widget2->GetNativeWindow());
   tracing_helper().GetTracing()->OnWindowActivated(
@@ -170,8 +177,10 @@
 }
 
 TEST_F(ArcAppPerformanceTracingTest, TracingNotScheduledForNonFocusApp) {
+  exo::Surface shell_root_surface;
   views::Widget* const arc_widget =
-      ArcAppPerformanceTracingTestHelper::CreateArcWindow("org.chromium.arc.1");
+      ArcAppPerformanceTracingTestHelper::CreateArcWindow("org.chromium.arc.1",
+                                                          &shell_root_surface);
   ASSERT_TRUE(arc_widget);
   ASSERT_TRUE(arc_widget->GetNativeWindow());
   tracing_helper().GetTracing()->OnWindowActivated(
@@ -224,8 +233,10 @@
 
 TEST_F(ArcAppPerformanceTracingTest, TracingNotScheduledWhenAppSyncDisabled) {
   tracing_helper().DisableAppSync();
+  exo::Surface shell_root_surface;
   views::Widget* const arc_widget =
-      ArcAppPerformanceTracingTestHelper::CreateArcWindow("org.chromium.arc.1");
+      ArcAppPerformanceTracingTestHelper::CreateArcWindow("org.chromium.arc.1",
+                                                          &shell_root_surface);
   ASSERT_TRUE(arc_widget);
   ASSERT_TRUE(arc_widget->GetNativeWindow());
   tracing_helper().GetTracing()->OnWindowActivated(
@@ -242,8 +253,10 @@
 TEST_F(ArcAppPerformanceTracingTest, TimeToFirstFrameRendered) {
   const std::string app_id =
       ArcAppListPrefs::GetAppId(kFocusAppPackage, kFocusAppActivity);
+  exo::Surface shell_root_surface;
   views::Widget* const arc_widget =
-      ArcAppPerformanceTracingTestHelper::CreateArcWindow("org.chromium.arc.1");
+      ArcAppPerformanceTracingTestHelper::CreateArcWindow("org.chromium.arc.1",
+                                                          &shell_root_surface);
   DCHECK(arc_widget && arc_widget->GetNativeWindow());
 
   tracing_helper().GetTracing()->OnWindowActivated(
diff --git a/chrome/browser/ash/exo/chrome_data_exchange_delegate_unittest.cc b/chrome/browser/ash/exo/chrome_data_exchange_delegate_unittest.cc
index 9dd5923..01de4f9a 100644
--- a/chrome/browser/ash/exo/chrome_data_exchange_delegate_unittest.cc
+++ b/chrome/browser/ash/exo/chrome_data_exchange_delegate_unittest.cc
@@ -142,39 +142,39 @@
 };
 
 TEST_F(ChromeDataExchangeDelegateTest, GetDataTransferEndpointType) {
+  // Create container window as the parent for other windows.
+  aura::Window container_window(nullptr, aura::client::WINDOW_TYPE_NORMAL);
+  container_window.Init(ui::LAYER_NOT_DRAWN);
+
   // ChromeDataExchangeDelegate always checks app type in
   // window->GetToplevelWindow(), so we must create a parent window with
   // delegate and app type set, but use the child window in tests. Arc:
-  std::unique_ptr<aura::Window> arc_toplevel(
-      aura::test::CreateTestWindowWithDelegate(&delegate_, 0, gfx::Rect(),
-                                               nullptr));
+  aura::Window* arc_toplevel = aura::test::CreateTestWindowWithDelegate(
+      &delegate_, 0, gfx::Rect(), &container_window);
   arc_toplevel->SetProperty(aura::client::kAppType,
                             static_cast<int>(ash::AppType::ARC_APP));
-  ASSERT_TRUE(ash::IsArcWindow(arc_toplevel.get()));
+  ASSERT_TRUE(ash::IsArcWindow(arc_toplevel));
   aura::Window* arc_window =
-      aura::test::CreateTestWindowWithBounds(gfx::Rect(), arc_toplevel.get());
+      aura::test::CreateTestWindowWithBounds(gfx::Rect(), arc_toplevel);
   ASSERT_TRUE(ash::IsArcWindow(arc_window->GetToplevelWindow()));
 
   // Crostini:
-  std::unique_ptr<aura::Window> crostini_toplevel(
-      aura::test::CreateTestWindowWithDelegate(&delegate_, 0, gfx::Rect(),
-                                               nullptr));
+  aura::Window* crostini_toplevel = aura::test::CreateTestWindowWithDelegate(
+      &delegate_, 0, gfx::Rect(), &container_window);
   crostini_toplevel->SetProperty(aura::client::kAppType,
                                  static_cast<int>(ash::AppType::CROSTINI_APP));
-  ASSERT_TRUE(crostini::IsCrostiniWindow(crostini_toplevel.get()));
-  aura::Window* crostini_window = aura::test::CreateTestWindowWithBounds(
-      gfx::Rect(), crostini_toplevel.get());
+  ASSERT_TRUE(crostini::IsCrostiniWindow(crostini_toplevel));
+  aura::Window* crostini_window =
+      aura::test::CreateTestWindowWithBounds(gfx::Rect(), crostini_toplevel);
   ASSERT_TRUE(crostini::IsCrostiniWindow(crostini_window->GetToplevelWindow()));
 
   // Plugin VM:
-  std::unique_ptr<aura::Window> plugin_vm_toplevel(
-      aura::test::CreateTestWindowWithDelegate(&delegate_, 0, gfx::Rect(),
-                                               nullptr));
-  exo::SetShellApplicationId(plugin_vm_toplevel.get(),
-                             "org.chromium.plugin_vm_ui");
-  ASSERT_TRUE(plugin_vm::IsPluginVmAppWindow(plugin_vm_toplevel.get()));
-  aura::Window* plugin_vm_window = aura::test::CreateTestWindowWithBounds(
-      gfx::Rect(), plugin_vm_toplevel.get());
+  aura::Window* plugin_vm_toplevel = aura::test::CreateTestWindowWithDelegate(
+      &delegate_, 0, gfx::Rect(), &container_window);
+  exo::SetShellApplicationId(plugin_vm_toplevel, "org.chromium.plugin_vm_ui");
+  ASSERT_TRUE(plugin_vm::IsPluginVmAppWindow(plugin_vm_toplevel));
+  aura::Window* plugin_vm_window =
+      aura::test::CreateTestWindowWithBounds(gfx::Rect(), plugin_vm_toplevel);
   ASSERT_TRUE(
       plugin_vm::IsPluginVmAppWindow(plugin_vm_window->GetToplevelWindow()));
 
diff --git a/chrome/browser/ash/input_method/input_method_engine_unittest.cc b/chrome/browser/ash/input_method/input_method_engine_unittest.cc
index 2a2b4f84..4bd9a7b 100644
--- a/chrome/browser/ash/input_method/input_method_engine_unittest.cc
+++ b/chrome/browser/ash/input_method/input_method_engine_unittest.cc
@@ -102,7 +102,6 @@
   void OnBlur(const std::string& engine_id, int context_id) override {
     calls_bitmap_ |= ONBLUR;
   }
-  void OnTouch(ui::EventPointerType pointerType) override {}
   void OnKeyEvent(
       const std::string& engine_id,
       const ui::KeyEvent& event,
diff --git a/chrome/browser/ash/input_method/native_input_method_engine_browsertest.cc b/chrome/browser/ash/input_method/native_input_method_engine_browsertest.cc
index c92375d..d865b165 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine_browsertest.cc
+++ b/chrome/browser/ash/input_method/native_input_method_engine_browsertest.cc
@@ -61,7 +61,6 @@
   TestObserver(const TestObserver&) = delete;
   TestObserver& operator=(const TestObserver&) = delete;
 
-  void OnTouch(ui::EventPointerType pointerType) override {}
   void OnKeyEvent(
       const std::string& engine_id,
       const ui::KeyEvent& event,
diff --git a/chrome/browser/ash/input_method/native_input_method_engine_unittest.cc b/chrome/browser/ash/input_method/native_input_method_engine_unittest.cc
index fa36387..36a83d1 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine_unittest.cc
+++ b/chrome/browser/ash/input_method/native_input_method_engine_unittest.cc
@@ -177,8 +177,6 @@
   mojo::Receiver<ime::mojom::InputEngineManager> receiver_;
 };
 
-// TODO(crbug.com/1148157): Refactor NativeInputMethodEngine etc. to avoid
-// hidden dependencies on globals such as ImeBridge.
 class NativeInputMethodEngineTest : public ::testing::Test {
  public:
   void SetUp() override {
@@ -700,8 +698,6 @@
   InputMethodManager::Shutdown();
 }
 
-// TODO(crbug.com/1148157): Refactor NativeInputMethodEngine etc. to avoid
-// hidden dependencies on globals such as ImeBridge.
 class NativeInputMethodEngineWithRenderViewHostTest
     : public content::RenderViewHostTestHarness {
   void SetUp() override {
diff --git a/chrome/browser/ash/smb_client/smbfs_share_unittest.cc b/chrome/browser/ash/smb_client/smbfs_share_unittest.cc
index b886417..9e8e648 100644
--- a/chrome/browser/ash/smb_client/smbfs_share_unittest.cc
+++ b/chrome/browser/ash/smb_client/smbfs_share_unittest.cc
@@ -99,8 +99,11 @@
 
 class SmbFsShareTest : public testing::Test {
  protected:
+  static void SetUpTestSuite() {
+    disks::DiskMountManager::InitializeForTesting(disk_mount_manager());
+  }
+
   void SetUp() override {
-    disks::DiskMountManager::InitializeForTesting(disk_mount_manager_);
     file_manager::VolumeManagerFactory::GetInstance()->SetTestingFactory(
         &profile_, base::BindRepeating(&BuildVolumeManager));
 
@@ -120,13 +123,19 @@
     file_manager::VolumeManager::Get(&profile_)->RemoveObserver(&observer_);
   }
 
+  static file_manager::FakeDiskMountManager* disk_mount_manager() {
+    static file_manager::FakeDiskMountManager* manager =
+        new file_manager::FakeDiskMountManager();
+    return manager;
+  }
+
   std::unique_ptr<smbfs::SmbFsHost> CreateSmbFsHost(
       SmbFsShare* share,
       mojo::Receiver<smbfs::mojom::SmbFs>* smbfs_receiver,
       mojo::Remote<smbfs::mojom::SmbFsDelegate>* delegate) {
     return std::make_unique<smbfs::SmbFsHost>(
         std::make_unique<disks::MountPoint>(base::FilePath(kMountPath),
-                                            disk_mount_manager_),
+                                            disk_mount_manager()),
         share,
         mojo::Remote<smbfs::mojom::SmbFs>(
             smbfs_receiver->BindNewPipeAndPassRemote()),
@@ -136,8 +145,6 @@
   content::BrowserTaskEnvironment task_environment_{
       content::BrowserTaskEnvironment::REAL_IO_THREAD,
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
-  file_manager::FakeDiskMountManager* disk_mount_manager_ =
-      new file_manager::FakeDiskMountManager;
   TestingProfile profile_;
   MockVolumeManagerObsever observer_;
 
diff --git a/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java b/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
index b71d60b8..d259af64 100644
--- a/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
+++ b/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
@@ -119,6 +119,7 @@
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@Features.DisableFeatures({ChromeFeatureList.MESSAGES_FOR_ANDROID_INFRASTRUCTURE})
 public class AppBannerManagerTest {
     @Rule
     public ChromeTabbedActivityTestRule mTabbedActivityTestRule =
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index a9e30d6d..dfd710b8 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -84,6 +84,7 @@
 #include "chrome/browser/prefetch/no_state_prefetch/chrome_no_state_prefetch_contents_delegate.h"
 #include "chrome/browser/prefetch/no_state_prefetch/no_state_prefetch_manager_factory.h"
 #include "chrome/browser/prefetch/no_state_prefetch/no_state_prefetch_navigation_throttle.h"
+#include "chrome/browser/prefetch/prefetch_prefs.h"
 #include "chrome/browser/prefetch/prefetch_proxy/chrome_speculation_host_delegate.h"
 #include "chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_features.h"
 #include "chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_service.h"
@@ -2404,7 +2405,6 @@
       switches::kEnableNaCl,
 #if BUILDFLAG(ENABLE_NACL)
       switches::kEnableNaClDebug,
-      switches::kEnableNaClNonSfiMode,
 #endif
       switches::kEnableNetBenchmarking,
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -2445,9 +2445,7 @@
 #if BUILDFLAG(ENABLE_NACL)
     static const char* const kSwitchNames[] = {
         switches::kEnableNaClDebug,
-        switches::kEnableNaClNonSfiMode,
         switches::kForcePNaClSubzero,
-        switches::kNaClDangerousNoSandboxNonSfi,
     };
 
     command_line->CopySwitchesFrom(browser_command_line, kSwitchNames,
@@ -6351,6 +6349,12 @@
 #endif
 }
 
+bool ChromeContentBrowserClient::ShouldPreconnectNavigation(
+    content::BrowserContext* browser_context) {
+  return prefetch::IsSomePreloadingEnabled(
+      *Profile::FromBrowserContext(browser_context)->GetPrefs());
+}
+
 ChromeContentBrowserClient::UserAgentReductionEnterprisePolicyState
 ChromeContentBrowserClient::GetUserAgentReductionEnterprisePolicyState(
     content::BrowserContext* context) {
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index a76209e..ee247be 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -768,6 +768,8 @@
   bool IsFindInPageDisabledForOrigin(const url::Origin& origin) override;
 
   void FlushBackgroundAttributions(base::OnceClosure callback) override;
+  bool ShouldPreconnectNavigation(
+      content::BrowserContext* browser_context) override;
 
   enum UserAgentReductionEnterprisePolicyState {
     kDefault = 0,
diff --git a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifier.java b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifier.java
index 35304550..2ff303b 100644
--- a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifier.java
+++ b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifier.java
@@ -166,6 +166,8 @@
         notificationBuilder.setContentText(notificationData.text);
         notificationBuilder.setContentIntent(createContentIntent(notificationData.destinationUrl));
         notificationBuilder.setSmallIcon(R.drawable.ic_chrome);
+        notificationBuilder.setTimeoutAfter(
+                PriceTrackingNotificationConfig.getNotificationTimeoutMs());
         if (notificationData.actions != null) {
             for (ActionData action : notificationData.actions) {
                 PendingIntentProvider actionClickIntentProvider = createClickIntent(
diff --git a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationConfig.java b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationConfig.java
new file mode 100644
index 0000000..90aaabd
--- /dev/null
+++ b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationConfig.java
@@ -0,0 +1,26 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.price_tracking;
+
+import org.chromium.base.FeatureList;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+
+import java.util.concurrent.TimeUnit;
+
+/** Flag configuration for Price Tracking Notification experience. */
+public class PriceTrackingNotificationConfig {
+    private static final String NOTIFICATION_TIMEOUT_PARAM = "notification_timeout_ms";
+
+    // Gets the timeout of the price drop notification.
+    public static int getNotificationTimeoutMs() {
+        int defaultTimeout = (int) TimeUnit.HOURS.toMillis(3);
+        if (FeatureList.isInitialized()) {
+            return ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
+                    ChromeFeatureList.COMMERCE_PRICE_TRACKING, NOTIFICATION_TIMEOUT_PARAM,
+                    defaultTimeout);
+        }
+        return defaultTimeout;
+    }
+}
\ No newline at end of file
diff --git a/chrome/browser/commerce/price_tracking/android/java_sources.gni b/chrome/browser/commerce/price_tracking/android/java_sources.gni
index 5f3fb55..84aeccf 100644
--- a/chrome/browser/commerce/price_tracking/android/java_sources.gni
+++ b/chrome/browser/commerce/price_tracking/android/java_sources.gni
@@ -6,4 +6,5 @@
   "//chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotificationManager.java",
   "//chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifier.java",
   "//chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridge.java",
+  "//chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationConfig.java",
 ]
diff --git a/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifierUnitTest.java b/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifierUnitTest.java
index 91019a5..e3dcb517 100644
--- a/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifierUnitTest.java
+++ b/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifierUnitTest.java
@@ -5,6 +5,8 @@
 package org.chromium.chrome.browser.price_tracking;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.times;
@@ -183,11 +185,20 @@
                 .create();
     }
 
+    private void verifySetNotificationProperties() {
+        verify(mNotificationBuilder, times(1)).setContentTitle(eq(TITLE));
+        verify(mNotificationBuilder, times(1)).setContentText(eq(TEXT));
+        verify(mNotificationBuilder, times(1)).setContentIntent(any(PendingIntentProvider.class));
+        verify(mNotificationBuilder, times(1)).setSmallIcon(anyInt());
+        verify(mNotificationBuilder, times(1)).setTimeoutAfter(anyLong());
+    }
+
     @Test
     public void testShowNotificationImageFetcherFailure() {
         showNotification(/*actionDataList=*/null);
         invokeImageFetcherCallback(null);
         verify(mNotificationBuilder, times(0)).setLargeIcon(any());
+        verifySetNotificationProperties();
         verify(mNotificationManagerProxy).notify(any());
     }
 
@@ -198,6 +209,7 @@
         mPriceDropNotifier.showNotification(data);
         verify(mNotificationBuilder, times(0)).setLargeIcon(any());
         verify(mNotificationBuilder, times(0)).setBigPictureStyle(any(), any());
+        verifySetNotificationProperties();
         verify(mNotificationManagerProxy).notify(any());
     }
 
@@ -207,6 +219,7 @@
         invokeImageFetcherCallback(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888));
         verify(mNotificationBuilder).setLargeIcon(any());
         verify(mNotificationBuilder).setBigPictureStyle(any(), eq(TEXT));
+        verifySetNotificationProperties();
         verify(mNotificationManagerProxy).notify(any());
     }
 
diff --git a/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/LightweightReactionsCoordinatorImpl.java b/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/LightweightReactionsCoordinatorImpl.java
index b19bf10..87d4ddb 100644
--- a/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/LightweightReactionsCoordinatorImpl.java
+++ b/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/LightweightReactionsCoordinatorImpl.java
@@ -113,7 +113,7 @@
      *                   {@code mAvailableReactions}.
      */
     private void onAssetsFetched(Bitmap[] thumbnails) {
-        boolean success = thumbnails != null && thumbnails.length == mAvailableReactions.size();
+        boolean success = thumbnails != null;
         LightweightReactionsMetrics.recordAssetsFetched(
                 success, System.currentTimeMillis() - mAssetFetchStartTime);
         if (success) {
diff --git a/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/LightweightReactionsMediator.java b/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/LightweightReactionsMediator.java
index 983ab24..041a161 100644
--- a/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/LightweightReactionsMediator.java
+++ b/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/LightweightReactionsMediator.java
@@ -53,6 +53,7 @@
 
     private final ImageFetcher mImageFetcher;
 
+    private boolean mAssetFetchCancelled;
     private boolean mGifGenerationCancelled;
     private int mFramesGenerated;
 
@@ -97,6 +98,8 @@
             return;
         }
 
+        mAssetFetchCancelled = false;
+
         // Keep track of the number of callbacks received (two per reaction expected). Need a
         // final instance because the counter is updated from within a callback.
         final Counter counter = new Counter(reactions.size() * 2);
@@ -111,6 +114,15 @@
 
             ReactionMetadata reaction = reactions.get(i);
             getBitmapForUrl(reaction.thumbnailUrl, bitmap -> {
+                if (mAssetFetchCancelled) {
+                    return;
+                }
+                if (bitmap == null) {
+                    mAssetFetchCancelled = true;
+                    callback.onResult(null);
+                    return;
+                }
+
                 thumbnails[index] = bitmap;
                 counter.increment();
 
@@ -118,7 +130,16 @@
                     callback.onResult(thumbnails);
                 }
             });
-            getGifForUrl(reaction.thumbnailUrl, gif -> {
+            getGifForUrl(reaction.assetUrl, gif -> {
+                if (mAssetFetchCancelled) {
+                    return;
+                }
+                if (gif == null) {
+                    mAssetFetchCancelled = true;
+                    callback.onResult(null);
+                    return;
+                }
+
                 counter.increment();
 
                 if (counter.isDone()) {
diff --git a/chrome/browser/devtools/protocol/cast_handler.cc b/chrome/browser/devtools/protocol/cast_handler.cc
index 35cc831..1f8c9104 100644
--- a/chrome/browser/devtools/protocol/cast_handler.cc
+++ b/chrome/browser/devtools/protocol/cast_handler.cc
@@ -49,9 +49,7 @@
   const std::vector<MediaRoute>& routes() const { return routes_; }
 
  private:
-  void OnRoutesUpdated(
-      const std::vector<MediaRoute>& routes,
-      const std::vector<MediaRoute::Id>& joinable_route_ids) override {
+  void OnRoutesUpdated(const std::vector<MediaRoute>& routes) override {
     routes_ = routes;
     update_callback_.Run();
   }
diff --git a/chrome/browser/devtools/protocol/cast_handler_unittest.cc b/chrome/browser/devtools/protocol/cast_handler_unittest.cc
index 67f8c1e..0cff2824 100644
--- a/chrome/browser/devtools/protocol/cast_handler_unittest.cc
+++ b/chrome/browser/devtools/protocol/cast_handler_unittest.cc
@@ -201,14 +201,14 @@
 
 TEST_F(CastHandlerTest, StopCasting) {
   sinks_observer_->OnSinksUpdated({sink1, sink2}, {});
-  routes_observer_->OnRoutesUpdated({Route1()}, {});
+  routes_observer_->OnRoutesUpdated({Route1()});
   EXPECT_CALL(*router_, TerminateRoute(kRouteId1));
   EXPECT_TRUE(handler_->StopCasting(kSinkName1).IsSuccess());
 }
 
 TEST_F(CastHandlerTest, StopCastingWithInvalidName) {
   sinks_observer_->OnSinksUpdated({sink1, sink2}, {});
-  routes_observer_->OnRoutesUpdated({Route1()}, {});
+  routes_observer_->OnRoutesUpdated({Route1()});
   // Attempting to stop casting to a sink without a route should fail.
   EXPECT_TRUE(handler_->StopCasting(kSinkName2).IsError());
 }
diff --git a/chrome/browser/enterprise/reporting/report_generator_desktop.cc b/chrome/browser/enterprise/reporting/report_generator_desktop.cc
index cdd312e..04bf1fd 100644
--- a/chrome/browser/enterprise/reporting/report_generator_desktop.cc
+++ b/chrome/browser/enterprise/reporting/report_generator_desktop.cc
@@ -26,11 +26,10 @@
 
 // TODO(crbug.com/1102047): Split up Chrome OS reporting code into its own
 // delegates, then move this method's implementation to ReportGeneratorChromeOS.
-void ReportGeneratorDesktop::SetAndroidAppInfos(
-    ReportGenerator::ReportRequest* basic_request) {
+void ReportGeneratorDesktop::SetAndroidAppInfos(ReportRequest* basic_request) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   DCHECK(basic_request);
-  basic_request->clear_android_app_infos();
+  basic_request->GetDeviceReportRequest().clear_android_app_infos();
 
   // Android application is only supported for primary profile.
   Profile* primary_profile =
@@ -51,8 +50,9 @@
 
   AndroidAppInfoGenerator generator;
   for (std::string app_id : prefs->GetAppIds()) {
-    basic_request->mutable_android_app_infos()->AddAllocated(
-        generator.Generate(prefs, app_id).release());
+    basic_request->GetDeviceReportRequest()
+        .mutable_android_app_infos()
+        ->AddAllocated(generator.Generate(prefs, app_id).release());
   }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 }
diff --git a/chrome/browser/enterprise/reporting/report_generator_desktop.h b/chrome/browser/enterprise/reporting/report_generator_desktop.h
index a27e0959..877e160 100644
--- a/chrome/browser/enterprise/reporting/report_generator_desktop.h
+++ b/chrome/browser/enterprise/reporting/report_generator_desktop.h
@@ -20,8 +20,7 @@
   ~ReportGeneratorDesktop() override = default;
 
   // ReportGenerator::Delegate implementation.
-  void SetAndroidAppInfos(
-      ReportGenerator::ReportRequest* basic_request) override;
+  void SetAndroidAppInfos(ReportRequest* basic_request) override;
 };
 
 }  // namespace enterprise_reporting
diff --git a/chrome/browser/enterprise/reporting/report_generator_unittest.cc b/chrome/browser/enterprise/reporting/report_generator_unittest.cc
index 27170ca..7a002a8 100644
--- a/chrome/browser/enterprise/reporting/report_generator_unittest.cc
+++ b/chrome/browser/enterprise/reporting/report_generator_unittest.cc
@@ -20,7 +20,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/account_id/account_id.h"
-#include "components/enterprise/browser/reporting/report_request_definition.h"
+#include "components/enterprise/browser/reporting/report_request.h"
 #include "components/enterprise/browser/reporting/report_type.h"
 #include "components/policy/core/common/cloud/cloud_policy_util.h"
 #include "content/public/common/webplugininfo.h"
@@ -155,8 +155,6 @@
 
 class ReportGeneratorTest : public ::testing::Test {
  public:
-  using ReportRequest = definition::ReportRequest;
-
   ReportGeneratorTest()
       : generator_(&delegate_factory_),
         profile_manager_(TestingBrowserProcess::GetGlobal()) {}
@@ -233,16 +231,15 @@
     histogram_tester_ = std::make_unique<base::HistogramTester>();
     base::RunLoop run_loop;
     std::vector<std::unique_ptr<ReportRequest>> rets;
-    generator_.Generate(
-        report_type,
-        base::BindLambdaForTesting(
-            [&run_loop, &rets](ReportGenerator::ReportRequests requests) {
-              while (!requests.empty()) {
-                rets.push_back(std::move(requests.front()));
-                requests.pop();
-              }
-              run_loop.Quit();
-            }));
+    generator_.Generate(report_type,
+                        base::BindLambdaForTesting(
+                            [&run_loop, &rets](ReportRequestQueue requests) {
+                              while (!requests.empty()) {
+                                rets.push_back(std::move(requests.front()));
+                                requests.pop();
+                              }
+                              run_loop.Quit();
+                            }));
     run_loop.Run();
     if (report_type == ReportType::kFull)
       VerifyMetrics(rets);  // Only generated for reports with profiles.
@@ -331,27 +328,32 @@
   // Verify the basic request
   auto* basic_request = requests[0].get();
 
-  EXPECT_NE(std::string(), basic_request->brand_name());
-  EXPECT_NE(std::string(), basic_request->device_model());
-  VerifySerialNumber(basic_request->serial_number());
+  EXPECT_NE(std::string(),
+            basic_request->GetDeviceReportRequest().brand_name());
+  EXPECT_NE(std::string(),
+            basic_request->GetDeviceReportRequest().device_model());
+  VerifySerialNumber(basic_request->GetDeviceReportRequest().serial_number());
 
-  EXPECT_EQ(
-      policy::GetBrowserDeviceIdentifier()->SerializePartialAsString(),
-      basic_request->browser_device_identifier().SerializePartialAsString());
+  EXPECT_EQ(policy::GetBrowserDeviceIdentifier()->SerializePartialAsString(),
+            basic_request->GetDeviceReportRequest()
+                .browser_device_identifier()
+                .SerializePartialAsString());
 
   // Verify the OS report
-  EXPECT_TRUE(basic_request->has_os_report());
-  auto& os_report = basic_request->os_report();
+  EXPECT_TRUE(basic_request->GetDeviceReportRequest().has_os_report());
+  auto& os_report = basic_request->GetDeviceReportRequest().os_report();
   EXPECT_NE(std::string(), os_report.name());
   EXPECT_NE(std::string(), os_report.arch());
   EXPECT_NE(std::string(), os_report.version());
 
   // Ensure there are no partial reports
-  EXPECT_EQ(0, basic_request->partial_report_types_size());
+  EXPECT_EQ(
+      0, basic_request->GetDeviceReportRequest().partial_report_types_size());
 
   // Verify the browser report
-  EXPECT_TRUE(basic_request->has_browser_report());
-  auto& browser_report = basic_request->browser_report();
+  EXPECT_TRUE(basic_request->GetDeviceReportRequest().has_browser_report());
+  auto& browser_report =
+      basic_request->GetDeviceReportRequest().browser_report();
   EXPECT_NE(std::string(), browser_report.browser_version());
   EXPECT_TRUE(browser_report.has_channel());
   EXPECT_NE(std::string(), browser_report.executable_path());
@@ -371,24 +373,29 @@
   // In the ChromeOsUserReportRequest for Chrome OS, these fields are not
   // existing. Therefore, they are skipped according to current environment.
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
-  EXPECT_NE(std::string(), basic_request->computer_name());
-  EXPECT_NE(std::string(), basic_request->os_user_name());
-  VerifySerialNumber(basic_request->serial_number());
-  EXPECT_EQ(
-      policy::GetBrowserDeviceIdentifier()->SerializePartialAsString(),
-      basic_request->browser_device_identifier().SerializePartialAsString());
+  EXPECT_NE(std::string(),
+            basic_request->GetDeviceReportRequest().computer_name());
+  EXPECT_NE(std::string(),
+            basic_request->GetDeviceReportRequest().os_user_name());
+  VerifySerialNumber(basic_request->GetDeviceReportRequest().serial_number());
+  EXPECT_EQ(policy::GetBrowserDeviceIdentifier()->SerializePartialAsString(),
+            basic_request->GetDeviceReportRequest()
+                .browser_device_identifier()
+                .SerializePartialAsString());
 
-  EXPECT_TRUE(basic_request->has_os_report());
-  auto& os_report = basic_request->os_report();
+  EXPECT_TRUE(basic_request->GetDeviceReportRequest().has_os_report());
+  auto& os_report = basic_request->GetDeviceReportRequest().os_report();
   EXPECT_NE(std::string(), os_report.name());
   EXPECT_NE(std::string(), os_report.arch());
   EXPECT_NE(std::string(), os_report.version());
 #endif
 
-  EXPECT_EQ(0, basic_request->partial_report_types_size());
+  EXPECT_EQ(
+      0, basic_request->GetDeviceReportRequest().partial_report_types_size());
 
-  EXPECT_TRUE(basic_request->has_browser_report());
-  auto& browser_report = basic_request->browser_report();
+  EXPECT_TRUE(basic_request->GetDeviceReportRequest().has_browser_report());
+  auto& browser_report =
+      basic_request->GetDeviceReportRequest().browser_report();
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   EXPECT_FALSE(browser_report.has_browser_version());
   EXPECT_FALSE(browser_report.has_channel());
@@ -426,19 +433,22 @@
   // In the ChromeOsUserReportRequest for Chrome OS, these fields are not
   // existing. Therefore, they are skipped according to current environment.
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
-  EXPECT_NE(std::string(), basic_request->computer_name());
-  EXPECT_NE(std::string(), basic_request->os_user_name());
-  VerifySerialNumber(basic_request->serial_number());
+  EXPECT_NE(std::string(),
+            basic_request->GetDeviceReportRequest().computer_name());
+  EXPECT_NE(std::string(),
+            basic_request->GetDeviceReportRequest().os_user_name());
+  VerifySerialNumber(basic_request->GetDeviceReportRequest().serial_number());
 
-  EXPECT_TRUE(basic_request->has_os_report());
-  auto& os_report = basic_request->os_report();
+  EXPECT_TRUE(basic_request->GetDeviceReportRequest().has_os_report());
+  auto& os_report = basic_request->GetDeviceReportRequest().os_report();
   EXPECT_NE(std::string(), os_report.name());
   EXPECT_NE(std::string(), os_report.arch());
   EXPECT_NE(std::string(), os_report.version());
 #endif
 
-  EXPECT_TRUE(basic_request->has_browser_report());
-  auto& browser_report = basic_request->browser_report();
+  EXPECT_TRUE(basic_request->GetDeviceReportRequest().has_browser_report());
+  auto& browser_report =
+      basic_request->GetDeviceReportRequest().browser_report();
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   EXPECT_FALSE(browser_report.has_browser_version());
   EXPECT_FALSE(browser_report.has_channel());
@@ -487,10 +497,12 @@
   EXPECT_EQ(1u, requests.size());
 
   ReportRequest* request = requests.front().get();
-  EXPECT_EQ(2, request->android_app_infos_size());
-  em::AndroidAppInfo app_info1 = request->android_app_infos(1);
+  EXPECT_EQ(2, request->GetDeviceReportRequest().android_app_infos_size());
+  em::AndroidAppInfo app_info1 =
+      request->GetDeviceReportRequest().android_app_infos(1);
   EXPECT_EQ(kArcAppName1, app_info1.app_name());
-  em::AndroidAppInfo app_info2 = request->android_app_infos(0);
+  em::AndroidAppInfo app_info2 =
+      request->GetDeviceReportRequest().android_app_infos(0);
   EXPECT_EQ(kArcAppName2, app_info2.app_name());
 
   // Generate the Arc application information again and make sure the report
@@ -499,10 +511,10 @@
   EXPECT_EQ(1u, requests.size());
 
   request = requests.front().get();
-  EXPECT_EQ(2, request->android_app_infos_size());
-  app_info1 = request->android_app_infos(1);
+  EXPECT_EQ(2, request->GetDeviceReportRequest().android_app_infos_size());
+  app_info1 = request->GetDeviceReportRequest().android_app_infos(1);
   EXPECT_EQ(kArcAppName1, app_info1.app_name());
-  app_info2 = request->android_app_infos(0);
+  app_info2 = request->GetDeviceReportRequest().android_app_infos(0);
   EXPECT_EQ(kArcAppName2, app_info2.app_name());
 
   arc_app_test.TearDown();
@@ -528,7 +540,7 @@
   EXPECT_EQ(1u, requests.size());
 
   ReportRequest* request = requests.front().get();
-  EXPECT_EQ(0, request->android_app_infos_size());
+  EXPECT_EQ(0, request->GetDeviceReportRequest().android_app_infos_size());
 
   arc_app_test.TearDown();
 }
diff --git a/chrome/browser/enterprise/reporting/report_request_queue_generator_unittest.cc b/chrome/browser/enterprise/reporting/report_request_queue_generator_unittest.cc
index 673bf2d..801dd96 100644
--- a/chrome/browser/enterprise/reporting/report_request_queue_generator_unittest.cc
+++ b/chrome/browser/enterprise/reporting/report_request_queue_generator_unittest.cc
@@ -19,7 +19,7 @@
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/account_id/account_id.h"
 #include "components/enterprise/browser/reporting/browser_report_generator.h"
-#include "components/enterprise/browser/reporting/report_request_definition.h"
+#include "components/enterprise/browser/reporting/report_request.h"
 #include "components/policy/core/common/mock_policy_service.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/sync_preferences/pref_service_syncable.h"
@@ -54,8 +54,6 @@
 // move this file to components/enterprise/browser.
 class ReportRequestQueueGeneratorTest : public ::testing::Test {
  public:
-  using ReportRequest = definition::ReportRequest;
-
   ReportRequestQueueGeneratorTest()
       : profile_manager_(TestingBrowserProcess::GetGlobal()),
         browser_report_generator_(&reporting_delegate_factory_),
@@ -138,14 +136,15 @@
 #endif  // !defined(OS_ANDROID)
 
   std::unique_ptr<ReportRequest> GenerateBasicRequest() {
-    auto request = std::make_unique<ReportRequest>();
+    auto request = std::make_unique<ReportRequest>(ReportType::kFull);
     base::RunLoop run_loop;
 
     browser_report_generator_.Generate(
         ReportType::kFull,
         base::BindLambdaForTesting(
             [&run_loop, &request](std::unique_ptr<em::BrowserReport> report) {
-              request->set_allocated_browser_report(report.release()),
+              request->GetDeviceReportRequest().set_allocated_browser_report(
+                  report.release()),
                   run_loop.Quit();
             }));
 
@@ -244,7 +243,8 @@
   auto requests = GenerateRequests(*basic_request);
   EXPECT_EQ(1u, requests.size());
 
-  VerifyProfiles(requests[0]->browser_report(), /*idle profiles*/ {},
+  VerifyProfiles(requests[0]->GetDeviceReportRequest().browser_report(),
+                 /*idle profiles*/ {},
                  /*active profiles*/ {kActiveProfileName1});
   histogram_tester()->ExpectBucketCount("Enterprise.CloudReportingRequestSize",
                                         /*report size floor to KB*/ 0, 1);
@@ -287,7 +287,7 @@
   auto requests = GenerateRequests(*basic_request);
   EXPECT_EQ(1u, requests.size());
 
-  auto browser_report = requests[0]->browser_report();
+  auto browser_report = requests[0]->GetDeviceReportRequest().browser_report();
   EXPECT_EQ(1, browser_report.chrome_user_profile_infos_size());
 
   auto profile_info = browser_report.chrome_user_profile_infos(0);
@@ -312,7 +312,8 @@
   auto requests = GenerateRequests(*basic_request);
   EXPECT_EQ(1u, requests.size());
 
-  VerifyProfiles(requests[0]->browser_report(), idle_profile_names, {});
+  VerifyProfiles(requests[0]->GetDeviceReportRequest().browser_report(),
+                 idle_profile_names, {});
   histogram_tester()->ExpectBucketCount("Enterprise.CloudReportingRequestSize",
                                         /*report size floor to KB*/ 0, 1);
 }
@@ -324,8 +325,8 @@
   auto requests = GenerateRequests(*basic_request);
   EXPECT_EQ(1u, requests.size());
 
-  VerifyProfiles(requests[0]->browser_report(), idle_profile_names,
-                 active_profile_names);
+  VerifyProfiles(requests[0]->GetDeviceReportRequest().browser_report(),
+                 idle_profile_names, active_profile_names);
   histogram_tester()->ExpectBucketCount("Enterprise.CloudReportingRequestSize",
                                         /*report size floor to KB*/ 0, 1);
 }
@@ -338,7 +339,8 @@
 
   // Set the limitation just below the size of the report so that it needs to be
   // separated into two requests later.
-  SetAndVerifyMaximumRequestSize(requests[0]->ByteSizeLong() - 30);
+  SetAndVerifyMaximumRequestSize(
+      requests[0]->GetDeviceReportRequest().ByteSizeLong() - 30);
   requests = GenerateRequests(*basic_request);
   EXPECT_EQ(2u, requests.size());
 
@@ -356,11 +358,11 @@
   // The first profile is activated in the first request only while the second
   // profile is activated in the second request.
   VerifyProfiles(
-      requests[0]->browser_report(),
+      requests[0]->GetDeviceReportRequest().browser_report(),
       {/* idle_profile_names */ expected_active_profiles_in_requests[1]},
       {/* active_profile_names */ expected_active_profiles_in_requests[0]});
   VerifyProfiles(
-      requests[1]->browser_report(),
+      requests[1]->GetDeviceReportRequest().browser_report(),
       {/* idle_profile_names */ expected_active_profiles_in_requests[0]},
       {/* active_profile_names */ expected_active_profiles_in_requests[1]});
   histogram_tester()->ExpectBucketCount("Enterprise.CloudReportingRequestSize",
@@ -374,7 +376,8 @@
   EXPECT_EQ(1u, requests.size());
 
   // Set the limitation just below the size of the report.
-  SetAndVerifyMaximumRequestSize(requests[0]->ByteSizeLong() - 30);
+  SetAndVerifyMaximumRequestSize(
+      requests[0]->GetDeviceReportRequest().ByteSizeLong() - 30);
 
   // Add a smaller Profile.
   CreateActiveProfile(kActiveProfileName2);
@@ -384,8 +387,8 @@
 
   // Only the second Profile is activated while the first one is too big to be
   // reported.
-  VerifyProfiles(requests[0]->browser_report(), {kActiveProfileName1},
-                 {kActiveProfileName2});
+  VerifyProfiles(requests[0]->GetDeviceReportRequest().browser_report(),
+                 {kActiveProfileName1}, {kActiveProfileName2});
   histogram_tester()->ExpectBucketCount("Enterprise.CloudReportingRequestSize",
                                         /*report size floor to KB*/ 0, 2);
 }
diff --git a/chrome/browser/enterprise/reporting/report_scheduler_unittest.cc b/chrome/browser/enterprise/reporting/report_scheduler_unittest.cc
index 455a04d3..e872580 100644
--- a/chrome/browser/enterprise/reporting/report_scheduler_unittest.cc
+++ b/chrome/browser/enterprise/reporting/report_scheduler_unittest.cc
@@ -27,6 +27,7 @@
 #include "components/enterprise/browser/reporting/real_time_report_generator.h"
 #include "components/enterprise/browser/reporting/real_time_uploader.h"
 #include "components/enterprise/browser/reporting/report_generator.h"
+#include "components/enterprise/browser/reporting/report_request.h"
 #include "components/enterprise/common/proto/extensions_workflow_events.pb.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
 #include "components/reporting/client/report_queue_provider.h"
@@ -69,9 +70,9 @@
 }  // namespace
 
 ACTION_P(ScheduleGeneratorCallback, request_number) {
-  ReportGenerator::ReportRequests requests;
+  ReportRequestQueue requests;
   for (int i = 0; i < request_number; i++)
-    requests.push(std::make_unique<ReportGenerator::ReportRequest>());
+    requests.push(std::make_unique<ReportRequest>(ReportType::kFull));
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(arg0), std::move(requests)));
 }
@@ -92,7 +93,7 @@
   }
   MOCK_METHOD2(OnGenerate,
                void(ReportType report_type, ReportCallback& callback));
-  MOCK_METHOD0(GenerateBasic, ReportRequests());
+  MOCK_METHOD0(GenerateBasic, ReportRequestQueue());
 };
 
 class MockReportUploader : public ReportUploader {
@@ -103,7 +104,7 @@
   MockReportUploader& operator=(const MockReportUploader&) = delete;
 
   ~MockReportUploader() override = default;
-  MOCK_METHOD2(SetRequestAndUpload, void(ReportRequests, ReportCallback));
+  MOCK_METHOD2(SetRequestAndUpload, void(ReportRequestQueue, ReportCallback));
 };
 
 class MockRealTimeReportGenerator : public RealTimeReportGenerator {
@@ -225,10 +226,10 @@
     }
   }
 
-  ReportGenerator::ReportRequests CreateRequests(int number) {
-    ReportGenerator::ReportRequests requests;
+  ReportRequestQueue CreateRequests(int number) {
+    ReportRequestQueue requests;
     for (int i = 0; i < number; i++)
-      requests.push(std::make_unique<ReportGenerator::ReportRequest>());
+      requests.push(std::make_unique<ReportRequest>(ReportType::kFull));
     return requests;
   }
 
@@ -559,7 +560,7 @@
   // Hang on to the uploader's ReportCallback.
   ReportUploader::ReportCallback saved_callback;
   EXPECT_CALL(*uploader_, SetRequestAndUpload(_, _))
-      .WillOnce([&saved_callback](ReportUploader::ReportRequests requests,
+      .WillOnce([&saved_callback](ReportRequestQueue requests,
                                   ReportUploader::ReportCallback callback) {
         saved_callback = std::move(callback);
       });
diff --git a/chrome/browser/extensions/api/debugger/debugger_api.cc b/chrome/browser/extensions/api/debugger/debugger_api.cc
index 96f7b8b5..73e19fb18 100644
--- a/chrome/browser/extensions/api/debugger/debugger_api.cc
+++ b/chrome/browser/extensions/api/debugger/debugger_api.cc
@@ -309,11 +309,11 @@
     DebuggerSendCommandFunction* function,
     const std::string& method,
     SendCommand::Params::CommandParams* command_params) {
-  base::DictionaryValue protocol_request;
+  base::Value protocol_request(base::Value::Type::DICTIONARY);
   int request_id = ++last_request_id_;
   pending_requests_[request_id] = function;
-  protocol_request.SetInteger("id", request_id);
-  protocol_request.SetString("method", method);
+  protocol_request.SetIntKey("id", request_id);
+  protocol_request.SetStringKey("method", method);
   if (command_params) {
     protocol_request.SetKey("params",
                             command_params->additional_properties.Clone());
@@ -376,27 +376,27 @@
 
   base::StringPiece message_str(reinterpret_cast<const char*>(message.data()),
                                 message.size());
-  std::unique_ptr<base::Value> result = base::JSONReader::ReadDeprecated(
+  absl::optional<base::Value> result = base::JSONReader::Read(
       message_str, base::JSON_REPLACE_INVALID_CHARACTERS);
   if (!result || !result->is_dict()) {
     LOG(ERROR) << "Tried to send invalid message to extension: " << message_str;
     return;
   }
-  base::DictionaryValue* dictionary =
-      static_cast<base::DictionaryValue*>(result.get());
+  base::Value dictionary = std::move(result.value());
 
-  absl::optional<int> id = dictionary->FindIntKey("id");
+  absl::optional<int> id = dictionary.FindIntKey("id");
   if (!id) {
-    std::string method_name;
-    if (!dictionary->GetString("method", &method_name))
+    std::string* method_name = dictionary.FindStringKey("method");
+    if (!method_name)
       return;
 
     OnEvent::Params params;
-    base::DictionaryValue* params_value;
-    if (dictionary->GetDictionary("params", &params_value))
-      params.additional_properties.Swap(params_value);
+    if (base::Value* params_value = dictionary.FindDictKey("params")) {
+      params.additional_properties.Swap(
+          static_cast<base::DictionaryValue*>(params_value));
+    }
 
-    auto args(OnEvent::Create(debuggee_, method_name, params));
+    auto args(OnEvent::Create(debuggee_, *method_name, params));
     auto event =
         std::make_unique<Event>(events::DEBUGGER_ON_EVENT, OnEvent::kEventName,
                                 std::move(args), profile_);
@@ -407,7 +407,7 @@
     if (it == pending_requests_.end())
       return;
 
-    it->second->SendResponseBody(dictionary);
+    it->second->SendResponseBody(std::move(dictionary));
     pending_requests_.erase(it);
   }
 }
@@ -654,20 +654,19 @@
   return RespondLater();
 }
 
-void DebuggerSendCommandFunction::SendResponseBody(
-    base::DictionaryValue* response) {
-  base::Value* error_body;
-  if (response->Get("error", &error_body)) {
+void DebuggerSendCommandFunction::SendResponseBody(base::Value response) {
+  if (base::Value* error_body = response.FindKey("error")) {
     std::string error;
     base::JSONWriter::Write(*error_body, &error);
     Respond(Error(std::move(error)));
     return;
   }
 
-  base::DictionaryValue* result_body;
   SendCommand::Results::Result result;
-  if (response->GetDictionary("result", &result_body))
-    result.additional_properties.Swap(result_body);
+  if (base::Value* result_body = response.FindDictKey("result")) {
+    result.additional_properties.Swap(
+        static_cast<base::DictionaryValue*>(result_body));
+  }
 
   Respond(ArgumentList(SendCommand::Results::Create(result)));
 }
@@ -693,35 +692,33 @@
 const char kTargetTypeWorker[] = "worker";
 const char kTargetTypeOther[] = "other";
 
-std::unique_ptr<base::DictionaryValue> SerializeTarget(
-    scoped_refptr<DevToolsAgentHost> host) {
-  std::unique_ptr<base::DictionaryValue> dictionary(
-      new base::DictionaryValue());
-  dictionary->SetString(kTargetIdField, host->GetId());
-  dictionary->SetString(kTargetTitleField, host->GetTitle());
-  dictionary->SetBoolean(kTargetAttachedField, host->IsAttached());
-  dictionary->SetString(kTargetUrlField, host->GetURL().spec());
+base::Value SerializeTarget(scoped_refptr<DevToolsAgentHost> host) {
+  base::Value dictionary(base::Value::Type::DICTIONARY);
+  dictionary.SetStringKey(kTargetIdField, host->GetId());
+  dictionary.SetStringKey(kTargetTitleField, host->GetTitle());
+  dictionary.SetBoolKey(kTargetAttachedField, host->IsAttached());
+  dictionary.SetStringKey(kTargetUrlField, host->GetURL().spec());
 
   std::string type = host->GetType();
   std::string target_type = kTargetTypeOther;
   if (type == DevToolsAgentHost::kTypePage) {
     int tab_id =
         extensions::ExtensionTabUtil::GetTabId(host->GetWebContents());
-    dictionary->SetInteger(kTargetTabIdField, tab_id);
+    dictionary.SetIntKey(kTargetTabIdField, tab_id);
     target_type = kTargetTypePage;
   } else if (type == ChromeDevToolsManagerDelegate::kTypeBackgroundPage) {
-    dictionary->SetString(kTargetExtensionIdField, host->GetURL().host());
+    dictionary.SetStringKey(kTargetExtensionIdField, host->GetURL().host());
     target_type = kTargetTypeBackgroundPage;
   } else if (type == DevToolsAgentHost::kTypeServiceWorker ||
              type == DevToolsAgentHost::kTypeSharedWorker) {
     target_type = kTargetTypeWorker;
   }
 
-  dictionary->SetString(kTargetTypeField, target_type);
+  dictionary.SetStringKey(kTargetTypeField, target_type);
 
   GURL favicon_url = host->GetFaviconURL();
   if (favicon_url.is_valid())
-    dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec());
+    dictionary.SetStringKey(kTargetFaviconUrlField, favicon_url.spec());
 
   return dictionary;
 }
@@ -735,8 +732,8 @@
 ExtensionFunction::ResponseAction DebuggerGetTargetsFunction::Run() {
   content::DevToolsAgentHost::List list = DevToolsAgentHost::GetOrCreateAll();
   std::unique_ptr<base::ListValue> result(new base::ListValue());
-  for (size_t i = 0; i < list.size(); ++i)
-    result->Append(SerializeTarget(list[i]));
+  for (auto& i : list)
+    result->Append(SerializeTarget(i));
 
   return RespondNow(
       OneArgument(base::Value::FromUniquePtrValue(std::move(result))));
diff --git a/chrome/browser/extensions/api/debugger/debugger_api.h b/chrome/browser/extensions/api/debugger/debugger_api.h
index e38d5a8a..9a4f5af 100644
--- a/chrome/browser/extensions/api/debugger/debugger_api.h
+++ b/chrome/browser/extensions/api/debugger/debugger_api.h
@@ -20,10 +20,6 @@
 
 // Base debugger function.
 
-namespace base {
-class DictionaryValue;
-}
-
 namespace extensions {
 class ExtensionDevToolsClientHost;
 
@@ -77,7 +73,7 @@
   DECLARE_EXTENSION_FUNCTION("debugger.sendCommand", DEBUGGER_SENDCOMMAND)
 
   DebuggerSendCommandFunction();
-  void SendResponseBody(base::DictionaryValue* result);
+  void SendResponseBody(base::Value result);
   void SendDetachedError();
 
  protected:
diff --git a/chrome/browser/extensions/extension_messages_apitest.cc b/chrome/browser/extensions/extension_messages_apitest.cc
index e219dd3..2fc7226 100644
--- a/chrome/browser/extensions/extension_messages_apitest.cc
+++ b/chrome/browser/extensions/extension_messages_apitest.cc
@@ -80,9 +80,9 @@
   static std::unique_ptr<base::ListValue> BuildEventArguments(
       const bool last_message,
       const std::string& data) {
-    std::unique_ptr<base::DictionaryValue> event(new base::DictionaryValue());
-    event->SetBoolean("lastMessage", last_message);
-    event->SetString("data", data);
+    base::Value event(base::Value::Type::DICTIONARY);
+    event.SetBoolKey("lastMessage", last_message);
+    event.SetStringKey("data", data);
     std::unique_ptr<base::ListValue> arguments(new base::ListValue());
     arguments->Append(std::move(event));
     return arguments;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 6c834ec..82af9b0d 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3127,12 +3127,12 @@
   {
     "name": "file-handling-api",
     "owners": [ "estade", "mgiuca", "cmp" ],
-    "expiry_milestone": 98
+    "expiry_milestone": 104
   },
   {
     "name": "file-handling-icons",
     "owners": [ "estade", "mgiuca", "cmp" ],
-    "expiry_milestone": 98
+    "expiry_milestone": 104
   },
   {
     "name": "files-archivemount",
@@ -5523,6 +5523,11 @@
     "expiry_milestone": -1
   },
   {
+    "name": "use-multiple-overlays",
+    "owners": [ "khaslett", "//components/viz/OWNERS" ],
+    "expiry_milestone": 104
+  },
+  {
     "name": "use-passthrough-command-decoder",
     "owners": [ "//third_party/angle/OWNERS" ],
     "expiry_milestone": 100
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 27e102d..7575410 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2229,14 +2229,6 @@
     "Enables taking"
     " screenshots from the desktop sharing hub.";
 
-const char kSharingHubDesktopAppMenuName[] = "Desktop Sharing Hub in App Menu";
-const char kSharingHubDesktopAppMenuDescription[] =
-    "Enables the Chrome Sharing Hub in the 3-dot menu for desktop.";
-
-const char kSharingHubDesktopOmniboxName[] = "Desktop Sharing Hub in Omnibox";
-const char kSharingHubDesktopOmniboxDescription[] =
-    "Enables the Chrome Sharing Hub in the omnibox for desktop.";
-
 const char kSharingPreferVapidName[] =
     "Prefer sending Sharing message via VAPID";
 const char kSharingPreferVapidDescription[] =
@@ -5148,6 +5140,11 @@
     "timestamp) instead of the system audio/video devices, for debugging "
     "purposes.";
 
+const char kUseMultipleOverlaysName[] = "Use Multiple Overlays";
+const char kUseMultipleOverlaysDescription[] =
+    "Specifies the maximum number of quads that Chrome will attempt to promote"
+    " to overlays.";
+
 const char kUiDevToolsName[] = "Enable native UI inspection";
 const char kUiDevToolsDescription[] =
     "Enables inspection of native UI elements. For local inspection use "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 5e7289a..393c389 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1285,12 +1285,6 @@
 extern const char kSharingDesktopScreenshotsEditName[];
 extern const char kSharingDesktopScreenshotsEditDescription[];
 
-extern const char kSharingHubDesktopAppMenuName[];
-extern const char kSharingHubDesktopAppMenuDescription[];
-
-extern const char kSharingHubDesktopOmniboxName[];
-extern const char kSharingHubDesktopOmniboxDescription[];
-
 extern const char kSharingPreferVapidName[];
 extern const char kSharingPreferVapidDescription[];
 
@@ -2981,6 +2975,9 @@
 extern const char kUseFakeDeviceForMediaStreamName[];
 extern const char kUseFakeDeviceForMediaStreamDescription[];
 
+extern const char kUseMultipleOverlaysName[];
+extern const char kUseMultipleOverlaysDescription[];
+
 extern const char kVaapiJpegImageDecodeAccelerationName[];
 extern const char kVaapiJpegImageDecodeAccelerationDescription[];
 
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
index 70ec75c..c9d9912d 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
@@ -166,18 +166,10 @@
 
 void MediaRouterMojoImpl::OnRoutesUpdated(
     mojom::MediaRouteProviderId provider_id,
-    const std::vector<MediaRoute>& routes,
-    const std::string& media_source,
-    const std::vector<std::string>& joinable_route_ids) {
+    const std::vector<MediaRoute>& routes) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  auto it = routes_queries_.find(media_source);
-  if (it == routes_queries_.end() || !it->second->HasObservers()) {
-    return;
-  }
-
-  auto* routes_query = it->second.get();
-  routes_query->SetRoutesForProvider(provider_id, routes, joinable_route_ids);
-  routes_query->NotifyObservers();
+  routes_query_.SetRoutesForProvider(provider_id, routes);
+  routes_query_.NotifyObservers();
 }
 
 void MediaRouterMojoImpl::RouteResponseReceived(
@@ -449,19 +441,9 @@
 
 void MediaRouterMojoImpl::MediaRoutesQuery::SetRoutesForProvider(
     mojom::MediaRouteProviderId provider_id,
-    const std::vector<MediaRoute>& routes,
-    const std::vector<MediaRoute::Id>& joinable_route_ids) {
+    const std::vector<MediaRoute>& routes) {
   providers_to_routes_[provider_id] = routes;
   UpdateCachedRouteList();
-
-  providers_to_joinable_routes_[provider_id] = joinable_route_ids;
-  joinable_route_ids_.clear();
-  for (const auto& provider_to_joinable_routes :
-       providers_to_joinable_routes_) {
-    joinable_route_ids_.insert(joinable_route_ids_.end(),
-                               provider_to_joinable_routes.second.begin(),
-                               provider_to_joinable_routes.second.end());
-  }
 }
 
 bool MediaRouterMojoImpl::MediaRoutesQuery::AddRouteForProvider(
@@ -493,8 +475,7 @@
     MediaRoutesObserver* observer) {
   observers_.AddObserver(observer);
   observer->OnRoutesUpdated(
-      cached_route_list_.value_or(std::vector<MediaRoute>()),
-      joinable_route_ids_);
+      cached_route_list_.value_or(std::vector<MediaRoute>()));
 }
 
 void MediaRouterMojoImpl::MediaRoutesQuery::RemoveObserver(
@@ -505,8 +486,7 @@
 void MediaRouterMojoImpl::MediaRoutesQuery::NotifyObservers() {
   for (auto& observer : observers_) {
     observer.OnRoutesUpdated(
-        cached_route_list_.value_or(std::vector<MediaRoute>()),
-        joinable_route_ids_);
+        cached_route_list_.value_or(std::vector<MediaRoute>()));
   }
 }
 
@@ -576,77 +556,40 @@
 void MediaRouterMojoImpl::RegisterMediaRoutesObserver(
     MediaRoutesObserver* observer) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  const MediaSource::Id source_id = observer->source_id();
-  auto& routes_query = routes_queries_[source_id];
-  bool is_new_query = false;
-  if (!routes_query) {
-    is_new_query = true;
-    routes_query = std::make_unique<MediaRoutesQuery>();
-  } else {
-    DCHECK(!routes_query->HasObserver(observer));
-  }
+  bool is_first_observer = !routes_query_.HasObservers();
+  if (!is_first_observer)
+    DCHECK(!routes_query_.HasObserver(observer));
 
-  routes_query->AddObserver(observer);
-  if (is_new_query) {
-    for (const auto& provider : media_route_providers_)
-      provider.second->StartObservingMediaRoutes(source_id);
-    // The MRPs will call MediaRouterMojoImpl::OnRoutesUpdated() soon, if there
-    // are any existing routes the new observer should be aware of.
-  } else if (routes_query->cached_route_list()) {
+  routes_query_.AddObserver(observer);
+  if (is_first_observer) {
+    for (const auto& provider : media_route_providers_) {
+      provider.second->StartObservingMediaRoutes();
+      // The MRPs will call MediaRouterMojoImpl::OnRoutesUpdated() soon, if
+      // there are any existing routes the new observer should be aware of.
+    }
+  } else {
     // Return to the event loop before notifying of a cached route list because
     // MediaRoutesObserver is calling this method from its constructor, and that
     // must complete before invoking its virtual OnRoutesUpdated() method.
     content::GetUIThreadTaskRunner({})->PostTask(
         FROM_HERE,
         base::BindOnce(&MediaRouterMojoImpl::NotifyOfExistingRoutesIfRegistered,
-                       weak_factory_.GetWeakPtr(), source_id, observer));
+                       weak_factory_.GetWeakPtr(), observer));
   }
 }
 
 void MediaRouterMojoImpl::NotifyOfExistingRoutesIfRegistered(
-    const MediaSource::Id& source_id,
     MediaRoutesObserver* observer) const {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  // Check that the route query still exists with a cached result, and that the
-  // observer is still registered. Otherwise, there is nothing to report to the
-  // observer.
-  const auto it = routes_queries_.find(source_id);
-  if (it == routes_queries_.end() || !it->second->cached_route_list() ||
-      !it->second->HasObserver(observer)) {
-    return;
+  if (routes_query_.HasObserver(observer)) {
+    observer->OnRoutesUpdated(
+        routes_query_.cached_route_list().value_or(std::vector<MediaRoute>{}));
   }
-
-  observer->OnRoutesUpdated(*it->second->cached_route_list(),
-                            it->second->joinable_route_ids());
 }
 
 void MediaRouterMojoImpl::UnregisterMediaRoutesObserver(
     MediaRoutesObserver* observer) {
-  const MediaSource::Id source_id = observer->source_id();
-  auto it = routes_queries_.find(source_id);
-  if (it == routes_queries_.end() || !it->second->HasObserver(observer)) {
-    return;
-  }
-
-  // If we are removing the final observer for the source, then stop
-  // observing routes for it.
-  // HasObservers() is reliable here on the assumption that this call
-  // is not inside the ObserverList iteration.
-  it->second->RemoveObserver(observer);
-  if (!it->second->HasObservers()) {
-    for (const auto& provider : media_route_providers_) {
-      if (!provider.second) {
-        // The provider somehow not existing may be the cause of the crash at
-        // crbug.com/1200786.
-        NOTREACHED() << "Provider is null: "
-                     << ProviderIdToString(provider.first);
-        continue;
-      }
-      provider.second->StopObservingMediaRoutes(source_id);
-    }
-    routes_queries_.erase(source_id);
-  }
+  routes_query_.RemoveObserver(observer);
 }
 
 void MediaRouterMojoImpl::RegisterRouteMessageObserver(
@@ -741,16 +684,8 @@
 
 void MediaRouterMojoImpl::OnRouteAdded(mojom::MediaRouteProviderId provider_id,
                                        const MediaRoute& route) {
-  // |routes_queries_| might be added during the iteration. Making a
-  // copy here to avoid the iterator from being invalidated.
-  std::vector<MediaRoutesQuery*> queries;
-  for (auto& routes_query : routes_queries_) {
-    if (routes_query.second->AddRouteForProvider(provider_id, route))
-      queries.push_back(routes_query.second.get());
-  }
-  for (auto* query : queries) {
-    query->NotifyObservers();
-  }
+  routes_query_.AddRouteForProvider(provider_id, route);
+  routes_query_.NotifyObservers();
 }
 
 void MediaRouterMojoImpl::SyncStateToMediaRouteProvider(
@@ -763,9 +698,9 @@
     provider->StartObservingMediaSinks(it.first);
   }
 
-  // Route queries.
-  for (const auto& it : routes_queries_)
-    provider->StartObservingMediaRoutes(it.first);
+  // Route updates.
+  if (routes_query_.HasObservers())
+    provider->StartObservingMediaRoutes();
 
   // Route messages.
   for (const auto& it : message_observers_)
@@ -856,17 +791,14 @@
 
 absl::optional<mojom::MediaRouteProviderId>
 MediaRouterMojoImpl::GetProviderIdForRoute(const MediaRoute::Id& route_id) {
-  for (const auto& routes_query : routes_queries_) {
-    MediaRoutesQuery* query = routes_query.second.get();
-    for (const auto& provider_to_routes : query->providers_to_routes()) {
-      const mojom::MediaRouteProviderId provider_id = provider_to_routes.first;
-      const std::vector<MediaRoute>& routes = provider_to_routes.second;
-      if (std::find_if(routes.begin(), routes.end(),
-                       [&route_id](const MediaRoute& route) {
-                         return route.media_route_id() == route_id;
-                       }) != routes.end()) {
-        return provider_id;
-      }
+  for (const auto& provider_to_routes : routes_query_.providers_to_routes()) {
+    const mojom::MediaRouteProviderId provider_id = provider_to_routes.first;
+    const std::vector<MediaRoute>& routes = provider_to_routes.second;
+    if (std::find_if(routes.begin(), routes.end(),
+                     [&route_id](const MediaRoute& route) {
+                       return route.media_route_id() == route_id;
+                     }) != routes.end()) {
+      return provider_id;
     }
   }
   return absl::nullopt;
@@ -883,18 +815,15 @@
 absl::optional<mojom::MediaRouteProviderId>
 MediaRouterMojoImpl::GetProviderIdForPresentation(
     const std::string& presentation_id) {
-  for (const auto& routes_query : routes_queries_) {
-    MediaRoutesQuery* query = routes_query.second.get();
-    for (const auto& provider_to_routes : query->providers_to_routes()) {
-      const mojom::MediaRouteProviderId provider_id = provider_to_routes.first;
-      const std::vector<MediaRoute>& routes = provider_to_routes.second;
-      auto pred = [&presentation_id](const MediaRoute& route) {
-        return route.presentation_id() == presentation_id;
-      };
-      DCHECK_LE(std::count_if(routes.begin(), routes.end(), pred), 1);
-      if (std::find_if(routes.begin(), routes.end(), pred) != routes.end()) {
-        return provider_id;
-      }
+  for (const auto& provider_to_routes : routes_query_.providers_to_routes()) {
+    const mojom::MediaRouteProviderId provider_id = provider_to_routes.first;
+    const std::vector<MediaRoute>& routes = provider_to_routes.second;
+    auto pred = [&presentation_id](const MediaRoute& route) {
+      return route.presentation_id() == presentation_id;
+    };
+    DCHECK_LE(std::count_if(routes.begin(), routes.end(), pred), 1);
+    if (std::find_if(routes.begin(), routes.end(), pred) != routes.end()) {
+      return provider_id;
     }
   }
   return absl::nullopt;
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.h b/chrome/browser/media/router/mojo/media_router_mojo_impl.h
index 43d4b705..01cfffd 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl.h
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.h
@@ -205,6 +205,10 @@
 
   // Represents a query to the MediaRouteProviders for media routes and caches
   // media routes returned by MRPs. Holds observers for the query.
+  //
+  // NOTE: If the to-do below for providers_for_routes_ is fixed, then this
+  // entire class can be replaced with a std::vector<MediaRoute> and a
+  // base::ObserverList of observers.
   class MediaRoutesQuery {
    public:
     MediaRoutesQuery();
@@ -214,12 +218,9 @@
 
     ~MediaRoutesQuery();
 
-    // Caches the list of routes and joinable route IDs for the provider
-    // returned from the query.
-    void SetRoutesForProvider(
-        mojom::MediaRouteProviderId provider_id,
-        const std::vector<MediaRoute>& routes,
-        const std::vector<MediaRoute::Id>& joinable_route_ids);
+    // Caches the list of routes for the provider returned from the query.
+    void SetRoutesForProvider(mojom::MediaRouteProviderId provider_id,
+                              const std::vector<MediaRoute>& routes);
 
     // Adds |route| to the list of routes managed by the provider and returns
     // true, if it hasn't been added already. Returns false otherwise.
@@ -239,26 +240,20 @@
     const absl::optional<std::vector<MediaRoute>>& cached_route_list() const {
       return cached_route_list_;
     }
-    const std::vector<MediaRoute::Id>& joinable_route_ids() const {
-      return joinable_route_ids_;
-    }
     const base::flat_map<mojom::MediaRouteProviderId, std::vector<MediaRoute>>&
     providers_to_routes() const {
       return providers_to_routes_;
     }
 
    private:
-    // Cached list of routes and joinable route IDs for the query.
+    // Cached list of routes for the query.
     absl::optional<std::vector<MediaRoute>> cached_route_list_;
-    std::vector<MediaRoute::Id> joinable_route_ids_;
 
-    // Per-MRP lists of routes and joinable route IDs for the query.
-    // TODO(crbug.com/761493): Consider making MRP ID an attribute
-    // of MediaRoute, so that we can simplify these into vectors.
+    // Per-MRP lists of routes for the query.
+    // TODO(crbug.com/761493): Consider making MRP ID an attribute of
+    // MediaRoute, so that we can simplify these into vectors.
     base::flat_map<mojom::MediaRouteProviderId, std::vector<MediaRoute>>
         providers_to_routes_;
-    base::flat_map<mojom::MediaRouteProviderId, std::vector<MediaRoute::Id>>
-        providers_to_joinable_routes_;
 
     base::ObserverList<MediaRoutesObserver>::Unchecked observers_;
   };
@@ -281,16 +276,12 @@
 
   // Notifies |observer| of any existing cached routes, if it is still
   // registered.
-  void NotifyOfExistingRoutesIfRegistered(const MediaSource::Id& source_id,
-                                          MediaRoutesObserver* observer) const;
+  void NotifyOfExistingRoutesIfRegistered(MediaRoutesObserver* observer) const;
 
   // mojom::MediaRouter implementation.
   void OnIssue(const IssueInfo& issue) override;
-  void OnRoutesUpdated(
-      mojom::MediaRouteProviderId provider_id,
-      const std::vector<MediaRoute>& routes,
-      const std::string& media_source,
-      const std::vector<std::string>& joinable_route_ids) override;
+  void OnRoutesUpdated(mojom::MediaRouteProviderId provider_id,
+                       const std::vector<MediaRoute>& routes) override;
   void OnPresentationConnectionStateChanged(
       const std::string& route_id,
       blink::mojom::PresentationConnectionState state) override;
@@ -385,8 +376,9 @@
   base::flat_map<MediaSource::Id, std::unique_ptr<MediaSinksQuery>>
       sinks_queries_;
 
-  base::flat_map<MediaSource::Id, std::unique_ptr<MediaRoutesQuery>>
-      routes_queries_;
+  // Holds observers for media route updates and a map of providers to route
+  // ids..
+  MediaRoutesQuery routes_query_;
 
   using RouteMessageObserverList =
       base::ObserverList<RouteMessageObserver>::Unchecked;
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
index 0ab267f4..3fbafdf3 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
@@ -61,6 +61,7 @@
 using testing::SaveArg;
 using testing::Sequence;
 using testing::SizeIs;
+using testing::StrictMock;
 using testing::UnorderedElementsAre;
 using testing::Unused;
 using testing::WithArg;
@@ -77,8 +78,6 @@
 const char kTabSourceTwo[] = "urn:x-org.chromium.media:source:tab:2";
 const char kRouteId[] = "routeId";
 const char kRouteId2[] = "routeId2";
-const char kJoinableRouteId[] = "joinableRouteId";
-const char kJoinableRouteId2[] = "joinableRouteId2";
 const char kSinkId[] = "sink";
 const char kSinkId2[] = "sink2";
 const char kSinkName[] = "sinkName";
@@ -191,11 +190,8 @@
   }
 
   void UpdateRoutes(mojom::MediaRouteProviderId provider_id,
-                    const std::vector<MediaRoute>& routes,
-                    const std::string& media_source,
-                    const std::vector<std::string>& joinable_route_ids) {
-    router()->OnRoutesUpdated(provider_id, routes, media_source,
-                              joinable_route_ids);
+                    const std::vector<MediaRoute>& routes) {
+    router()->OnRoutesUpdated(provider_id, routes);
   }
 
   void RecordPresentationRequestUrlBySink(
@@ -229,7 +225,9 @@
 TEST_F(MediaRouterMojoImplTest, RouteRecognizedAfterCreation) {
   MockMediaRoutesObserver routes_observer(router());
 
-  EXPECT_CALL(routes_observer, OnRoutesUpdated(SizeIs(1), _));
+  EXPECT_CALL(routes_observer, OnRoutesUpdated(SizeIs(0)));
+  EXPECT_CALL(routes_observer, OnRoutesUpdated(SizeIs(1)));
+
   // TestCreateRoute() does not explicitly call OnRoutesUpdated() on the router.
   TestCreateRoute();
   base::RunLoop().RunUntilIdle();
@@ -356,8 +354,7 @@
                         nullptr, base::DoNothing(),
                         base::Milliseconds(kTimeoutMillis), true);
   const std::vector<MediaRoute> routes{route};
-  UpdateRoutes(mojom::MediaRouteProviderId::CAST, routes, std::string(),
-               std::vector<std::string>());
+  UpdateRoutes(mojom::MediaRouteProviderId::CAST, routes);
 
   // TODO(mfoltz): Where possible, convert other tests to use RunUntilIdle
   // instead of manually calling Run/Quit on the run loop.
@@ -399,8 +396,7 @@
   // Make sure the MR has received an update with the route, so it knows there
   // is a route to join.
   const std::vector<MediaRoute> routes{CreateMediaRoute()};
-  UpdateRoutes(mojom::MediaRouteProviderId::CAST, routes, std::string(),
-               std::vector<std::string>());
+  UpdateRoutes(mojom::MediaRouteProviderId::CAST, routes);
   EXPECT_TRUE(router()->HasJoinableRoute());
 
   EXPECT_CALL(mock_cast_provider_,
@@ -436,8 +432,7 @@
   // Make sure the MR has received an update with the route, so it knows there
   // is a route to join.
   const std::vector<MediaRoute> routes{route};
-  UpdateRoutes(mojom::MediaRouteProviderId::CAST, routes, std::string(),
-               std::vector<std::string>());
+  UpdateRoutes(mojom::MediaRouteProviderId::CAST, routes);
   EXPECT_TRUE(router()->HasJoinableRoute());
 
   // Use a lambda function as an invocation target here to work around
@@ -646,51 +641,19 @@
 
 TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaRoutesObserver) {
   MockMediaRouter mock_router;
-  MediaSource media_source(kSource);
-  MediaSource different_media_source(kSource2);
-  EXPECT_CALL(mock_cast_provider_, StartObservingMediaRoutes(media_source.id()))
-      .Times(1);
-  EXPECT_CALL(mock_cast_provider_,
-              StartObservingMediaRoutes(different_media_source.id()))
-      .Times(1);
 
   MediaRoutesObserver* observer_captured;
   EXPECT_CALL(mock_router, RegisterMediaRoutesObserver(_))
       .Times(3)
       .WillRepeatedly(SaveArg<0>(&observer_captured));
-  NiceMock<MockMediaRoutesObserver> routes_observer(&mock_router,
-                                                    media_source.id());
+  MockMediaRoutesObserver routes_observer(&mock_router);
   EXPECT_EQ(observer_captured, &routes_observer);
-  NiceMock<MockMediaRoutesObserver> extra_routes_observer(&mock_router,
-                                                          media_source.id());
+
+  MockMediaRoutesObserver extra_routes_observer(&mock_router);
   EXPECT_EQ(observer_captured, &extra_routes_observer);
-  NiceMock<MockMediaRoutesObserver> different_routes_observer(
-      &mock_router, different_media_source.id());
+
+  MockMediaRoutesObserver different_routes_observer(&mock_router);
   EXPECT_EQ(observer_captured, &different_routes_observer);
-  RegisterMediaRoutesObserver(&routes_observer);
-  RegisterMediaRoutesObserver(&extra_routes_observer);
-  RegisterMediaRoutesObserver(&different_routes_observer);
-
-  std::vector<MediaRoute> expected_routes{
-      MediaRoute(kRouteId, media_source, kSinkId, kDescription, false, false)};
-
-  MediaRoute incognito_expected_route(kRouteId2, media_source, kSinkId,
-                                      kDescription, false, false);
-  incognito_expected_route.set_off_the_record(true);
-  expected_routes.emplace_back(incognito_expected_route);
-
-  const std::vector<MediaRoute::Id> kExpectedJoinableRouteIds{
-      kJoinableRouteId, kJoinableRouteId2};
-  EXPECT_CALL(routes_observer,
-              OnRoutesUpdated(expected_routes, kExpectedJoinableRouteIds));
-  EXPECT_CALL(extra_routes_observer,
-              OnRoutesUpdated(expected_routes, kExpectedJoinableRouteIds));
-  EXPECT_CALL(different_routes_observer,
-              OnRoutesUpdated(expected_routes, kExpectedJoinableRouteIds))
-      .Times(0);
-  UpdateRoutes(mojom::MediaRouteProviderId::CAST, expected_routes,
-               media_source.id(), kExpectedJoinableRouteIds);
-  base::RunLoop().RunUntilIdle();
 
   EXPECT_CALL(mock_router, UnregisterMediaRoutesObserver(&routes_observer));
   EXPECT_CALL(mock_router,
@@ -700,62 +663,35 @@
   UnregisterMediaRoutesObserver(&routes_observer);
   UnregisterMediaRoutesObserver(&extra_routes_observer);
   UnregisterMediaRoutesObserver(&different_routes_observer);
-  EXPECT_CALL(mock_cast_provider_, StopObservingMediaRoutes(media_source.id()))
-      .Times(1);
-  EXPECT_CALL(mock_cast_provider_,
-              StopObservingMediaRoutes(different_media_source.id()));
-  base::RunLoop().RunUntilIdle();
 }
 
-// Tests that multiple MediaRoutesObservers having the same query do not cause
-// extra calls to providers because the OnRoutesUpdated() results are cached.
-TEST_F(MediaRouterMojoImplTest, RegisterMediaRoutesObserver_DedupingWithCache) {
-  const MediaSource media_source = MediaSource(kSource);
-  const std::vector<MediaRoute> kExpectedRoutes{
+TEST_F(MediaRouterMojoImplTest, RegisteredObserversGetMediaRouteUpdates) {
+  StrictMock<MockMediaRoutesObserver> routes_observer(router());
+  StrictMock<MockMediaRoutesObserver> extra_routes_observer(router());
+  StrictMock<MockMediaRoutesObserver> different_routes_observer(router());
+
+  MediaSource media_source(kSource);
+  std::vector<MediaRoute> expected_routes{
       MediaRoute(kRouteId, media_source, kSinkId, kDescription, false, false)};
-  const std::vector<MediaRoute::Id> kExpectedJoinableRouteIds{kJoinableRouteId};
 
-  Sequence sequence;
+  MediaRoute incognito_expected_route(kRouteId2, media_source, kSinkId,
+                                      kDescription, false, false);
+  incognito_expected_route.set_off_the_record(true);
+  expected_routes.push_back(incognito_expected_route);
 
-  // Creating the first observer will ask the provider to start observing routes
-  // having source |kSource|. The provider will respond with the existing route.
-  EXPECT_CALL(mock_cast_provider_, StartObservingMediaRoutes(media_source.id()))
+  EXPECT_CALL(routes_observer, OnRoutesUpdated(expected_routes)).Times(1);
+  EXPECT_CALL(extra_routes_observer, OnRoutesUpdated(expected_routes)).Times(1);
+  EXPECT_CALL(different_routes_observer, OnRoutesUpdated(expected_routes))
       .Times(1);
-  auto observer1 =
-      std::make_unique<MockMediaRoutesObserver>(router(), media_source.id());
-  base::RunLoop().RunUntilIdle();
-  EXPECT_CALL(*observer1,
-              OnRoutesUpdated(kExpectedRoutes, kExpectedJoinableRouteIds))
-      .Times(1);
-  UpdateRoutes(mojom::MediaRouteProviderId::CAST, kExpectedRoutes,
-               media_source.id(), kExpectedJoinableRouteIds);
-  base::RunLoop().RunUntilIdle();
 
-  // Creating two more observers will not make calls to the provider. Instead,
-  // the cached route list will be returned.
-  auto observer2 =
-      std::make_unique<MockMediaRoutesObserver>(router(), media_source.id());
-  auto observer3 =
-      std::make_unique<MockMediaRoutesObserver>(router(), media_source.id());
-  EXPECT_CALL(*observer2,
-              OnRoutesUpdated(kExpectedRoutes, kExpectedJoinableRouteIds))
-      .Times(1);
-  EXPECT_CALL(*observer3,
-              OnRoutesUpdated(kExpectedRoutes, kExpectedJoinableRouteIds))
-      .Times(1);
-  base::RunLoop().RunUntilIdle();
+  UpdateRoutes(mojom::MediaRouteProviderId::CAST, expected_routes);
 
-  // Kill 2 of three observers, and expect nothing happens at the provider.
-  observer1.reset();
-  observer2.reset();
-  base::RunLoop().RunUntilIdle();
+  UnregisterMediaRoutesObserver(&routes_observer);
+  UnregisterMediaRoutesObserver(&extra_routes_observer);
+  UnregisterMediaRoutesObserver(&different_routes_observer);
 
-  // Kill the final observer, and expect the provider to be told to stop
-  // observing.
-  EXPECT_CALL(mock_cast_provider_, StopObservingMediaRoutes(media_source.id()))
-      .Times(1);
-  observer3.reset();
-  base::RunLoop().RunUntilIdle();
+  // No route observers should be notified.
+  UpdateRoutes(mojom::MediaRouteProviderId::CAST, expected_routes);
 }
 
 TEST_F(MediaRouterMojoImplTest, SendRouteMessage) {
@@ -947,7 +883,7 @@
   MockMediaStatusObserver mock_observer(
       observer_remote.InitWithNewPipeAndPassReceiver());
   mojo::Remote<mojom::MediaStatusObserver> observer_remote_held_by_controller;
-  UpdateRoutes(mojom::MediaRouteProviderId::CAST, {CreateMediaRoute()}, "", {});
+  UpdateRoutes(mojom::MediaRouteProviderId::CAST, {CreateMediaRoute()});
 
   EXPECT_CALL(mock_cast_provider_,
               CreateMediaRouteControllerInternal(kRouteId, _, _, _))
@@ -1096,33 +1032,26 @@
   const MediaRoute route2a("route2a", source, "sink 2a", "", true, true);
   const MediaRoute route2b("route2b", source, "sink 2b", "", true, true);
   RegisterWiredDisplayProvider();
-  MockMediaRoutesObserver observer(router(), kSource);
+  MockMediaRoutesObserver observer(router());
 
   // Have the Cast MRP report routes.
   EXPECT_CALL(observer,
-              OnRoutesUpdated(UnorderedElementsAre(route1a, route1b),
-                              UnorderedElementsAre(route1a.media_route_id())));
-  UpdateRoutes(mojom::MediaRouteProviderId::CAST, {route1a, route1b}, kSource,
-               {route1a.media_route_id()});
+              OnRoutesUpdated(UnorderedElementsAre(route1a, route1b)));
+  UpdateRoutes(mojom::MediaRouteProviderId::CAST, {route1a, route1b});
 
   // Have the wired display MRP report routes.
-  EXPECT_CALL(
-      observer,
-      OnRoutesUpdated(UnorderedElementsAre(route1a, route1b, route2a, route2b),
-                      UnorderedElementsAre(route1a.media_route_id(),
-                                           route2a.media_route_id())));
-  UpdateRoutes(mojom::MediaRouteProviderId::WIRED_DISPLAY, {route2a, route2b},
-               kSource, {route2a.media_route_id()});
+  EXPECT_CALL(observer, OnRoutesUpdated(UnorderedElementsAre(
+                            route1a, route1b, route2a, route2b)));
+  UpdateRoutes(mojom::MediaRouteProviderId::WIRED_DISPLAY, {route2a, route2b});
 
   // Have the Cast MRP report an empty list of routes.
   EXPECT_CALL(observer,
-              OnRoutesUpdated(UnorderedElementsAre(route2a, route2b),
-                              UnorderedElementsAre(route2a.media_route_id())));
-  UpdateRoutes(mojom::MediaRouteProviderId::CAST, {}, kSource, {});
+              OnRoutesUpdated(UnorderedElementsAre(route2a, route2b)));
+  UpdateRoutes(mojom::MediaRouteProviderId::CAST, {});
 
   // Have the wired display MRP report an empty list of routes.
-  EXPECT_CALL(observer, OnRoutesUpdated(IsEmpty(), IsEmpty()));
-  UpdateRoutes(mojom::MediaRouteProviderId::WIRED_DISPLAY, {}, kSource, {});
+  EXPECT_CALL(observer, OnRoutesUpdated(IsEmpty()));
+  UpdateRoutes(mojom::MediaRouteProviderId::WIRED_DISPLAY, {});
 }
 
 TEST_F(MediaRouterMojoImplTest, TestRecordPresentationRequestUrlBySink) {
diff --git a/chrome/browser/media/router/providers/cast/cast_activity_manager.cc b/chrome/browser/media/router/providers/cast/cast_activity_manager.cc
index e79e281..2e6f6c0 100644
--- a/chrome/browser/media/router/providers/cast/cast_activity_manager.cc
+++ b/chrome/browser/media/router/providers/cast/cast_activity_manager.cc
@@ -82,19 +82,6 @@
   session_tracker_->RemoveObserver(this);
 }
 
-void CastActivityManager::AddRouteQuery(const MediaSource::Id& source) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  route_queries_.insert(source);
-  std::vector<MediaRoute> routes = GetRoutes();
-  if (!routes.empty())
-    NotifyOnRoutesUpdated(source, routes);
-}
-
-void CastActivityManager::RemoveRouteQuery(const MediaSource::Id& source) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  route_queries_.erase(source);
-}
-
 void CastActivityManager::LaunchSession(
     const CastMediaSource& cast_source,
     const MediaSinkInternal& sink,
@@ -762,17 +749,7 @@
 
 void CastActivityManager::NotifyAllOnRoutesUpdated() {
   std::vector<MediaRoute> routes = GetRoutes();
-  for (const auto& source_id : route_queries_)
-    NotifyOnRoutesUpdated(source_id, routes);
-}
-
-void CastActivityManager::NotifyOnRoutesUpdated(
-    const MediaSource::Id& source_id,
-    const std::vector<MediaRoute>& routes) {
-  // Note: joinable_route_ids is empty as we are deprecating the join feature
-  // in the Harmony UI.
-  media_router_->OnRoutesUpdated(mojom::MediaRouteProviderId::CAST, routes,
-                                 source_id, std::vector<MediaRoute::Id>());
+  media_router_->OnRoutesUpdated(mojom::MediaRouteProviderId::CAST, routes);
 }
 
 void CastActivityManager::HandleLaunchSessionResponse(
diff --git a/chrome/browser/media/router/providers/cast/cast_activity_manager.h b/chrome/browser/media/router/providers/cast/cast_activity_manager.h
index 003f05763..444b748 100644
--- a/chrome/browser/media/router/providers/cast/cast_activity_manager.h
+++ b/chrome/browser/media/router/providers/cast/cast_activity_manager.h
@@ -74,13 +74,6 @@
                       const std::string& hash_token);
   ~CastActivityManager() override;
 
-  // Adds or removes a route query with |source|. When adding a route query, if
-  // the current list of routes is non-empty, the query will be immediately
-  // updated with the current list.
-  // TODO(https://crbug.com/882481): Simplify the route query API.
-  void AddRouteQuery(const MediaSource::Id& source);
-  void RemoveRouteQuery(const MediaSource::Id& source);
-
   // Launches a Cast session with parameters given by |cast_source| to |sink|.
   // Returns the created MediaRoute and notifies existing route queries.
   void LaunchSession(const CastMediaSource& cast_source,
@@ -110,6 +103,7 @@
 
   const MediaRoute* GetRoute(const MediaRoute::Id& route_id) const;
   std::vector<MediaRoute> GetRoutes() const;
+  void NotifyAllOnRoutesUpdated();
   CastSessionTracker* GetCastSessionTracker() const { return session_tracker_; }
 
   // cast_channel::CastMessageHandler::Observer overrides.
@@ -226,10 +220,6 @@
       blink::mojom::PresentationConnectionState state,
       blink::mojom::PresentationConnectionCloseReason close_reason);
 
-  void NotifyAllOnRoutesUpdated();
-  void NotifyOnRoutesUpdated(const MediaSource::Id& source_id,
-                             const std::vector<MediaRoute>& routes);
-
   void HandleLaunchSessionResponse(
       DoLaunchSessionParams params,
       cast_channel::LaunchSessionResponse response);
@@ -286,8 +276,6 @@
 
   static CastActivityFactoryForTest* cast_activity_factory_for_test_;
 
-  base::flat_set<MediaSource::Id> route_queries_;
-
   // This map contains all activities--both presentation activities and
   // mirroring activities.
   ActivityMap activities_;
diff --git a/chrome/browser/media/router/providers/cast/cast_activity_manager_unittest.cc b/chrome/browser/media/router/providers/cast/cast_activity_manager_unittest.cc
index 1988ca8..123a52a 100644
--- a/chrome/browser/media/router/providers/cast/cast_activity_manager_unittest.cc
+++ b/chrome/browser/media/router/providers/cast/cast_activity_manager_unittest.cc
@@ -179,9 +179,6 @@
         }));
 
     RunUntilIdle();
-
-    // Make sure we get route updates.
-    manager_->AddRouteQuery(route_query_);
   }
 
   void TearDown() override {
@@ -445,9 +442,8 @@
   // optionally be saved in the variable pointed to by |route_ptr|.
   void ExpectSingleRouteUpdate() {
     updated_route_ = absl::nullopt;
-    EXPECT_CALL(mock_router_,
-                OnRoutesUpdated(mojom::MediaRouteProviderId::CAST,
-                                ElementsAre(_), route_query_, IsEmpty()))
+    EXPECT_CALL(mock_router_, OnRoutesUpdated(mojom::MediaRouteProviderId::CAST,
+                                              ElementsAre(_)))
         .WillOnce(WithArg<1>(
             [this](const auto& routes) { updated_route_ = routes[0]; }));
   }
@@ -456,8 +452,7 @@
   void ExpectEmptyRouteUpdate() {
     updated_route_ = absl::nullopt;
     EXPECT_CALL(mock_router_,
-                OnRoutesUpdated(mojom::MediaRouteProviderId::CAST, IsEmpty(),
-                                route_query_, IsEmpty()))
+                OnRoutesUpdated(mojom::MediaRouteProviderId::CAST, IsEmpty()))
         .Times(1);
   }
 
diff --git a/chrome/browser/media/router/providers/cast/cast_media_route_provider.cc b/chrome/browser/media/router/providers/cast/cast_media_route_provider.cc
index 7db82202..341c1cd 100644
--- a/chrome/browser/media/router/providers/cast/cast_media_route_provider.cc
+++ b/chrome/browser/media/router/providers/cast/cast_media_route_provider.cc
@@ -231,16 +231,9 @@
   sink_queries_.erase(media_source);
 }
 
-void CastMediaRouteProvider::StartObservingMediaRoutes(
-    const std::string& media_source) {
+void CastMediaRouteProvider::StartObservingMediaRoutes() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  activity_manager_->AddRouteQuery(media_source);
-}
-
-void CastMediaRouteProvider::StopObservingMediaRoutes(
-    const std::string& media_source) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  activity_manager_->RemoveRouteQuery(media_source);
+  activity_manager_->NotifyAllOnRoutesUpdated();
 }
 
 void CastMediaRouteProvider::StartListeningForRouteMessages(
diff --git a/chrome/browser/media/router/providers/cast/cast_media_route_provider.h b/chrome/browser/media/router/providers/cast/cast_media_route_provider.h
index db0d40c..76d6953 100644
--- a/chrome/browser/media/router/providers/cast/cast_media_route_provider.h
+++ b/chrome/browser/media/router/providers/cast/cast_media_route_provider.h
@@ -76,8 +76,7 @@
                               const std::vector<uint8_t>& data) override;
   void StartObservingMediaSinks(const std::string& media_source) override;
   void StopObservingMediaSinks(const std::string& media_source) override;
-  void StartObservingMediaRoutes(const std::string& media_source) override;
-  void StopObservingMediaRoutes(const std::string& media_source) override;
+  void StartObservingMediaRoutes() override;
   void StartListeningForRouteMessages(const std::string& route_id) override;
   void StopListeningForRouteMessages(const std::string& route_id) override;
   void DetachRoute(const std::string& route_id) override;
diff --git a/chrome/browser/media/router/providers/dial/dial_media_route_provider.cc b/chrome/browser/media/router/providers/dial/dial_media_route_provider.cc
index 206a21f..31c9da0 100644
--- a/chrome/browser/media/router/providers/dial/dial_media_route_provider.cc
+++ b/chrome/browser/media/router/providers/dial/dial_media_route_provider.cc
@@ -507,16 +507,12 @@
 
 void DialMediaRouteProvider::NotifyAllOnRoutesUpdated() {
   auto routes = activity_manager_->GetRoutes();
-  for (const auto& query : media_route_queries_)
-    NotifyOnRoutesUpdated(query, routes);
+  NotifyOnRoutesUpdated(routes);
 }
 
 void DialMediaRouteProvider::NotifyOnRoutesUpdated(
-    const MediaSource::Id& source_id,
     const std::vector<MediaRoute>& routes) {
-  media_router_->OnRoutesUpdated(mojom::MediaRouteProviderId::DIAL, routes,
-                                 source_id,
-                                 /* joinable_route_ids */ {});
+  media_router_->OnRoutesUpdated(mojom::MediaRouteProviderId::DIAL, routes);
 }
 
 void DialMediaRouteProvider::SendRouteBinaryMessage(
@@ -580,19 +576,11 @@
     media_sink_queries_.erase(sink_query_it);
 }
 
-void DialMediaRouteProvider::StartObservingMediaRoutes(
-    const std::string& media_source) {
-  media_route_queries_.insert(media_source);
-
+void DialMediaRouteProvider::StartObservingMediaRoutes() {
   // Return current set of routes.
   auto routes = activity_manager_->GetRoutes();
   if (!routes.empty())
-    NotifyOnRoutesUpdated(media_source, routes);
-}
-
-void DialMediaRouteProvider::StopObservingMediaRoutes(
-    const std::string& media_source) {
-  media_route_queries_.erase(media_source);
+    NotifyOnRoutesUpdated(routes);
 }
 
 void DialMediaRouteProvider::StartListeningForRouteMessages(
diff --git a/chrome/browser/media/router/providers/dial/dial_media_route_provider.h b/chrome/browser/media/router/providers/dial/dial_media_route_provider.h
index d055cf2..88ef3f95 100644
--- a/chrome/browser/media/router/providers/dial/dial_media_route_provider.h
+++ b/chrome/browser/media/router/providers/dial/dial_media_route_provider.h
@@ -91,8 +91,7 @@
                               const std::vector<uint8_t>& data) override;
   void StartObservingMediaSinks(const std::string& media_source) override;
   void StopObservingMediaSinks(const std::string& media_source) override;
-  void StartObservingMediaRoutes(const std::string& media_source) override;
-  void StopObservingMediaRoutes(const std::string& media_source) override;
+  void StartObservingMediaRoutes() override;
   void StartListeningForRouteMessages(const std::string& route_id) override;
   void StopListeningForRouteMessages(const std::string& route_id) override;
   void DetachRoute(const std::string& route_id) override;
@@ -171,8 +170,7 @@
                            const absl::optional<std::string>& message,
                            RouteRequestResult::ResultCode result_code);
   void NotifyAllOnRoutesUpdated();
-  void NotifyOnRoutesUpdated(const MediaSource::Id& source_id,
-                             const std::vector<MediaRoute>& routes);
+  void NotifyOnRoutesUpdated(const std::vector<MediaRoute>& routes);
 
   // Returns a list of valid origins for |app_name|. Returns an empty list if
   // all origins are valid.
diff --git a/chrome/browser/media/router/providers/dial/dial_media_route_provider_unittest.cc b/chrome/browser/media/router/providers/dial/dial_media_route_provider_unittest.cc
index 7b7b22d..d2b045ac 100644
--- a/chrome/browser/media/router/providers/dial/dial_media_route_provider_unittest.cc
+++ b/chrome/browser/media/router/providers/dial/dial_media_route_provider_unittest.cc
@@ -112,7 +112,7 @@
 
     // Observe media routes in order for DialMediaRouteProvider to send back
     // route updates.
-    provider_->StartObservingMediaRoutes(MediaSource::Id());
+    provider_->StartObservingMediaRoutes();
   }
 
   void TearDown() override { provider_.reset(); }
@@ -148,7 +148,7 @@
 
     // DialMediaRouteProvider doesn't send route list update following
     // CreateRoute, but MR will add the route returned in the response.
-    EXPECT_CALL(mock_router_, OnRoutesUpdated(_, _, _, _)).Times(0);
+    EXPECT_CALL(mock_router_, OnRoutesUpdated(_, _)).Times(0);
     provider_->CreateRoute(
         source_id, sink_id, presentation_id, origin_, 1, base::TimeDelta(),
         /* off_the_record */ false,
@@ -287,7 +287,7 @@
     loader_factory_.AddResponse(app_launch_url_, std::move(response_head), "",
                                 network::URLLoaderCompletionStatus());
     std::vector<MediaRoute> routes;
-    EXPECT_CALL(mock_router_, OnRoutesUpdated(_, Not(IsEmpty()), _, IsEmpty()))
+    EXPECT_CALL(mock_router_, OnRoutesUpdated(_, Not(IsEmpty())))
         .WillOnce(SaveArg<1>(&routes));
     base::RunLoop().RunUntilIdle();
 
@@ -363,7 +363,7 @@
         mock_router_,
         OnPresentationConnectionStateChanged(
             route_id, blink::mojom::PresentationConnectionState::TERMINATED));
-    EXPECT_CALL(mock_router_, OnRoutesUpdated(_, IsEmpty(), _, IsEmpty()));
+    EXPECT_CALL(mock_router_, OnRoutesUpdated(_, IsEmpty()));
     base::RunLoop().RunUntilIdle();
 
     ASSERT_EQ(1u, received_messages.size());
@@ -386,7 +386,7 @@
     EXPECT_CALL(*this,
                 OnTerminateRoute(_, testing::Ne(RouteRequestResult::OK)));
     EXPECT_CALL(mock_router_, OnRouteMessagesReceived(_, _));
-    EXPECT_CALL(mock_router_, OnRoutesUpdated(_, _, _, _)).Times(1);
+    EXPECT_CALL(mock_router_, OnRoutesUpdated(_, _)).Times(1);
     EXPECT_CALL(*mock_sink_service_.app_discovery_service(),
                 DoFetchDialAppInfo(_, _));
     provider_->TerminateRoute(
diff --git a/chrome/browser/media/router/providers/test/test_media_route_provider.cc b/chrome/browser/media/router/providers/test/test_media_route_provider.cc
index 7754a82..36541995 100644
--- a/chrome/browser/media/router/providers/test/test_media_route_provider.cc
+++ b/chrome/browser/media/router/providers/test/test_media_route_provider.cc
@@ -101,8 +101,7 @@
 
     media_router_->OnPresentationConnectionStateChanged(
         route_id, blink::mojom::PresentationConnectionState::CONNECTED);
-    media_router_->OnRoutesUpdated(kProviderId, GetMediaRoutes(), media_source,
-                                   {});
+    media_router_->OnRoutesUpdated(kProviderId, GetMediaRoutes());
     std::move(callback).Run(routes_[route_id], nullptr, absl::nullopt,
                             RouteRequestResult::ResultCode::OK);
   }
@@ -160,9 +159,7 @@
   routes_.erase(it);
   media_router_->OnPresentationConnectionStateChanged(
       route_id, blink::mojom::PresentationConnectionState::TERMINATED);
-  media_router_->OnRoutesUpdated(
-      kProviderId, GetMediaRoutes(),
-      MediaRoute::GetMediaSourceIdFromMediaRouteId(route_id), {});
+  media_router_->OnRoutesUpdated(kProviderId, GetMediaRoutes());
   std::move(callback).Run(absl::nullopt, RouteRequestResult::OK);
 }
 
@@ -176,9 +173,7 @@
         media_route_id,
         blink::mojom::PresentationConnectionCloseReason::CONNECTION_ERROR,
         "Send error. Closing connection.");
-    media_router_->OnRoutesUpdated(
-        kProviderId, GetMediaRoutes(),
-        MediaRoute::GetMediaSourceIdFromMediaRouteId(media_route_id), {});
+    media_router_->OnRoutesUpdated(kProviderId, GetMediaRoutes());
   } else {
     std::string response = "Pong: " + message;
     std::vector<mojom::RouteMessagePtr> messages;
@@ -205,11 +200,7 @@
 void TestMediaRouteProvider::StopObservingMediaSinks(
     const std::string& media_source) {}
 
-void TestMediaRouteProvider::StartObservingMediaRoutes(
-    const std::string& media_source) {}
-
-void TestMediaRouteProvider::StopObservingMediaRoutes(
-    const std::string& media_source) {}
+void TestMediaRouteProvider::StartObservingMediaRoutes() {}
 
 void TestMediaRouteProvider::StartListeningForRouteMessages(
     const std::string& route_id) {}
@@ -257,6 +248,10 @@
   offscreen_tab_->Start(source_urn, gfx::Size(180, 180), presentation_id);
 }
 
+bool TestMediaRouteProvider::HasRoutes() const {
+  return !routes_.empty();
+}
+
 void TestMediaRouteProvider::TearDown() {
   // An OffscreenTab observes its Profile*, and must be destroyed before
   // Profiles.
diff --git a/chrome/browser/media/router/providers/test/test_media_route_provider.h b/chrome/browser/media/router/providers/test/test_media_route_provider.h
index d9acb9e..c33644d83 100644
--- a/chrome/browser/media/router/providers/test/test_media_route_provider.h
+++ b/chrome/browser/media/router/providers/test/test_media_route_provider.h
@@ -56,8 +56,7 @@
                               const std::vector<uint8_t>& data) override;
   void StartObservingMediaSinks(const std::string& media_source) override;
   void StopObservingMediaSinks(const std::string& media_source) override;
-  void StartObservingMediaRoutes(const std::string& media_source) override;
-  void StopObservingMediaRoutes(const std::string& media_source) override;
+  void StartObservingMediaRoutes() override;
   void StartListeningForRouteMessages(const std::string& route_id) override;
   void StopListeningForRouteMessages(const std::string& route_id) override;
   void DetachRoute(const std::string& route_id) override;
@@ -99,6 +98,9 @@
   void CaptureOffScreenTab(content::WebContents* web_contents,
                            GURL source_urn,
                            std::string& presentation_id);
+
+  bool HasRoutes() const;
+
   void TearDown();
 
  private:
diff --git a/chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider.cc b/chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider.cc
index a168be8..fae1a36 100644
--- a/chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider.cc
+++ b/chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider.cc
@@ -188,18 +188,12 @@
   sink_queries_.erase(media_source);
 }
 
-void WiredDisplayMediaRouteProvider::StartObservingMediaRoutes(
-    const std::string& media_source) {
-  route_queries_.insert(media_source);
+void WiredDisplayMediaRouteProvider::StartObservingMediaRoutes() {
   std::vector<MediaRoute> route_list;
   for (const auto& presentation : presentations_)
     route_list.push_back(presentation.second.route());
-  media_router_->OnRoutesUpdated(kProviderId, route_list, media_source, {});
-}
 
-void WiredDisplayMediaRouteProvider::StopObservingMediaRoutes(
-    const std::string& media_source) {
-  route_queries_.erase(media_source);
+  media_router_->OnRoutesUpdated(kProviderId, route_list);
 }
 
 void WiredDisplayMediaRouteProvider::StartListeningForRouteMessages(
@@ -327,8 +321,8 @@
   std::vector<MediaRoute> route_list;
   for (const auto& presentation : presentations_)
     route_list.push_back(presentation.second.route());
-  for (const auto& route_query : route_queries_)
-    media_router_->OnRoutesUpdated(kProviderId, route_list, route_query, {});
+
+  media_router_->OnRoutesUpdated(kProviderId, route_list);
 }
 
 void WiredDisplayMediaRouteProvider::NotifySinkObservers() {
diff --git a/chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider.h b/chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider.h
index b86cdf4b..3e05bf49 100644
--- a/chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider.h
+++ b/chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider.h
@@ -80,8 +80,7 @@
                               const std::vector<uint8_t>& data) override;
   void StartObservingMediaSinks(const std::string& media_source) override;
   void StopObservingMediaSinks(const std::string& media_source) override;
-  void StartObservingMediaRoutes(const std::string& media_source) override;
-  void StopObservingMediaRoutes(const std::string& media_source) override;
+  void StartObservingMediaRoutes() override;
   void StartListeningForRouteMessages(const std::string& route_id) override;
   void StopListeningForRouteMessages(const std::string& route_id) override;
   void DetachRoute(const std::string& route_id) override;
@@ -196,9 +195,6 @@
   // Map from presentation IDs to active presentations managed by this provider.
   std::map<std::string, Presentation> presentations_;
 
-  // A set of MediaSource IDs associated with queries for MediaRoute updates.
-  base::flat_set<std::string> route_queries_;
-
   // A set of MediaSource IDs associated with queries for MediaSink updates.
   base::flat_set<std::string> sink_queries_;
 
diff --git a/chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider_unittest.cc b/chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider_unittest.cc
index a27c9397..e1d875e7 100644
--- a/chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider_unittest.cc
+++ b/chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider_unittest.cc
@@ -308,7 +308,7 @@
   MockCallback callback;
 
   provider_->set_all_displays({secondary_display1_, primary_display_});
-  provider_remote_->StartObservingMediaRoutes(kPresentationSource);
+  provider_remote_->StartObservingMediaRoutes();
   base::RunLoop().RunUntilIdle();
 
   // Create a route for |presentation_id|.
@@ -320,8 +320,7 @@
             EXPECT_EQ(route->media_route_id(), presentation_id);
             EXPECT_EQ(route->description(), "Presenting (www.example.com)");
           })));
-  EXPECT_CALL(router_,
-              OnRoutesUpdated(kProviderId, _, kPresentationSource, IsEmpty()))
+  EXPECT_CALL(router_, OnRoutesUpdated(kProviderId, _))
       .WillOnce(WithArg<1>(
           Invoke([&presentation_id](const std::vector<MediaRoute>& routes) {
             EXPECT_EQ(routes.size(), 1u);
@@ -352,8 +351,7 @@
 
   // The presentation should not be removed until the receiver's termination
   // callback is called.
-  EXPECT_CALL(router_, OnRoutesUpdated(kProviderId, IsEmpty(),
-                                       kPresentationSource, IsEmpty()));
+  EXPECT_CALL(router_, OnRoutesUpdated(kProviderId, IsEmpty()));
   receiver_creator_.receiver()->RunTerminationCallback();
 }
 
@@ -363,7 +361,7 @@
   NiceMock<MockCallback> callback;
 
   provider_->set_all_displays({secondary_display1_, primary_display_});
-  provider_remote_->StartObservingMediaRoutes(kPresentationSource);
+  provider_remote_->StartObservingMediaRoutes();
   base::RunLoop().RunUntilIdle();
 
   // Create a route for |presentation_id|.
@@ -393,7 +391,7 @@
 TEST_F(WiredDisplayMediaRouteProviderTest, ExitFullscreenOnDisplayRemoved) {
   NiceMock<MockCallback> callback;
   provider_->set_all_displays({secondary_display1_, primary_display_});
-  provider_remote_->StartObservingMediaRoutes(kPresentationSource);
+  provider_remote_->StartObservingMediaRoutes();
   base::RunLoop().RunUntilIdle();
 
   provider_remote_->CreateRoute(
diff --git a/chrome/browser/media/router/test/media_router_mojo_test.cc b/chrome/browser/media/router/test/media_router_mojo_test.cc
index 14f8dec4..8a22726 100644
--- a/chrome/browser/media/router/test/media_router_mojo_test.cc
+++ b/chrome/browser/media/router/test/media_router_mojo_test.cc
@@ -142,10 +142,10 @@
     mojom::MediaRouteProviderId provider_id,
     const MediaRoute::Id& route_id) {
   if (!routes_observer_)
-    routes_observer_ = std::make_unique<MediaRoutesObserver>(router(), kSource);
+    routes_observer_ = std::make_unique<MediaRoutesObserver>(router());
   MediaRoute route = CreateMediaRoute();
   route.set_media_route_id(route_id);
-  router()->OnRoutesUpdated(provider_id, {route}, kSource, {});
+  router()->OnRoutesUpdated(provider_id, {route});
 }
 
 void MediaRouterMojoTest::ProvideTestSink(
@@ -211,8 +211,7 @@
   // is a route to join.
   std::vector<MediaRoute> routes;
   routes.push_back(route);
-  router()->OnRoutesUpdated(mojom::MediaRouteProviderId::CAST, routes,
-                            std::string(), std::vector<std::string>());
+  router()->OnRoutesUpdated(mojom::MediaRouteProviderId::CAST, routes);
   EXPECT_TRUE(router()->HasJoinableRoute());
 
   // Use a lambda function as an invocation target here to work around
diff --git a/chrome/browser/media/router/test/media_router_mojo_test.h b/chrome/browser/media/router/test/media_router_mojo_test.h
index 0bdc2bf..0dca0dc 100644
--- a/chrome/browser/media/router/test/media_router_mojo_test.h
+++ b/chrome/browser/media/router/test/media_router_mojo_test.h
@@ -103,8 +103,7 @@
                void(const std::string& route_id));
   MOCK_METHOD1(OnPresentationSessionDetached,
                void(const std::string& route_id));
-  MOCK_METHOD1(StartObservingMediaRoutes, void(const std::string& source));
-  MOCK_METHOD1(StopObservingMediaRoutes, void(const std::string& source));
+  MOCK_METHOD0(StartObservingMediaRoutes, void());
   MOCK_METHOD0(EnableMdnsDiscovery, void());
   MOCK_METHOD1(UpdateMediaSinks, void(const std::string& source));
   void CreateMediaRouteController(
diff --git a/chrome/browser/media/router/test/mock_mojo_media_router.h b/chrome/browser/media/router/test/mock_mojo_media_router.h
index b03932f..dc08246 100644
--- a/chrome/browser/media/router/test/mock_mojo_media_router.h
+++ b/chrome/browser/media/router/test/mock_mojo_media_router.h
@@ -36,11 +36,9 @@
                     const std::string& media_source,
                     const std::vector<MediaSinkInternal>& internal_sinks,
                     const std::vector<url::Origin>& origins));
-  MOCK_METHOD4(OnRoutesUpdated,
+  MOCK_METHOD2(OnRoutesUpdated,
                void(mojom::MediaRouteProviderId provider_id,
-                    const std::vector<MediaRoute>& routes,
-                    const std::string& media_source,
-                    const std::vector<std::string>& joinable_route_ids));
+                    const std::vector<MediaRoute>& routes));
   MOCK_METHOD2(OnPresentationConnectionStateChanged,
                void(const std::string& route_id,
                     blink::mojom::PresentationConnectionState state));
diff --git a/chrome/browser/optimization_guide/page_content_annotations_service_factory.cc b/chrome/browser/optimization_guide/page_content_annotations_service_factory.cc
index e41c604..7af73261 100644
--- a/chrome/browser/optimization_guide/page_content_annotations_service_factory.cc
+++ b/chrome/browser/optimization_guide/page_content_annotations_service_factory.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/optimization_guide/page_content_annotations_service_factory.h"
 
+#include "base/task/sequenced_task_runner.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
@@ -11,9 +14,11 @@
 #include "chrome/browser/profiles/profile.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 #include "components/optimization_guide/content/browser/page_content_annotations_service.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
 
 // static
 optimization_guide::PageContentAnnotationsService*
@@ -46,6 +51,12 @@
     return nullptr;
 
   Profile* profile = Profile::FromBrowserContext(context);
+
+  auto* proto_db_provider = profile->GetOriginalProfile()
+                                ->GetDefaultStoragePartition()
+                                ->GetProtoDatabaseProvider();
+  base::FilePath profile_path = profile->GetOriginalProfile()->GetPath();
+
   // The optimization guide and history services must be available for the page
   // content annotations service to work.
   OptimizationGuideKeyedService* optimization_guide_keyed_service =
@@ -56,7 +67,10 @@
   if (optimization_guide_keyed_service && history_service) {
     return new optimization_guide::PageContentAnnotationsService(
         g_browser_process->GetApplicationLocale(),
-        optimization_guide_keyed_service, history_service);
+        optimization_guide_keyed_service, history_service, proto_db_provider,
+        profile_path,
+        base::ThreadPool::CreateSequencedTaskRunner(
+            {base::MayBlock(), base::TaskPriority::BEST_EFFORT}));
   }
   return nullptr;
 }
diff --git a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java
index 0fad594..a831712 100644
--- a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java
+++ b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java
@@ -97,7 +97,7 @@
      */
     public static boolean hasChosenToSyncPasswordsWithNoCustomPassphrase(SyncService syncService) {
         return PasswordManagerHelper.hasChosenToSyncPasswords(syncService)
-                && syncService.isEngineInitialized() && !syncService.isUsingExplicitPassphrase();
+                && !syncService.isUsingExplicitPassphrase();
     }
 
     /**
@@ -109,7 +109,6 @@
     public static boolean isSyncingPasswordsWithNoCustomPassphrase(SyncService syncService) {
         if (syncService == null || !syncService.hasSyncConsent()) return false;
         if (!syncService.getActiveDataTypes().contains(ModelType.PASSWORDS)) return false;
-        if (!syncService.isEngineInitialized()) return false;
         if (syncService.isUsingExplicitPassphrase()) return false;
         return true;
     }
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
index 67940dc..35b05a1f 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
@@ -131,7 +131,6 @@
         when(mSyncServiceMock.isSyncFeatureEnabled()).thenReturn(true);
         when(mSyncServiceMock.getChosenDataTypes())
                 .thenReturn(CollectionUtil.newHashSet(ModelType.PASSWORDS));
-        when(mSyncServiceMock.isEngineInitialized()).thenReturn(true);
         when(mSyncServiceMock.isUsingExplicitPassphrase()).thenReturn(true);
         Assert.assertTrue(PasswordManagerHelper.hasChosenToSyncPasswords(mSyncServiceMock));
         Assert.assertFalse(PasswordManagerHelper.hasChosenToSyncPasswordsWithNoCustomPassphrase(
@@ -139,23 +138,10 @@
     }
 
     @Test
-    public void testSyncEnabledButInitializing() {
-        when(mSyncServiceMock.isSyncFeatureEnabled()).thenReturn(true);
-        when(mSyncServiceMock.getChosenDataTypes())
-                .thenReturn(CollectionUtil.newHashSet(ModelType.PASSWORDS));
-        when(mSyncServiceMock.isEngineInitialized()).thenReturn(false);
-        Assert.assertTrue(PasswordManagerHelper.hasChosenToSyncPasswords(mSyncServiceMock));
-        Assert.assertFalse(PasswordManagerHelper.hasChosenToSyncPasswordsWithNoCustomPassphrase(
-                mSyncServiceMock));
-        verify(mSyncServiceMock, never()).isUsingExplicitPassphrase();
-    }
-
-    @Test
     public void testActivelySyncingPasswordsWithNoCustomPassphrase() {
         when(mSyncServiceMock.hasSyncConsent()).thenReturn(true);
         when(mSyncServiceMock.getActiveDataTypes())
                 .thenReturn(CollectionUtil.newHashSet(ModelType.PASSWORDS));
-        when(mSyncServiceMock.isEngineInitialized()).thenReturn(true);
         when(mSyncServiceMock.isUsingExplicitPassphrase()).thenReturn(false);
         Assert.assertTrue(
                 PasswordManagerHelper.isSyncingPasswordsWithNoCustomPassphrase(mSyncServiceMock));
@@ -166,7 +152,6 @@
         when(mSyncServiceMock.hasSyncConsent()).thenReturn(true);
         when(mSyncServiceMock.getActiveDataTypes())
                 .thenReturn(CollectionUtil.newHashSet(ModelType.PASSWORDS));
-        when(mSyncServiceMock.isEngineInitialized()).thenReturn(true);
         when(mSyncServiceMock.isUsingExplicitPassphrase()).thenReturn(true);
         Assert.assertFalse(
                 PasswordManagerHelper.isSyncingPasswordsWithNoCustomPassphrase(mSyncServiceMock));
diff --git a/chrome/browser/policy/BUILD.gn b/chrome/browser/policy/BUILD.gn
index 841f8b8..24080c6 100644
--- a/chrome/browser/policy/BUILD.gn
+++ b/chrome/browser/policy/BUILD.gn
@@ -62,6 +62,8 @@
     sources += [
       "policy_test_utils.cc",
       "policy_test_utils.h",
+      "safe_search_policy_test.cc",
+      "safe_search_policy_test.h",
       "url_blocking_policy_test_utils.cc",
       "url_blocking_policy_test_utils.h",
     ]
diff --git a/chrome/browser/policy/policy_test_utils.cc b/chrome/browser/policy/policy_test_utils.cc
index 853392e..fa2e86de 100644
--- a/chrome/browser/policy/policy_test_utils.cc
+++ b/chrome/browser/policy/policy_test_utils.cc
@@ -4,21 +4,22 @@
 
 #include "chrome/browser/policy/policy_test_utils.h"
 
+#include <string>
+#include <utility>
+
 #include "base/callback_helpers.h"
 #include "base/path_service.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/current_thread.h"
 #include "base/test/bind.h"
+#include "base/values.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/net/safe_search_util.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/omnibox/browser/omnibox_edit_model.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/policy_constants.h"
 #include "components/security_interstitials/content/security_interstitial_page.h"
@@ -27,7 +28,6 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/network_service_util.h"
-#include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/dns/mock_host_resolver.h"
@@ -51,10 +51,6 @@
 
 PolicyTest::~PolicyTest() = default;
 
-void PolicyTest::SetUp() {
-  InProcessBrowserTest::SetUp();
-}
-
 void PolicyTest::SetUpInProcessBrowserTestFixture() {
   base::CommandLine::ForCurrentProcess()->AppendSwitch("noerrdialogs");
   provider_.SetDefaultReturns(true /* is_initialization_complete_return */,
@@ -99,46 +95,6 @@
                 POLICY_SOURCE_CLOUD, std::move(value), nullptr);
 }
 
-void PolicyTest::ApplySafeSearchPolicy(
-    absl::optional<base::Value> legacy_safe_search,
-    absl::optional<base::Value> google_safe_search,
-    absl::optional<base::Value> legacy_youtube,
-    absl::optional<base::Value> youtube_restrict) {
-  PolicyMap policies;
-  SetPolicy(&policies, key::kForceSafeSearch, std::move(legacy_safe_search));
-  SetPolicy(&policies, key::kForceGoogleSafeSearch,
-            std::move(google_safe_search));
-  SetPolicy(&policies, key::kForceYouTubeSafetyMode, std::move(legacy_youtube));
-  SetPolicy(&policies, key::kForceYouTubeRestrict, std::move(youtube_restrict));
-  UpdateProviderPolicy(policies);
-}
-
-// static
-GURL PolicyTest::GetExpectedSearchURL(bool expect_safe_search) {
-  std::string expected_url("http://google.com/");
-  if (expect_safe_search) {
-    expected_url += "?" +
-                    std::string(safe_search_util::kSafeSearchSafeParameter) +
-                    "&" + safe_search_util::kSafeSearchSsuiParameter;
-  }
-  return GURL(expected_url);
-}
-
-// static
-void PolicyTest::CheckSafeSearch(Browser* browser,
-                                 bool expect_safe_search,
-                                 const std::string& url) {
-  content::WebContents* web_contents =
-      browser->tab_strip_model()->GetActiveWebContents();
-  content::TestNavigationObserver observer(web_contents);
-  ui_test_utils::SendToOmniboxAndSubmit(browser, url);
-  observer.Wait();
-  OmniboxEditModel* model =
-      browser->window()->GetLocationBar()->GetOmniboxView()->model();
-  EXPECT_TRUE(model->CurrentMatch(nullptr).destination_url.is_valid());
-  EXPECT_EQ(GetExpectedSearchURL(expect_safe_search), web_contents->GetURL());
-}
-
 // static
 bool PolicyTest::FetchSubresource(content::WebContents* web_contents,
                                   const GURL& url) {
diff --git a/chrome/browser/policy/policy_test_utils.h b/chrome/browser/policy/policy_test_utils.h
index 7c11633..0b6e9121 100644
--- a/chrome/browser/policy/policy_test_utils.h
+++ b/chrome/browser/policy/policy_test_utils.h
@@ -5,8 +5,6 @@
 #ifndef CHROME_BROWSER_POLICY_POLICY_TEST_UTILS_H_
 #define CHROME_BROWSER_POLICY_POLICY_TEST_UTILS_H_
 
-#include <string>
-
 #include "base/files/file_path.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/test/base/chrome_test_utils.h"
@@ -14,8 +12,6 @@
 #include "components/security_interstitials/core/controller_client.h"
 #include "url/gurl.h"
 
-class Browser;
-
 namespace content {
 class WebContents;
 }  // namespace content
@@ -45,8 +41,6 @@
   PolicyTest();
   ~PolicyTest() override;
 
-  void SetUp() override;
-
   void SetUpInProcessBrowserTestFixture() override;
 
   void SetUpOnMainThread() override;
@@ -64,21 +58,10 @@
                         const char* key,
                         absl::optional<base::Value> value);
 
-  void ApplySafeSearchPolicy(absl::optional<base::Value> legacy_safe_search,
-                             absl::optional<base::Value> google_safe_search,
-                             absl::optional<base::Value> legacy_youtube,
-                             absl::optional<base::Value> youtube_restrict);
-
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   void TestScreenshotFile(bool enabled);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-  static GURL GetExpectedSearchURL(bool expect_safe_search);
-
-  static void CheckSafeSearch(Browser* browser,
-                              bool expect_safe_search,
-                              const std::string& url = "http://google.com/");
-
   static bool FetchSubresource(content::WebContents* web_contents,
                                const GURL& url);
 
diff --git a/chrome/browser/policy/safe_search_policy_test.cc b/chrome/browser/policy/safe_search_policy_test.cc
new file mode 100644
index 0000000..f29c5730
--- /dev/null
+++ b/chrome/browser/policy/safe_search_policy_test.cc
@@ -0,0 +1,62 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/policy/safe_search_policy_test.h"
+
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/location_bar/location_bar.h"
+#include "chrome/common/net/safe_search_util.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/omnibox/browser/omnibox_edit_model.h"
+#include "components/policy/policy_constants.h"
+#include "content/public/test/test_navigation_observer.h"
+
+namespace policy {
+
+SafeSearchPolicyTest::SafeSearchPolicyTest() = default;
+
+SafeSearchPolicyTest::~SafeSearchPolicyTest() = default;
+
+void SafeSearchPolicyTest::ApplySafeSearchPolicy(
+    absl::optional<base::Value> legacy_safe_search,
+    absl::optional<base::Value> google_safe_search,
+    absl::optional<base::Value> legacy_youtube,
+    absl::optional<base::Value> youtube_restrict) {
+  PolicyMap policies;
+  SetPolicy(&policies, key::kForceSafeSearch, std::move(legacy_safe_search));
+  SetPolicy(&policies, key::kForceGoogleSafeSearch,
+            std::move(google_safe_search));
+  SetPolicy(&policies, key::kForceYouTubeSafetyMode, std::move(legacy_youtube));
+  SetPolicy(&policies, key::kForceYouTubeRestrict, std::move(youtube_restrict));
+  UpdateProviderPolicy(policies);
+}
+
+// static
+GURL SafeSearchPolicyTest::GetExpectedSearchURL(bool expect_safe_search) {
+  std::string expected_url("http://google.com/");
+  if (expect_safe_search) {
+    expected_url += "?" +
+                    std::string(safe_search_util::kSafeSearchSafeParameter) +
+                    "&" + safe_search_util::kSafeSearchSsuiParameter;
+  }
+  return GURL(expected_url);
+}
+
+// static
+void SafeSearchPolicyTest::CheckSafeSearch(Browser* browser,
+                                           bool expect_safe_search,
+                                           const std::string& url) {
+  content::WebContents* web_contents =
+      browser->tab_strip_model()->GetActiveWebContents();
+  content::TestNavigationObserver observer(web_contents);
+  ui_test_utils::SendToOmniboxAndSubmit(browser, url);
+  observer.Wait();
+  OmniboxEditModel* model =
+      browser->window()->GetLocationBar()->GetOmniboxView()->model();
+  EXPECT_TRUE(model->CurrentMatch(nullptr).destination_url.is_valid());
+  EXPECT_EQ(GetExpectedSearchURL(expect_safe_search), web_contents->GetURL());
+}
+
+}  // namespace policy
diff --git a/chrome/browser/policy/safe_search_policy_test.h b/chrome/browser/policy/safe_search_policy_test.h
new file mode 100644
index 0000000..3936314
--- /dev/null
+++ b/chrome/browser/policy/safe_search_policy_test.h
@@ -0,0 +1,39 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_POLICY_SAFE_SEARCH_POLICY_TEST_H_
+#define CHROME_BROWSER_POLICY_SAFE_SEARCH_POLICY_TEST_H_
+
+#include <string>
+
+#include "base/values.h"
+#include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/test/base/chrome_test_utils.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/gurl.h"
+
+class Browser;
+
+namespace policy {
+
+class SafeSearchPolicyTest : public PolicyTest {
+ protected:
+  SafeSearchPolicyTest();
+  ~SafeSearchPolicyTest() override;
+
+  void ApplySafeSearchPolicy(absl::optional<base::Value> legacy_safe_search,
+                             absl::optional<base::Value> google_safe_search,
+                             absl::optional<base::Value> legacy_youtube,
+                             absl::optional<base::Value> youtube_restrict);
+
+  static GURL GetExpectedSearchURL(bool expect_safe_search);
+
+  static void CheckSafeSearch(Browser* browser,
+                              bool expect_safe_search,
+                              const std::string& url = "http://google.com/");
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_POLICY_SAFE_SEARCH_POLICY_TEST_H_
diff --git a/chrome/browser/policy/test/force_google_safe_search_policy_browsertest.cc b/chrome/browser/policy/test/force_google_safe_search_policy_browsertest.cc
index b5b39fcc..b8400e2f 100644
--- a/chrome/browser/policy/test/force_google_safe_search_policy_browsertest.cc
+++ b/chrome/browser/policy/test/force_google_safe_search_policy_browsertest.cc
@@ -7,7 +7,7 @@
 #include "base/synchronization/lock.h"
 #include "base/test/bind.h"
 #include "base/values.h"
-#include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/browser/policy/safe_search_policy_test.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/net/safe_search_util.h"
@@ -26,7 +26,7 @@
 
 namespace policy {
 
-IN_PROC_BROWSER_TEST_F(PolicyTest, LegacySafeSearch) {
+IN_PROC_BROWSER_TEST_F(SafeSearchPolicyTest, LegacySafeSearch) {
   static_assert(safe_search_util::YOUTUBE_RESTRICT_OFF == 0 &&
                     safe_search_util::YOUTUBE_RESTRICT_MODERATE == 1 &&
                     safe_search_util::YOUTUBE_RESTRICT_STRICT == 2 &&
@@ -111,7 +111,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(PolicyTest, ForceGoogleSafeSearch) {
+IN_PROC_BROWSER_TEST_F(SafeSearchPolicyTest, ForceGoogleSafeSearch) {
   base::Lock lock;
   std::set<GURL> google_urls_requested;
   content::URLLoaderInterceptor interceptor(base::BindLambdaForTesting(
diff --git a/chrome/browser/policy/test/policy_test_google_browsertest.cc b/chrome/browser/policy/test/policy_test_google_browsertest.cc
index 4126e5d..5d0b49e 100644
--- a/chrome/browser/policy/test/policy_test_google_browsertest.cc
+++ b/chrome/browser/policy/test/policy_test_google_browsertest.cc
@@ -9,7 +9,7 @@
 #include "base/synchronization/lock.h"
 #include "base/test/bind.h"
 #include "base/values.h"
-#include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/browser/policy/safe_search_policy_test.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/net/safe_search_util.h"
@@ -57,7 +57,7 @@
   EXPECT_EQ(header, allowed_domain);
 }
 
-class PolicyTestGoogle : public PolicyTest {
+class PolicyTestGoogle : public SafeSearchPolicyTest {
  public:
   PolicyTestGoogle() : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
 
@@ -70,7 +70,7 @@
 
  private:
   void SetUpOnMainThread() override {
-    PolicyTest::SetUpOnMainThread();
+    SafeSearchPolicyTest::SetUpOnMainThread();
 
     https_server_.AddDefaultHandlers(GetChromeTestDataDir());
 
@@ -87,6 +87,8 @@
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
+    SafeSearchPolicyTest::SetUpCommandLine(command_line);
+
     // Note for the google and youtube tests below, the throttles expect that
     // the URLs are to google.com or youtube.com. Networking code also
     // automatically upgrades http requests to these domains to https (see the
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js
index 64223ee..f13741f 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js
@@ -301,7 +301,6 @@
     }
 
     this.state_ = Dictation.DictationState.LISTENING;
-    // Display the "....".
     this.clearInterimText_();
 
     // Record metrics.
@@ -368,7 +367,6 @@
 
   /**
    * Shows the interim result in the UI.
-   * TODO(crbug.com/1252037): Implement with final design instead of input.ime.
    * @param {string} text
    * @private
    */
@@ -383,7 +381,7 @@
     // although SODA does not seem to do that. The newline character looks wrong
     // here.
     this.interimText_ = text;
-    this.inputController_.showAnnotation(this.interimText_);
+    this.inputController_.showBubble(this.interimText_);
     if (this.clearUITextTimeoutId_) {
       clearTimeout(this.clearUITextTimeoutId_);
       this.clearUITextTimeoutId_ = null;
@@ -391,8 +389,7 @@
   }
 
   /**
-   * Clears the interim result in the UI, replacing it with '....'.
-   * TODO(crbug.com/1252037): Implement with final design instead of input.ime.
+   * Clears the interim result in the UI.
    * @private
    */
   clearInterimText_() {
@@ -402,7 +399,7 @@
     }
 
     this.interimText_ = '';
-    this.inputController_.showAnnotation('....');
+    this.inputController_.showBubble('');
     if (this.clearUITextTimeoutId_) {
       clearTimeout(this.clearUITextTimeoutId_);
       this.clearUITextTimeoutId_ = null;
@@ -412,7 +409,6 @@
   /**
    * Shows that a macro was executed in the UI by putting a checkmark next to
    * the transcript.
-   * TODO(crbug.com/1252037): Implement with final design instead of input.ime.
    * @param {Macro} macro
    * @param {string} transcript
    * @private
@@ -425,12 +421,11 @@
 
     if (macro.getMacroName() === MacroName.INPUT_TEXT_VIEW ||
         macro.getMacroName() === MacroName.NEW_LINE) {
-      // Return to the '....' UI.
       this.clearInterimText_();
       return;
     }
     this.interimText_ = '';
-    this.inputController_.showAnnotation('☑' + transcript);
+    this.inputController_.showBubble('☑' + transcript);
     this.clearUITextTimeoutId_ = setTimeout(
         () => this.clearInterimText_(),
         Dictation.Timeouts.SHOW_COMMAND_MESSAGE_MS);
@@ -438,7 +433,6 @@
 
   /**
    * Shows a message in the UI that a command failed to execute.
-   * TODO(crbug.com/1252037): Implement with final design instead of input.ime.
    * TODO(crbug.com/1252037): Optionally use the MacroError to provide
    * additional context.
    * @param {string} transcript The user's spoken transcript, shown so they
@@ -454,7 +448,7 @@
 
     this.interimText_ = '';
     // TODO(crbug.com/1252037): Finalize string and internationalization.
-    this.inputController_.showAnnotation(`ⓘ Failed to execute: ` + transcript);
+    this.inputController_.showBubble(`ⓘ Failed to execute: ` + transcript);
     this.clearUITextTimeoutId_ = setTimeout(
         () => this.clearInterimText_(),
         Dictation.Timeouts.SHOW_COMMAND_MESSAGE_MS);
@@ -462,7 +456,6 @@
 
   /**
    * Hides the commands UI bubble.
-   * TODO(crbug.com/1252037): Implement with final design instead of input.ime.
    * @private
    */
   hideCommandsUI_() {
@@ -471,7 +464,7 @@
     }
 
     this.interimText_ = '';
-    this.inputController_.hideAnnotation();
+    this.inputController_.hideBubble();
     if (this.clearUITextTimeoutId_) {
       clearTimeout(this.clearUITextTimeoutId_);
       this.clearUITextTimeoutId_ = null;
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation_test.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation_test.js
index 026c358..af4e65b 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation_test.js
@@ -398,7 +398,7 @@
           assertFalse(!!this.mockInputIme.getLastCommittedParameters());
         }
 
-        // Try a command to "type delete", etc.
+        // Try to type the command e.g. "type delete".
         this.mockSpeechRecognitionPrivate.fireMockOnResultEvent(
             'type ' + command, true);
         // The command should be entered but not the word "type".
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js
index 1f3c01fe..867c700d 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js
@@ -133,41 +133,16 @@
   }
 
   /**
-   * Shows an annotation in the candidate window.
-   * TODO(crbug.com/1252037): After implementing final UX design, remove
-   * this method from InputController.
-   * @param {string} annotation
+   * Shows the bubble UI with the given text.
+   * @param {string} text
    */
-  showAnnotation(annotation) {
-    chrome.input.ime.setCandidateWindowProperties({
-      engineID: InputController.IME_ENGINE_ID,
-      properties: {
-        cursorVisible: true,
-        currentCandidateIndex: 0,
-        vertical: false,
-        visible: true,
-        windowPosition: 'cursor',
-        pageSize: 1
-      }
-    });
-    chrome.input.ime.setCandidates(
-        {
-          candidates: [{candidate: '', annotation, id: 1}],
-          contextID: this.activeImeContextId_,
-        },
-        (success) => {});
+  showBubble(text) {
+    chrome.accessibilityPrivate.updateDictationBubble(/*visible=*/ true, text);
   }
 
-  /**
-   * Hides the annotation and candidate window.
-   * TODO(crbug.com/1252037): After implementing final UX design, remove
-   * this method from InputController.
-   */
-  hideAnnotation() {
-    chrome.input.ime.setCandidateWindowProperties({
-      engineID: InputController.IME_ENGINE_ID,
-      properties: {visible: false}
-    });
+  /** Hides the bubble UI. */
+  hideBubble() {
+    chrome.accessibilityPrivate.updateDictationBubble(/*visible=*/ false);
   }
 
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/common/testing/mock_accessibility_private.js b/chrome/browser/resources/chromeos/accessibility/common/testing/mock_accessibility_private.js
index a0f3780..805ad88 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/testing/mock_accessibility_private.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/testing/mock_accessibility_private.js
@@ -61,6 +61,12 @@
   /** @private {boolean} */
   dictationActivated_: false,
 
+  /** @private {boolean} */
+  dictationBubbleVisible_: false,
+
+  /** @private {?string} */
+  dictationBubbleText_: null,
+
   /** @private {Set<string>} */
   enabledFeatures_: new Set(),
 
@@ -320,6 +326,11 @@
     return MockAccessibilityPrivate.dictationActivated_;
   },
 
+  updateDictationBubble(visible, text) {
+    MockAccessibilityPrivate.dictationBubbleVisible_ = visible;
+    MockAccessibilityPrivate.dictationBubbleText_ = text || null;
+  },
+
   /**
    * Enables or disables a feature for testing, causing
    * MockAccessibilityPrivate.isFeatureEnabled to consider it enabled.
diff --git a/chrome/browser/resources/welcome/BUILD.gn b/chrome/browser/resources/welcome/BUILD.gn
index f0311f6..9de602d 100644
--- a/chrome/browser/resources/welcome/BUILD.gn
+++ b/chrome/browser/resources/welcome/BUILD.gn
@@ -8,6 +8,7 @@
 import("//tools/polymer/html_to_js.gni")
 import("//tools/typescript/ts_library.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
+import("welcome.gni")
 
 assert(!is_chromeos_ash && !is_android)
 
@@ -34,7 +35,7 @@
   ]
   input_files_base_dir = rebase_path(".", "//")
 
-  deps = [ ":build" ]
+  deps = [ ":build_ts" ]
   manifest_files = [ "$target_gen_dir/tsconfig.manifest" ]
 
   if (is_chrome_branded) {
@@ -69,42 +70,14 @@
 preprocess_if_expr("preprocess") {
   in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = [
-    "google_apps/google_app_proxy.ts",
-    "google_apps/google_apps_metrics_proxy.ts",
-    "landing_view_proxy.ts",
-    "navigation_mixin.ts",
-    "ntp_background/ntp_background_metrics_proxy.ts",
-    "ntp_background/ntp_background_proxy.ts",
-    "set_as_default/nux_set_as_default_proxy.ts",
-    "shared/bookmark_proxy.ts",
-    "shared/module_metrics_proxy.ts",
-    "shared/nux_types.ts",
-    "signin_view_proxy.ts",
-    "welcome_browser_proxy.ts",
-    "welcome.ts",
-  ]
+  in_files = non_web_component_files
 }
 
 preprocess_if_expr("preprocess_generated") {
   deps = [ ":web_components" ]
   in_folder = target_gen_dir
   out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = [
-    "google_apps/nux_google_apps.ts",
-    "landing_view.ts",
-    "ntp_background/nux_ntp_background.ts",
-    "set_as_default/nux_set_as_default.ts",
-    "shared/action_link_style_css.ts",
-    "shared/animations_css.ts",
-    "shared/chooser_shared_css.ts",
-    "shared/navi_colors_css.ts",
-    "shared/onboarding_background.ts",
-    "shared/splash_pages_shared_css.ts",
-    "shared/step_indicator.ts",
-    "signin_view.ts",
-    "welcome_app.ts",
-  ]
+  in_files = web_component_files
 }
 
 grit("resources") {
@@ -124,57 +97,16 @@
   output_dir = "$root_gen_dir/chrome"
 }
 
-group("web_components") {
-  public_deps = [
-    ":web_components_local",
-    "./google_apps:web_components",
-    "./ntp_background:web_components",
-    "./set_as_default:web_components",
-    "./shared:web_components",
-  ]
+html_to_js("web_components") {
+  js_files = web_component_files
 }
 
-html_to_js("web_components_local") {
-  js_files = [
-    "landing_view.ts",
-    "signin_view.ts",
-    "welcome_app.ts",
-  ]
-}
-
-ts_library("build") {
+ts_library("build_ts") {
   root_dir = "$target_gen_dir/$preprocess_folder"
-  out_dir = "$target_gen_dir/$preprocess_folder"
+  out_dir = "$target_gen_dir/tsc"
   composite = true
   tsconfig_base = "tsconfig_base.json"
-  in_files = [
-    "google_apps/google_app_proxy.ts",
-    "google_apps/google_apps_metrics_proxy.ts",
-    "google_apps/nux_google_apps.ts",
-    "landing_view.ts",
-    "landing_view_proxy.ts",
-    "navigation_mixin.ts",
-    "ntp_background/ntp_background_metrics_proxy.ts",
-    "ntp_background/ntp_background_proxy.ts",
-    "ntp_background/nux_ntp_background.ts",
-    "set_as_default/nux_set_as_default.ts",
-    "set_as_default/nux_set_as_default_proxy.ts",
-    "shared/action_link_style_css.ts",
-    "shared/animations_css.ts",
-    "shared/bookmark_proxy.ts",
-    "shared/chooser_shared_css.ts",
-    "shared/module_metrics_proxy.ts",
-    "shared/navi_colors_css.ts",
-    "shared/nux_types.ts",
-    "shared/onboarding_background.ts",
-    "shared/splash_pages_shared_css.ts",
-    "shared/step_indicator.ts",
-    "signin_view.ts",
-    "signin_view_proxy.ts",
-    "welcome_app.ts",
-    "welcome_browser_proxy.ts",
-    "welcome.ts",
-  ]
+  in_files = web_component_files + non_web_component_files
   definitions = [
     "//tools/typescript/definitions/bookmarks.d.ts",
     "//tools/typescript/definitions/chrome_event.d.ts",
diff --git a/chrome/browser/resources/welcome/google_apps/BUILD.gn b/chrome/browser/resources/welcome/google_apps/BUILD.gn
deleted file mode 100644
index 1dbefe6..0000000
--- a/chrome/browser/resources/welcome/google_apps/BUILD.gn
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//tools/polymer/html_to_js.gni")
-
-html_to_js("web_components") {
-  js_files = [ "nux_google_apps.ts" ]
-}
diff --git a/chrome/browser/resources/welcome/ntp_background/BUILD.gn b/chrome/browser/resources/welcome/ntp_background/BUILD.gn
deleted file mode 100644
index 2aebd7c4..0000000
--- a/chrome/browser/resources/welcome/ntp_background/BUILD.gn
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//tools/polymer/html_to_js.gni")
-
-html_to_js("web_components") {
-  js_files = [ "nux_ntp_background.ts" ]
-}
diff --git a/chrome/browser/resources/welcome/set_as_default/BUILD.gn b/chrome/browser/resources/welcome/set_as_default/BUILD.gn
deleted file mode 100644
index 1b06844..0000000
--- a/chrome/browser/resources/welcome/set_as_default/BUILD.gn
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//tools/polymer/html_to_js.gni")
-
-html_to_js("web_components") {
-  js_files = [ "nux_set_as_default.ts" ]
-}
diff --git a/chrome/browser/resources/welcome/shared/BUILD.gn b/chrome/browser/resources/welcome/shared/BUILD.gn
deleted file mode 100644
index b147ab0..0000000
--- a/chrome/browser/resources/welcome/shared/BUILD.gn
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//tools/polymer/html_to_js.gni")
-
-html_to_js("web_components") {
-  js_files = [
-    "action_link_style_css.ts",
-    "animations_css.ts",
-    "chooser_shared_css.ts",
-    "navi_colors_css.ts",
-    "onboarding_background.ts",
-    "splash_pages_shared_css.ts",
-    "step_indicator.ts",
-  ]
-}
diff --git a/chrome/browser/resources/welcome/welcome.gni b/chrome/browser/resources/welcome/welcome.gni
new file mode 100644
index 0000000..64710ca
--- /dev/null
+++ b/chrome/browser/resources/welcome/welcome.gni
@@ -0,0 +1,37 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# List of files that don't need to be passed to html_to_js().
+non_web_component_files = [
+  "google_apps/google_app_proxy.ts",
+  "google_apps/google_apps_metrics_proxy.ts",
+  "landing_view_proxy.ts",
+  "navigation_mixin.ts",
+  "ntp_background/ntp_background_metrics_proxy.ts",
+  "ntp_background/ntp_background_proxy.ts",
+  "set_as_default/nux_set_as_default_proxy.ts",
+  "shared/bookmark_proxy.ts",
+  "shared/module_metrics_proxy.ts",
+  "shared/nux_types.ts",
+  "signin_view_proxy.ts",
+  "welcome_browser_proxy.ts",
+  "welcome.ts",
+]
+
+# List of files that should be passed to html_to_js().
+web_component_files = [
+  "google_apps/nux_google_apps.ts",
+  "landing_view.ts",
+  "ntp_background/nux_ntp_background.ts",
+  "set_as_default/nux_set_as_default.ts",
+  "shared/action_link_style_css.ts",
+  "shared/animations_css.ts",
+  "shared/chooser_shared_css.ts",
+  "shared/navi_colors_css.ts",
+  "shared/onboarding_background.ts",
+  "shared/splash_pages_shared_css.ts",
+  "shared/step_indicator.ts",
+  "signin_view.ts",
+  "welcome_app.ts",
+]
diff --git a/chrome/browser/segmentation_platform/segmentation_platform_config.cc b/chrome/browser/segmentation_platform/segmentation_platform_config.cc
index d3ec5d6..4b0d6b99 100644
--- a/chrome/browser/segmentation_platform/segmentation_platform_config.cc
+++ b/chrome/browser/segmentation_platform/segmentation_platform_config.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/flags/android/cached_feature_flags.h"
 #include "chrome/browser/flags/android/chrome_feature_list.h"
 #include "chrome/browser/ui/android/start_surface/start_surface_android.h"
+#include "components/query_tiles/switches.h"
 #endif
 
 using optimization_guide::proto::OptimizationTarget;
@@ -31,6 +32,7 @@
 constexpr int kDummyFeatureSelectionTTLDays = 1;
 
 #if defined(OS_ANDROID)
+
 constexpr int kAdaptiveToolbarDefaultSelectionTTLDays = 28;
 
 constexpr int kChromeStartDefaultSelectionTTLDays = 30;
@@ -38,6 +40,13 @@
 
 constexpr int kChromeLowUserEngagementSelectionTTLDays = 30;
 
+// See
+// https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/query_tiles/QueryTileUtils.java
+const char kNumDaysKeepShowingQueryTiles[] =
+    "num_days_keep_showing_query_tiles";
+const char kNumDaysMVCkicksBelowThreshold[] =
+    "num_days_mv_clicks_below_threshold";
+
 // DEFAULT_NUM_DAYS_KEEP_SHOWING_QUERY_TILES
 constexpr int kQueryTilesDefaultSelectionTTLDays = 28;
 // DEFAULT_NUM_DAYS_MV_CLICKS_BELOW_THRESHOLD
@@ -103,10 +112,15 @@
   config->segment_ids = {
       OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES,
   };
-  // TODO(ssid): use experiment params to configure these.
-  config->segment_selection_ttl =
-      base::Days(kQueryTilesDefaultSelectionTTLDays);
-  config->unknown_selection_ttl = base::Days(kQueryTilesDefaultUnknownTTLDays);
+
+  int segment_selection_ttl_days = base::GetFieldTrialParamByFeatureAsInt(
+      query_tiles::features::kQueryTilesSegmentation,
+      kNumDaysKeepShowingQueryTiles, kQueryTilesDefaultSelectionTTLDays);
+  int unknown_selection_ttl_days = base::GetFieldTrialParamByFeatureAsInt(
+      query_tiles::features::kQueryTilesSegmentation,
+      kNumDaysMVCkicksBelowThreshold, kQueryTilesDefaultUnknownTTLDays);
+  config->segment_selection_ttl = base::Days(segment_selection_ttl_days);
+  config->unknown_selection_ttl = base::Days(unknown_selection_ttl_days);
   return config;
 }
 
diff --git a/chrome/browser/sessions/session_service_factory.cc b/chrome/browser/sessions/session_service_factory.cc
index 03eb006..3ff9118 100644
--- a/chrome/browser/sessions/session_service_factory.cc
+++ b/chrome/browser/sessions/session_service_factory.cc
@@ -10,9 +10,17 @@
 #include "chrome/browser/sessions/session_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 
+namespace {
+
+bool ShouldUseSessionServiceForProfile(const Profile& profile) {
+  return profile.IsRegularProfile();
+}
+
+}  // namespace
+
 // static
 SessionService* SessionServiceFactory::GetForProfile(Profile* profile) {
-  if (profile->IsOffTheRecord() || profile->IsGuestSession())
+  if (!ShouldUseSessionServiceForProfile(*profile))
     return nullptr;
 
   return static_cast<SessionService*>(
@@ -41,6 +49,9 @@
 
 // static
 void SessionServiceFactory::ShutdownForProfile(Profile* profile) {
+  if (!ShouldUseSessionServiceForProfile(*profile))
+    return;
+
   if (SessionDataServiceFactory::GetForProfile(profile))
     SessionDataServiceFactory::GetForProfile(profile)->StartCleanup();
 
@@ -73,6 +84,9 @@
 
 KeyedService* SessionServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* profile) const {
+  if (!ShouldUseSessionServiceForProfile(*static_cast<Profile*>(profile)))
+    return nullptr;
+
   SessionService* service = new SessionService(static_cast<Profile*>(profile));
   service->ResetFromCurrentBrowsers();
   return service;
diff --git a/chrome/browser/sharing_hub/sharing_hub_features.cc b/chrome/browser/sharing_hub/sharing_hub_features.cc
index 5dbf1bdc..96dcc4e 100644
--- a/chrome/browser/sharing_hub/sharing_hub_features.cc
+++ b/chrome/browser/sharing_hub/sharing_hub_features.cc
@@ -16,7 +16,8 @@
 
 namespace {
 
-bool IsEnterprisePolicyEnabled(content::BrowserContext* context) {
+// Whether the sharing hub feature should be disabled by policy.
+bool SharingHubDisabledByPolicy(content::BrowserContext* context) {
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
   const PrefService* prefs = Profile::FromBrowserContext(context)->GetPrefs();
   return prefs->GetBoolean(prefs::kDesktopSharingHubEnabled);
@@ -35,15 +36,8 @@
 
 }  // namespace
 
-bool SharingHubAppMenuEnabled(content::BrowserContext* context) {
-  return base::FeatureList::IsEnabled(kSharingHubDesktopAppMenu) &&
-         IsEnterprisePolicyEnabled(context);
-}
-
 bool SharingHubOmniboxEnabled(content::BrowserContext* context) {
-  return (base::FeatureList::IsEnabled(kSharingHubDesktopOmnibox) ||
-          share::AreUpcomingSharingFeaturesEnabled()) &&
-         IsEnterprisePolicyEnabled(context);
+  return !SharingHubDisabledByPolicy(context);
 }
 
 bool DesktopScreenshotsFeatureEnabled(content::BrowserContext* context) {
@@ -52,12 +46,6 @@
          !ScreenshotsDisabledByPolicy(context);
 }
 
-const base::Feature kSharingHubDesktopAppMenu{
-    "SharingHubDesktopAppMenu", base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kSharingHubDesktopOmnibox{"SharingHubDesktopOmnibox",
-                                              base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kDesktopScreenshots{"DesktopScreenshots",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/sharing_hub/sharing_hub_features.h b/chrome/browser/sharing_hub/sharing_hub_features.h
index ef5aa78..f696fe61 100644
--- a/chrome/browser/sharing_hub/sharing_hub_features.h
+++ b/chrome/browser/sharing_hub/sharing_hub_features.h
@@ -16,10 +16,6 @@
 
 namespace sharing_hub {
 
-// Returns true if the app menu sharing hub is enabled for |context|. Only for
-// Windows/Mac/Linux.
-bool SharingHubAppMenuEnabled(content::BrowserContext* context);
-
 // Returns true if the omnibox sharing hub is enabled for |context|. Only for
 // Windows/Mac/Linux.
 bool SharingHubOmniboxEnabled(content::BrowserContext* context);
@@ -30,13 +26,6 @@
 // image editor before sharing.
 bool DesktopScreenshotsFeatureEnabled(content::BrowserContext* context);
 
-// Feature flag to enable the 3-dot menu entry point for the desktop sharing
-// hub.
-extern const base::Feature kSharingHubDesktopAppMenu;
-
-// Feature flag to enable the omnibox entry point for the desktop sharing hub.
-extern const base::Feature kSharingHubDesktopOmnibox;
-
 // Feature flag to enable the screenshots feature, currently accessed only
 // through the sharing hub.
 extern const base::Feature kDesktopScreenshots;
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 29090522..47d5810 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -1991,7 +1991,10 @@
 
 // Visit a HTTPS page which requires client cert authentication. The client
 // cert will be selected automatically, then a test which uses WebSocket runs.
-IN_PROC_BROWSER_TEST_F(SSLUITestWithClientCert, TestWSSClientCert) {
+//
+// TODO(https://crbug.com/1279930): disabled because of race in when certs
+// are incorporated.
+IN_PROC_BROWSER_TEST_F(SSLUITestWithClientCert, DISABLED_TestWSSClientCert) {
   // Import a client cert for test.
   crypto::ScopedPK11Slot public_slot = cert_db_->GetPublicSlot();
   std::string pkcs12_data;
@@ -2213,7 +2216,10 @@
   EXPECT_EQ("", tab->GetLastCommittedURL().ref());
 }
 
-IN_PROC_BROWSER_TEST_F(SSLUITest, TestCertDBChangedFlushesClientAuthCache) {
+// TODO(https://crbug.com/1279930): disabled because of race in when certs
+// are incorporated.
+IN_PROC_BROWSER_TEST_F(SSLUITest,
+                       DISABLED_TestCertDBChangedFlushesClientAuthCache) {
   // Make the browser use the ClientCertStoreStub instead of the regular one.
   ProfileNetworkContextServiceFactory::GetForContext(browser()->profile())
       ->set_client_cert_store_factory_for_testing(
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index dd4a0d5..9947982 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3890,8 +3890,6 @@
       "sharing_hub/sharing_hub_bubble_controller.cc",
       "sharing_hub/sharing_hub_bubble_controller.h",
       "sharing_hub/sharing_hub_bubble_view.h",
-      "sharing_hub/sharing_hub_sub_menu_model.cc",
-      "sharing_hub/sharing_hub_sub_menu_model.h",
 
       # This test header is included because it contains forward declarations
       # needed for "friend" statements for use in tests.
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
index 00d4a8e..bc9800f 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
@@ -683,7 +683,9 @@
             return;
         }
         resetCustomIconsStatus();
-        StatusIconResource storeIconResource = new StatusIconResource(drawable);
+        // Use {@link PermissionIconResource} instead of {@link StatusIconResource} to encapsulate
+        // the icon with a circle background.
+        StatusIconResource storeIconResource = new PermissionIconResource(drawable, false);
         storeIconResource.setTransitionType(IconTransitionType.ROTATE);
         storeIconResource.setAnimationFinishedCallback(() -> {
             if (canShowIph) {
diff --git a/chrome/browser/ui/ash/cast_config_controller_media_router.cc b/chrome/browser/ui/ash/cast_config_controller_media_router.cc
index b173d13..3b947aa 100644
--- a/chrome/browser/ui/ash/cast_config_controller_media_router.cc
+++ b/chrome/browser/ui/ash/cast_config_controller_media_router.cc
@@ -92,8 +92,7 @@
   void OnSinksReceived(const MediaSinks& sinks) override;
 
   // media_router::MediaRoutesObserver:
-  void OnRoutesUpdated(const MediaRoutes& routes,
-                       const MediaRouteIds& unused_joinable_route_ids) override;
+  void OnRoutesUpdated(const MediaRoutes& routes) override;
 
   MediaSinks sinks_;
   MediaRoutes routes_;
@@ -138,9 +137,7 @@
   update_devices_callback_.Run();
 }
 
-void CastDeviceCache::OnRoutesUpdated(
-    const MediaRoutes& routes,
-    const MediaRouteIds& unused_joinable_route_ids) {
+void CastDeviceCache::OnRoutesUpdated(const MediaRoutes& routes) {
   routes_ = routes;
   update_devices_callback_.Run();
 }
diff --git a/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc b/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
index a60b5e4..91d9919 100644
--- a/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
+++ b/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
@@ -249,10 +249,8 @@
   }
 
   void LaunchTemplate(const base::GUID& uuid) {
-    ash::DeskSwitchAnimationWaiter waiter;
     DesksTemplatesClient::Get()->LaunchDeskTemplate(uuid.AsLowercaseString(),
                                                     base::DoNothing());
-    waiter.Wait();
   }
 
   void SetAndLaunchTemplate(std::unique_ptr<ash::DeskTemplate> desk_template) {
@@ -1043,14 +1041,8 @@
   ASSERT_TRUE(template_item);
   ClickButton(template_item);
 
-  // Clicking the button is a two part, both async process. We need to wait for
-  // the template to be fetched from the model, and then wait for the desk
-  // animation to be launched.
-  // TODO(dandersson): Remove this when the desk is no longer activated on
-  // template launch.
+  // We need to wait for the template to be fetched from the model.
   ash::WaitForDesksTemplatesUI();
-  ash::DeskSwitchAnimationWaiter waiter;
-  waiter.Wait();
 
   // Wait for the tabs to load.
   content::RunAllTasksUntilIdle();
@@ -1118,14 +1110,8 @@
   ASSERT_TRUE(template_item);
   ClickButton(template_item);
 
-  // Clicking the button is a two part, both async process. We need to wait for
-  // the template to be fetched from the model, and then wait for the desk
-  // animation to be launched.
-  // TODO(dandersson): Remove this when the desk is no longer activated on
-  // template launch.
+  // We need to wait for the template to be fetched from the model.
   ash::WaitForDesksTemplatesUI();
-  ash::DeskSwitchAnimationWaiter waiter;
-  waiter.Wait();
 
   for (auto* browser : *BrowserList::GetInstance()) {
     aura::Window* window = browser->window()->GetNativeWindow();
diff --git a/chrome/browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc b/chrome/browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc
index 3f6e34e..5b70ebb 100644
--- a/chrome/browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc
+++ b/chrome/browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc
@@ -200,23 +200,20 @@
                                                               local_route};
 
   // We do not show the cast view for non-local routes.
-  media_routes_observer()->OnRoutesUpdated(
-      non_local_routes, std::vector<media_router::MediaRoute::Id>());
+  media_routes_observer()->OnRoutesUpdated(non_local_routes);
   content::RunAllPendingInMessageLoop();
   EXPECT_FALSE(IsCastingNotificationVisible());
 
   // If there are multiple routes active at the same time, then we need to
   // display the local route over a non-local route. This also verifies that we
   // display the cast view when we're casting.
-  media_routes_observer()->OnRoutesUpdated(
-      multiple_routes, std::vector<media_router::MediaRoute::Id>());
+  media_routes_observer()->OnRoutesUpdated(multiple_routes);
   content::RunAllPendingInMessageLoop();
   EXPECT_TRUE(IsCastingNotificationVisible());
   EXPECT_NE(std::u16string::npos, GetNotificationString().find(u"Local Sink"));
 
   // When a casting session stops, we shouldn't display the cast view.
-  media_routes_observer()->OnRoutesUpdated(
-      no_routes, std::vector<media_router::MediaRoute::Id>());
+  media_routes_observer()->OnRoutesUpdated(no_routes);
   content::RunAllPendingInMessageLoop();
   EXPECT_FALSE(IsCastingNotificationVisible());
 }
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index 408fcb87..33eff8b 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -1115,8 +1115,7 @@
   // Hosted app browser commands.
   const bool enable_copy_url =
       is_web_app_or_custom_tab ||
-      sharing_hub::SharingHubOmniboxEnabled(browser_->profile()) ||
-      sharing_hub::SharingHubAppMenuEnabled(browser_->profile());
+      sharing_hub::SharingHubOmniboxEnabled(browser_->profile());
   command_updater_.UpdateCommandEnabled(IDC_COPY_URL, enable_copy_url);
   command_updater_.UpdateCommandEnabled(IDC_OPEN_IN_CHROME,
                                         is_web_app_or_custom_tab);
diff --git a/chrome/browser/ui/color/BUILD.gn b/chrome/browser/ui/color/BUILD.gn
index 3c510ac..0eccf93 100644
--- a/chrome/browser/ui/color/BUILD.gn
+++ b/chrome/browser/ui/color/BUILD.gn
@@ -60,5 +60,8 @@
         "//components/crash/core/app:crash_export_thunks",
       ]
     }
+    if (is_chromeos_ash) {
+      deps += [ "//components/exo/wayland:weston_test_stub" ]
+    }
   }
 }
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model.cc b/chrome/browser/ui/content_settings/content_setting_image_model.cc
index 443bd63..f751a4e3 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_image_model.cc
@@ -78,6 +78,8 @@
 //   ContentSettingMediaImageModel              - media
 //   ContentSettingFramebustBlockImageModel     - blocked framebust
 
+constexpr bool kNotifyAccessibility = true;
+
 class ContentSettingBlockedImageModel : public ContentSettingSimpleImageModel {
  public:
   ContentSettingBlockedImageModel(ImageType image_type,
@@ -508,7 +510,7 @@
 // Geolocation -----------------------------------------------------------------
 
 ContentSettingGeolocationImageModel::ContentSettingGeolocationImageModel()
-    : ContentSettingImageModel(ImageType::GEOLOCATION) {}
+    : ContentSettingImageModel(ImageType::GEOLOCATION, kNotifyAccessibility) {}
 
 bool ContentSettingGeolocationImageModel::UpdateAndGetVisibility(
     WebContents* web_contents) {
@@ -581,6 +583,8 @@
   set_tooltip(l10n_util::GetStringUTF16(is_allowed
                                             ? IDS_ALLOWED_GEOLOCATION_MESSAGE
                                             : IDS_BLOCKED_GEOLOCATION_MESSAGE));
+  set_explanatory_string_id(is_allowed ? IDS_ALLOWED_GEOLOCATION_MESSAGE
+                                       : IDS_BLOCKED_GEOLOCATION_MESSAGE);
 
   return true;
 }
@@ -729,7 +733,7 @@
 // Media -----------------------------------------------------------------------
 
 ContentSettingMediaImageModel::ContentSettingMediaImageModel()
-    : ContentSettingImageModel(ImageType::MEDIASTREAM) {}
+    : ContentSettingImageModel(ImageType::MEDIASTREAM, kNotifyAccessibility) {}
 
 bool ContentSettingMediaImageModel::UpdateAndGetVisibility(
     WebContents* web_contents) {
@@ -834,6 +838,7 @@
                            : IDS_MICROPHONE_ACCESSED;
   }
   set_tooltip(l10n_util::GetStringUTF16(id));
+  set_explanatory_string_id(id);
 
   return true;
 }
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model_states.cc b/chrome/browser/ui/content_settings/content_setting_image_model_states.cc
index 431bd4c..848691c5 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model_states.cc
+++ b/chrome/browser/ui/content_settings/content_setting_image_model_states.cc
@@ -32,8 +32,6 @@
 void ContentSettingImageModelStates::SetAccessibilityNotified(ImageType type,
                                                               bool notified) {
   VerifyType(type);
-  // Currently only NOTIFICATIONS_QUIET_PROMPT will notify accessibility.
-  DCHECK_EQ(ImageType::NOTIFICATIONS_QUIET_PROMPT, type);
 
   accessibility_notified_[static_cast<int>(type)] = notified;
 }
@@ -41,8 +39,6 @@
 bool ContentSettingImageModelStates::GetAccessibilityNotified(
     ImageType type) const {
   VerifyType(type);
-  // Currently only NOTIFICATIONS_QUIET_PROMPT will notify accessibility.
-  DCHECK_EQ(ImageType::NOTIFICATIONS_QUIET_PROMPT, type);
 
   return accessibility_notified_[static_cast<int>(type)];
 }
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc
index 2a3a7a3..bf68c6af 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc
@@ -326,20 +326,23 @@
   content_settings->OnContentAllowed(ContentSettingsType::GEOLOCATION);
   UpdateModelAndVerifyStates(
       content_setting_image_model.get(), /* is_visible = */ true,
-      /* tooltip_empty = */ false, IDS_ALLOWED_GEOLOCATION_MESSAGE, 0);
+      /* tooltip_empty = */ false, IDS_ALLOWED_GEOLOCATION_MESSAGE,
+      IDS_ALLOWED_GEOLOCATION_MESSAGE);
 
   settings_map->SetDefaultContentSetting(ContentSettingsType::GEOLOCATION,
                                          CONTENT_SETTING_BLOCK);
   content_settings->OnContentBlocked(ContentSettingsType::GEOLOCATION);
   UpdateModelAndVerifyStates(
       content_setting_image_model.get(), /* is_visible = */ true,
-      /* tooltip_empty = */ false, IDS_BLOCKED_GEOLOCATION_MESSAGE, 0);
+      /* tooltip_empty = */ false, IDS_BLOCKED_GEOLOCATION_MESSAGE,
+      IDS_BLOCKED_GEOLOCATION_MESSAGE);
 
   geolocation_manager->SetSystemPermission(
       device::LocationSystemPermissionStatus::kDenied);
   UpdateModelAndVerifyStates(
       content_setting_image_model.get(), /* is_visible = */ true,
-      /* tooltip_empty = */ false, IDS_BLOCKED_GEOLOCATION_MESSAGE, 0);
+      /* tooltip_empty = */ false, IDS_BLOCKED_GEOLOCATION_MESSAGE,
+      IDS_BLOCKED_GEOLOCATION_MESSAGE);
 
   content_settings->OnContentAllowed(ContentSettingsType::GEOLOCATION);
   UpdateModelAndVerifyStates(
@@ -392,7 +395,8 @@
   content_settings->OnContentBlocked(ContentSettingsType::GEOLOCATION);
   UpdateModelAndVerifyStates(
       content_setting_image_model.get(), /* is_visible = */ true,
-      /* tooltip_empty = */ false, IDS_BLOCKED_GEOLOCATION_MESSAGE, 0);
+      /* tooltip_empty = */ false, IDS_BLOCKED_GEOLOCATION_MESSAGE,
+      IDS_BLOCKED_GEOLOCATION_MESSAGE);
 }
 
 TEST_F(ContentSettingImageModelTest, GeolocationAccessDeniedExperiment) {
diff --git a/chrome/browser/ui/global_media_controls/cast_media_notification_producer.cc b/chrome/browser/ui/global_media_controls/cast_media_notification_producer.cc
index 8ce97de6..e1013bf 100644
--- a/chrome/browser/ui/global_media_controls/cast_media_notification_producer.cc
+++ b/chrome/browser/ui/global_media_controls/cast_media_notification_producer.cc
@@ -128,8 +128,7 @@
 }
 
 void CastMediaNotificationProducer::OnRoutesUpdated(
-    const std::vector<media_router::MediaRoute>& routes,
-    const std::vector<media_router::MediaRoute::Id>& joinable_route_ids) {
+    const std::vector<media_router::MediaRoute>& routes) {
   const bool had_items = HasActiveItems();
 
   base::EraseIf(items_, [&routes](const auto& item) {
diff --git a/chrome/browser/ui/global_media_controls/cast_media_notification_producer.h b/chrome/browser/ui/global_media_controls/cast_media_notification_producer.h
index 79f0963..fb57b5c4 100644
--- a/chrome/browser/ui/global_media_controls/cast_media_notification_producer.h
+++ b/chrome/browser/ui/global_media_controls/cast_media_notification_producer.h
@@ -59,9 +59,8 @@
   void OnMediaItemUIDismissed(const std::string& id) override;
 
   // media_router::MediaRoutesObserver:
-  void OnRoutesUpdated(const std::vector<media_router::MediaRoute>& routes,
-                       const std::vector<media_router::MediaRoute::Id>&
-                           joinable_route_ids) override;
+  void OnRoutesUpdated(
+      const std::vector<media_router::MediaRoute>& routes) override;
 
   size_t GetActiveItemCount() const;
   bool HasLocalMediaRoute() const;
diff --git a/chrome/browser/ui/global_media_controls/cast_media_notification_producer_unittest.cc b/chrome/browser/ui/global_media_controls/cast_media_notification_producer_unittest.cc
index 190b00b6..8c7842d 100644
--- a/chrome/browser/ui/global_media_controls/cast_media_notification_producer_unittest.cc
+++ b/chrome/browser/ui/global_media_controls/cast_media_notification_producer_unittest.cc
@@ -80,13 +80,13 @@
   MediaRoute route = CreateRoute(route_id);
 
   EXPECT_CALL(items_changed_callback_, Run());
-  notification_producer_->OnRoutesUpdated({route}, {});
+  notification_producer_->OnRoutesUpdated({route});
   testing::Mock::VerifyAndClearExpectations(&items_changed_callback_);
   EXPECT_EQ(1u, notification_producer_->GetActiveItemCount());
   EXPECT_NE(nullptr, notification_producer_->GetMediaItem(route_id));
 
   EXPECT_CALL(items_changed_callback_, Run());
-  notification_producer_->OnRoutesUpdated({}, {});
+  notification_producer_->OnRoutesUpdated({});
   testing::Mock::VerifyAndClearExpectations(&items_changed_callback_);
   EXPECT_EQ(0u, notification_producer_->GetActiveItemCount());
 }
@@ -95,7 +95,7 @@
   const std::string route_id = "route-id-1";
   MediaRoute route = CreateRoute(route_id);
 
-  notification_producer_->OnRoutesUpdated({route}, {});
+  notification_producer_->OnRoutesUpdated({route});
   auto* item = static_cast<CastMediaNotificationItem*>(
       notification_producer_->GetMediaItem(route_id).get());
   NiceMock<media_message_center::test::MockMediaNotificationView> view;
@@ -112,7 +112,7 @@
         EXPECT_EQ(base::UTF8ToUTF16(new_description + separator + new_sink),
                   metadata.source_title);
       });
-  notification_producer_->OnRoutesUpdated({route}, {});
+  notification_producer_->OnRoutesUpdated({route});
 }
 
 TEST_F(CastMediaNotificationProducerTest, RoutesWithoutNotifications) {
@@ -124,7 +124,7 @@
   MediaRoute multizone_member_route = CreateRoute("route-3", "cast:705D30C6");
 
   notification_producer_->OnRoutesUpdated(
-      {non_display_route, no_controller_route, multizone_member_route}, {});
+      {non_display_route, no_controller_route, multizone_member_route});
   EXPECT_EQ(0u, notification_producer_->GetActiveItemCount());
 }
 
@@ -133,14 +133,14 @@
   const std::string route_id2 = "route-id-2";
   MediaRoute route1 = CreateRoute(route_id1);
   MediaRoute route2 = CreateRoute(route_id2);
-  notification_producer_->OnRoutesUpdated({route1}, {});
+  notification_producer_->OnRoutesUpdated({route1});
   EXPECT_EQ(1u, notification_producer_->GetActiveItemCount());
 
   notification_producer_->OnMediaItemUIDismissed(route_id1);
   EXPECT_EQ(0u, notification_producer_->GetActiveItemCount());
 
   // Adding another route should not bring back the dismissed notification.
-  notification_producer_->OnRoutesUpdated({route1, route2}, {});
+  notification_producer_->OnRoutesUpdated({route1, route2});
   EXPECT_EQ(1u, notification_producer_->GetActiveItemCount());
 }
 
@@ -155,9 +155,8 @@
   MediaRoute connecting_route = CreateRoute("route-4");
   connecting_route.set_is_connecting(true);
 
-  notification_producer_->OnRoutesUpdated(
-      {non_display_route, mirroring_route, multizone_member_route,
-       connecting_route},
-      {});
+  notification_producer_->OnRoutesUpdated({non_display_route, mirroring_route,
+                                           multizone_member_route,
+                                           connecting_route});
   EXPECT_EQ(0u, notification_producer_->GetActiveItemCount());
 }
diff --git a/chrome/browser/ui/global_media_controls/media_notification_service_unittest.cc b/chrome/browser/ui/global_media_controls/media_notification_service_unittest.cc
index 025ded7..d99e7b3c2 100644
--- a/chrome/browser/ui/global_media_controls/media_notification_service_unittest.cc
+++ b/chrome/browser/ui/global_media_controls/media_notification_service_unittest.cc
@@ -157,7 +157,7 @@
 
   void SimulateMediaRoutesUpdate(
       const std::vector<media_router::MediaRoute>& routes) {
-    service_->cast_notification_producer_->OnRoutesUpdated(routes, {});
+    service_->cast_notification_producer_->OnRoutesUpdated(routes);
   }
 
   MediaNotificationService::PresentationManagerObservation*
diff --git a/chrome/browser/ui/media_router/media_router_ui.cc b/chrome/browser/ui/media_router/media_router_ui.cc
index 173eea9..a0bd208 100644
--- a/chrome/browser/ui/media_router/media_router_ui.cc
+++ b/chrome/browser/ui/media_router/media_router_ui.cc
@@ -84,16 +84,6 @@
                                               : sink.name());
 }
 
-// Returns the first source in |sources| that can be connected to, or an empty
-// source if there is none.  This is used by the Media Router to find such a
-// matching route if it exists.
-MediaSource GetSourceForRouteObserver(const std::vector<MediaSource>& sources) {
-  auto source_it = std::find_if(
-      sources.begin(), sources.end(),
-      [](const auto& source) { return source.IsCastPresentationUrl(); });
-  return source_it != sources.end() ? *source_it : MediaSource(std::string());
-}
-
 void MaybeReportCastingSource(MediaCastMode cast_mode,
                               const RouteRequestResult& result) {
   if (result.result_code() == RouteRequestResult::OK)
@@ -345,9 +335,8 @@
   } else {
     // Register for MediaRoute updates without a media source.
     routes_observer_ = std::make_unique<UIMediaRoutesObserver>(
-        GetMediaRouter(), MediaSource::Id(),
-        base::BindRepeating(&MediaRouterUI::OnRoutesUpdated,
-                            base::Unretained(this)));
+        GetMediaRouter(), base::BindRepeating(&MediaRouterUI::OnRoutesUpdated,
+                                              base::Unretained(this)));
   }
 }
 
@@ -565,18 +554,16 @@
 
 MediaRouterUI::UIMediaRoutesObserver::UIMediaRoutesObserver(
     MediaRouter* router,
-    const MediaSource::Id& source_id,
     const RoutesUpdatedCallback& callback)
-    : MediaRoutesObserver(router, source_id), callback_(callback) {
+    : MediaRoutesObserver(router), callback_(callback) {
   DCHECK(!callback_.is_null());
 }
 
 MediaRouterUI::UIMediaRoutesObserver::~UIMediaRoutesObserver() = default;
 
 void MediaRouterUI::UIMediaRoutesObserver::OnRoutesUpdated(
-    const std::vector<MediaRoute>& routes,
-    const std::vector<MediaRoute::Id>& joinable_route_ids) {
-  callback_.Run(routes, joinable_route_ids);
+    const std::vector<MediaRoute>& routes) {
+  callback_.Run(routes);
 }
 
 std::vector<MediaSource> MediaRouterUI::GetSourcesForCastMode(
@@ -609,8 +596,7 @@
 
   // Get the current list of media routes, so that the WebUI will have routes
   // information at initialization.
-  OnRoutesUpdated(GetMediaRouter()->GetCurrentRoutes(),
-                  std::vector<MediaRoute::Id>());
+  OnRoutesUpdated(GetMediaRouter()->GetCurrentRoutes());
   display_observer_ = WebContentsDisplayObserver::Create(
       initiator_,
       base::BindRepeating(&MediaRouterUI::UpdateSinks, base::Unretained(this)));
@@ -655,17 +641,10 @@
   query_result_manager_->SetSourcesForCastMode(
       MediaCastMode::PRESENTATION, sources,
       presentation_request_->frame_origin);
-  // Register for MediaRoute updates.  NOTE(mfoltz): If there are multiple
-  // sources that can be connected to via the dialog, this will break.  We will
-  // need to observe multiple sources (keyed by sinks) in that case.  As this is
-  // Cast-specific for the foreseeable future, it may be simpler to plumb a new
-  // observer API for this case.
-  const MediaSource source_for_route_observer =
-      GetSourceForRouteObserver(sources);
+  // Register for MediaRoute updates.
   routes_observer_ = std::make_unique<UIMediaRoutesObserver>(
-      GetMediaRouter(), source_for_route_observer.id(),
-      base::BindRepeating(&MediaRouterUI::OnRoutesUpdated,
-                          base::Unretained(this)));
+      GetMediaRouter(), base::BindRepeating(&MediaRouterUI::OnRoutesUpdated,
+                                            base::Unretained(this)));
   UpdateModelHeader();
 }
 
@@ -673,11 +652,10 @@
   presentation_request_.reset();
   query_result_manager_->RemoveSourcesForCastMode(MediaCastMode::PRESENTATION);
 
-  // Register for MediaRoute updates without a media source.
+  // Register for MediaRoute updates.
   routes_observer_ = std::make_unique<UIMediaRoutesObserver>(
-      GetMediaRouter(), MediaSource::Id(),
-      base::BindRepeating(&MediaRouterUI::OnRoutesUpdated,
-                          base::Unretained(this)));
+      GetMediaRouter(), base::BindRepeating(&MediaRouterUI::OnRoutesUpdated,
+                                            base::Unretained(this)));
 
   UpdateModelHeader();
 }
@@ -873,9 +851,7 @@
   UpdateSinks();
 }
 
-void MediaRouterUI::OnRoutesUpdated(
-    const std::vector<MediaRoute>& routes,
-    const std::vector<MediaRoute::Id>& joinable_route_ids) {
+void MediaRouterUI::OnRoutesUpdated(const std::vector<MediaRoute>& routes) {
   routes_.clear();
 
   for (const MediaRoute& route : routes) {
diff --git a/chrome/browser/ui/media_router/media_router_ui.h b/chrome/browser/ui/media_router/media_router_ui.h
index 9296a4c..b99ba48 100644
--- a/chrome/browser/ui/media_router/media_router_ui.h
+++ b/chrome/browser/ui/media_router/media_router_ui.h
@@ -201,10 +201,8 @@
   class UIMediaRoutesObserver : public MediaRoutesObserver {
    public:
     using RoutesUpdatedCallback =
-        base::RepeatingCallback<void(const std::vector<MediaRoute>&,
-                                     const std::vector<MediaRoute::Id>&)>;
+        base::RepeatingCallback<void(const std::vector<MediaRoute>&)>;
     UIMediaRoutesObserver(MediaRouter* router,
-                          const MediaSource::Id& source_id,
                           const RoutesUpdatedCallback& callback);
 
     UIMediaRoutesObserver(const UIMediaRoutesObserver&) = delete;
@@ -213,9 +211,7 @@
     ~UIMediaRoutesObserver() override;
 
     // MediaRoutesObserver:
-    void OnRoutesUpdated(
-        const std::vector<MediaRoute>& routes,
-        const std::vector<MediaRoute::Id>& joinable_route_ids) override;
+    void OnRoutesUpdated(const std::vector<MediaRoute>& routes) override;
 
    private:
     // Callback to the owning MediaRouterUI instance.
@@ -283,8 +279,7 @@
   void OnIssueCleared();
 
   // Called by |routes_observer_| when the set of active routes has changed.
-  void OnRoutesUpdated(const std::vector<MediaRoute>& routes,
-                       const std::vector<MediaRoute::Id>& joinable_route_ids);
+  void OnRoutesUpdated(const std::vector<MediaRoute>& routes);
 
   // QueryResultManager::Observer:
   void OnResultsUpdated(
diff --git a/chrome/browser/ui/media_router/media_router_ui_unittest.cc b/chrome/browser/ui/media_router/media_router_ui_unittest.cc
index 91356e6..b6e4b66 100644
--- a/chrome/browser/ui/media_router/media_router_ui_unittest.cc
+++ b/chrome/browser/ui/media_router/media_router_ui_unittest.cc
@@ -189,10 +189,8 @@
       const std::vector<MediaSinkWithCastModes>& sinks) {
     ui_->OnResultsUpdated(sinks);
   }
-  void NotifyUiOnRoutesUpdated(
-      const std::vector<MediaRoute>& routes,
-      const std::vector<MediaRoute::Id>& joinable_route_ids) {
-    ui_->OnRoutesUpdated(routes, joinable_route_ids);
+  void NotifyUiOnRoutesUpdated(const std::vector<MediaRoute>& routes) {
+    ui_->OnRoutesUpdated(routes);
   }
 
   void StartTabCasting(bool is_incognito) {
@@ -302,7 +300,7 @@
             EXPECT_EQ(UIMediaSinkState::CONNECTED, ui_sink.state);
             EXPECT_EQ(route.media_route_id(), ui_sink.route->media_route_id());
           })));
-  NotifyUiOnRoutesUpdated({route}, {});
+  NotifyUiOnRoutesUpdated({route});
 
   EXPECT_CALL(observer, OnControllerInvalidatedInternal());
   ui_.reset();
@@ -418,7 +416,7 @@
         EXPECT_EQ(UIMediaSinkState::CONNECTED, model.media_sinks()[0].state);
       })));
   MediaRoute route(kRouteId, MediaSource(kSourceId), kSinkId, "", true, true);
-  NotifyUiOnRoutesUpdated({route}, {});
+  NotifyUiOnRoutesUpdated({route});
 }
 
 TEST_F(MediaRouterViewsUITest, DisconnectingState) {
@@ -428,7 +426,7 @@
   MediaRoute route(kRouteId, MediaSource(kSourceId), kSinkId, "", true, true);
   for (MediaSinksObserver* sinks_observer : media_sinks_observers_)
     sinks_observer->OnSinksUpdated({sink}, std::vector<url::Origin>());
-  NotifyUiOnRoutesUpdated({route}, {});
+  NotifyUiOnRoutesUpdated({route});
 
   // When a request to stop casting to a sink is made, its state should become
   // DISCONNECTING.
@@ -446,7 +444,7 @@
         ASSERT_EQ(1u, model.media_sinks().size());
         EXPECT_EQ(UIMediaSinkState::AVAILABLE, model.media_sinks()[0].state);
       })));
-  NotifyUiOnRoutesUpdated({}, {});
+  NotifyUiOnRoutesUpdated({});
 }
 
 TEST_F(MediaRouterViewsUITest, AddAndRemoveIssue) {
@@ -617,7 +615,7 @@
                              true, true);
 
   NotifyUiOnRoutesUpdated(
-      {display_route_1, non_display_route_1, display_route_2}, {});
+      {display_route_1, non_display_route_1, display_route_2});
   ASSERT_EQ(2u, ui_->routes().size());
   EXPECT_EQ(display_route_1, ui_->routes()[0]);
   EXPECT_TRUE(ui_->routes()[0].for_display());
diff --git a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarView.java b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarView.java
index 99d3736..d01675c 100644
--- a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarView.java
+++ b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarView.java
@@ -268,7 +268,7 @@
 
     private static int getTextAppearance(Snackbar snackbar) {
         if (snackbar.getTheme() == Snackbar.Theme.GOOGLE) {
-            return R.style.TextAppearance_TextMedium_Primary_Baseline_Inverse;
+            return R.style.TextAppearance_TextMedium_Primary_OnAccent1;
         }
 
         assert snackbar.getTheme() == Snackbar.Theme.BASIC;
diff --git a/chrome/browser/ui/sharing_hub/sharing_hub_sub_menu_model.cc b/chrome/browser/ui/sharing_hub/sharing_hub_sub_menu_model.cc
deleted file mode 100644
index 819c30e1..0000000
--- a/chrome/browser/ui/sharing_hub/sharing_hub_sub_menu_model.cc
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/sharing_hub/sharing_hub_sub_menu_model.h"
-
-#include "base/metrics/user_metrics.h"
-#include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/media/router/media_router_feature.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/share/share_metrics.h"
-#include "chrome/browser/sharing_hub/sharing_hub_model.h"
-#include "chrome/browser/sharing_hub/sharing_hub_service.h"
-#include "chrome/browser/sharing_hub/sharing_hub_service_factory.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/global_error/global_error.h"
-#include "chrome/browser/ui/global_error/global_error_service.h"
-#include "chrome/browser/ui/global_error/global_error_service_factory.h"
-#include "chrome/grit/generated_resources.h"
-#include "content/public/browser/web_contents.h"
-#include "ui/base/models/simple_menu_model.h"
-#include "ui/color/color_id.h"
-
-namespace sharing_hub {
-
-SharingHubSubMenuModel::SharingHubSubMenuModel(Browser* browser)
-    : SimpleMenuModel(this), browser_(browser) {
-  Build(browser_->tab_strip_model()->GetActiveWebContents());
-}
-
-SharingHubSubMenuModel::~SharingHubSubMenuModel() = default;
-
-bool SharingHubSubMenuModel::IsCommandIdEnabled(int command_id) const {
-  return true;
-}
-
-void SharingHubSubMenuModel::ExecuteCommand(int command_id, int event_flags) {
-  share::LogShareSourceDesktop(share::ShareSourceDesktop::kAppMenuSharingHub);
-
-  if (IsThirdPartyAction(command_id)) {
-    SharingHubModel* const model = GetSharingHubModel();
-    if (!model)
-      return;
-    model->ExecuteThirdPartyAction(
-        browser_->tab_strip_model()->GetActiveWebContents(), command_id);
-  } else {
-    GlobalError* error =
-        GlobalErrorServiceFactory::GetForProfile(browser_->profile())
-            ->GetGlobalErrorByMenuItemCommandID(command_id);
-    if (error) {
-      error->ExecuteMenuItem(browser_);
-      return;
-    }
-    base::RecordComputedAction(user_actions_by_id_[command_id]);
-    chrome::ExecuteCommand(browser_, command_id);
-  }
-}
-
-SharingHubModel* SharingHubSubMenuModel::GetSharingHubModel() const {
-  SharingHubService* const service =
-      SharingHubServiceFactory::GetForProfile(browser_->profile());
-  return service ? service->GetSharingHubModel() : nullptr;
-}
-
-void SharingHubSubMenuModel::Build(content::WebContents* web_contents) {
-  if (!web_contents)
-    return;
-  web_contents_ = web_contents;
-  SharingHubModel* const model = GetSharingHubModel();
-  if (!model)
-    return;
-
-  std::vector<SharingHubAction> first_party_actions;
-  std::vector<SharingHubAction> third_party_actions;
-  model->GetFirstPartyActionList(web_contents, &first_party_actions);
-  model->GetThirdPartyActionList(&third_party_actions);
-
-  for (auto action : first_party_actions) {
-    AddItem(action.command_id, action.title);
-    user_actions_by_id_[action.command_id] = action.feature_name_for_metrics;
-  }
-  AddSeparator(ui::NORMAL_SEPARATOR);
-  for (auto action : third_party_actions) {
-    if (action.third_party_icon.isNull()) {
-      AddItemWithIcon(
-          action.command_id, action.title,
-          ui::ImageModel::FromVectorIcon(*action.icon, ui::kColorMenuIcon,
-                                         /*icon_size*/ 16));
-    } else {
-      AddItemWithIcon(action.command_id, action.title,
-                      ui::ImageModel::FromImageSkia(action.third_party_icon));
-    }
-    third_party_action_ids_.push_back(action.command_id);
-  }
-}
-
-bool SharingHubSubMenuModel::IsThirdPartyAction(int id) {
-  return std::find(third_party_action_ids_.begin(),
-                   third_party_action_ids_.end(),
-                   id) != third_party_action_ids_.end();
-}
-
-}  // namespace sharing_hub
diff --git a/chrome/browser/ui/sharing_hub/sharing_hub_sub_menu_model.h b/chrome/browser/ui/sharing_hub/sharing_hub_sub_menu_model.h
deleted file mode 100644
index aa76f11..0000000
--- a/chrome/browser/ui/sharing_hub/sharing_hub_sub_menu_model.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_SHARING_HUB_SHARING_HUB_SUB_MENU_MODEL_H_
-#define CHROME_BROWSER_UI_SHARING_HUB_SHARING_HUB_SUB_MENU_MODEL_H_
-
-#include <map>
-#include "base/memory/raw_ptr.h"
-#include "content/public/browser/web_contents.h"
-#include "ui/base/models/simple_menu_model.h"
-
-class Browser;
-
-namespace sharing_hub {
-
-class SharingHubModel;
-
-class SharingHubSubMenuModel : public ui::SimpleMenuModel,
-                               public ui::SimpleMenuModel::Delegate {
- public:
-  explicit SharingHubSubMenuModel(Browser* browser);
-
-  SharingHubSubMenuModel(const SharingHubSubMenuModel&) = delete;
-  SharingHubSubMenuModel& operator=(const SharingHubSubMenuModel&) = delete;
-
-  ~SharingHubSubMenuModel() override;
-
-  // Overridden from ui::SimpleMenuModel::Delegate:
-  bool IsCommandIdEnabled(int command_id) const override;
-  void ExecuteCommand(int command_id, int event_flags) override;
-
- private:
-  SharingHubModel* GetSharingHubModel() const;
-  void Build(content::WebContents* web_contents);
-  bool IsThirdPartyAction(int id);
-
-  raw_ptr<Browser> browser_;
-  raw_ptr<content::WebContents> web_contents_;
-  std::vector<int> third_party_action_ids_;
-
-  // A list of user action names mapped to action id.
-  std::map<int, std::string> user_actions_by_id_;
-};
-}  // namespace sharing_hub
-
-#endif  // CHROME_BROWSER_UI_SHARING_HUB_SHARING_HUB_SUB_MENU_MODEL_H_
diff --git a/chrome/browser/ui/sharing_hub/sharing_hub_sub_menu_model_unittest.cc b/chrome/browser/ui/sharing_hub/sharing_hub_sub_menu_model_unittest.cc
deleted file mode 100644
index 8b85cfb0..0000000
--- a/chrome/browser/ui/sharing_hub/sharing_hub_sub_menu_model_unittest.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/sharing_hub/sharing_hub_sub_menu_model.h"
-
-#include "chrome/test/base/browser_with_test_window_test.h"
-
-namespace {
-
-class SharingHubSubMenuModelTest : public BrowserWithTestWindowTest {
- public:
-  SharingHubSubMenuModelTest() = default;
-
-  SharingHubSubMenuModelTest(const SharingHubSubMenuModelTest&) = delete;
-  SharingHubSubMenuModelTest& operator=(const SharingHubSubMenuModelTest&) =
-      delete;
-
-  ~SharingHubSubMenuModelTest() override = default;
-};
-
-TEST_F(SharingHubSubMenuModelTest, DISABLED_EnableItemsBySharable) {
-  // TODO crbug/1186848 test enablement based on browser sharability.
-}
-
-}  // namespace
diff --git a/chrome/browser/ui/startup/bad_flags_prompt.cc b/chrome/browser/ui/startup/bad_flags_prompt.cc
index 3844fb4..91d46a7 100644
--- a/chrome/browser/ui/startup/bad_flags_prompt.cc
+++ b/chrome/browser/ui/startup/bad_flags_prompt.cc
@@ -25,8 +25,6 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
 #include "components/infobars/content/content_infobar_manager.h"
 #include "components/infobars/core/infobar_delegate.h"
-#include "components/nacl/common/buildflags.h"
-#include "components/nacl/common/nacl_switches.h"
 #include "components/network_session_configurator/common/network_switches.h"
 #include "components/startup_metric_utils/browser/startup_metric_utils.h"
 #include "components/translate/core/common/translate_switches.h"
@@ -68,9 +66,6 @@
 #endif
     switches::kDisableSiteIsolation,
     switches::kDisableWebSecurity,
-#if BUILDFLAG(ENABLE_NACL)
-    switches::kNaClDangerousNoSandboxNonSfi,
-#endif
     switches::kSingleProcess,
 
     // These flags disable or undermine the Same Origin Policy.
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc
index 7a4da1b..e0b0f6c 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -30,7 +30,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/search/search.h"
-#include "chrome/browser/sharing_hub/sharing_hub_features.h"
 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -41,7 +40,6 @@
 #include "chrome/browser/ui/global_error/global_error_service.h"
 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
 #include "chrome/browser/ui/managed_ui.h"
-#include "chrome/browser/ui/sharing_hub/sharing_hub_sub_menu_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/toolbar/app_menu_icon_controller.h"
 #include "chrome/browser/ui/toolbar/bookmark_sub_menu_model.h"
@@ -235,8 +233,7 @@
 // - Developer tools.
 // - Option to enable profiling.
 void ToolsMenuModel::Build(Browser* browser) {
-  if (!base::FeatureList::IsEnabled(sharing_hub::kSharingHubDesktopAppMenu) ||
-      browser->profile()->IsIncognitoProfile() ||
+  if (browser->profile()->IsIncognitoProfile() ||
       browser->profile()->IsGuestSession()) {
     AddItemWithStringId(IDC_SAVE_PAGE, IDS_SAVE_PAGE);
   }
@@ -830,23 +827,10 @@
   CreateZoomMenu();
   AddSeparator(ui::UPPER_SEPARATOR);
 
-  if (!(browser_->profile()->IsIncognitoProfile() ||
-        browser_->profile()->IsGuestSession()) &&
-      sharing_hub::SharingHubAppMenuEnabled(browser()->profile())) {
-    sub_menus_.push_back(
-        std::make_unique<sharing_hub::SharingHubSubMenuModel>(browser_));
-    AddSubMenuWithStringId(IDC_SHARING_HUB_MENU, IDS_SHARING_HUB_TITLE,
-                           sub_menus_.back().get());
-  }
-
   AddItemWithStringId(IDC_PRINT, IDS_PRINT);
 
-  if (!sharing_hub::SharingHubAppMenuEnabled(browser()->profile()) ||
-      browser_->profile()->IsIncognitoProfile() ||
-      browser_->profile()->IsGuestSession()) {
-    if (media_router::MediaRouterEnabled(browser()->profile()))
-      AddItemWithStringId(IDC_ROUTE_MEDIA, IDS_MEDIA_ROUTER_MENU_ITEM_TITLE);
-  }
+  if (media_router::MediaRouterEnabled(browser()->profile()))
+    AddItemWithStringId(IDC_ROUTE_MEDIA, IDS_MEDIA_ROUTER_MENU_ITEM_TITLE);
 
   AddItemWithStringId(IDC_FIND, IDS_FIND);
 
diff --git a/chrome/browser/ui/toolbar/media_router_action_controller.cc b/chrome/browser/ui/toolbar/media_router_action_controller.cc
index 2bb7c1d..10a57f3 100644
--- a/chrome/browser/ui/toolbar/media_router_action_controller.cc
+++ b/chrome/browser/ui/toolbar/media_router_action_controller.cc
@@ -61,8 +61,7 @@
 }
 
 void MediaRouterActionController::OnRoutesUpdated(
-    const std::vector<media_router::MediaRoute>& routes,
-    const std::vector<media_router::MediaRoute::Id>& joinable_route_ids) {
+    const std::vector<media_router::MediaRoute>& routes) {
   has_local_display_route_ =
       std::find_if(routes.begin(), routes.end(),
                    [this](const media_router::MediaRoute& route) {
diff --git a/chrome/browser/ui/toolbar/media_router_action_controller.h b/chrome/browser/ui/toolbar/media_router_action_controller.h
index 08b0c53..863e5c9 100644
--- a/chrome/browser/ui/toolbar/media_router_action_controller.h
+++ b/chrome/browser/ui/toolbar/media_router_action_controller.h
@@ -66,9 +66,8 @@
   void OnIssuesCleared() override;
 
   // media_router::MediaRoutesObserver:
-  void OnRoutesUpdated(const std::vector<media_router::MediaRoute>& routes,
-                       const std::vector<media_router::MediaRoute::Id>&
-                           joinable_route_ids) override;
+  void OnRoutesUpdated(
+      const std::vector<media_router::MediaRoute>& routes) override;
 
   // Called when a Media Router dialog is shown or hidden, and updates the
   // visibility of the action icon. Overridden in tests.
diff --git a/chrome/browser/ui/toolbar/media_router_action_controller_unittest.cc b/chrome/browser/ui/toolbar/media_router_action_controller_unittest.cc
index 77dfe5a..d89bb51b 100644
--- a/chrome/browser/ui/toolbar/media_router_action_controller_unittest.cc
+++ b/chrome/browser/ui/toolbar/media_router_action_controller_unittest.cc
@@ -96,7 +96,6 @@
 
   std::vector<media_router::MediaRoute> local_display_route_list_;
   std::vector<media_router::MediaRoute> non_local_display_route_list_;
-  std::vector<media_router::MediaRoute::Id> empty_route_id_list_;
 };
 
 // TODO(b/185139027): Remove this class once
@@ -130,12 +129,11 @@
   EXPECT_FALSE(IsIconShown());
 
   // Creating a local route should show the action icon.
-  controller_->OnRoutesUpdated(local_display_route_list_, empty_route_id_list_);
+  controller_->OnRoutesUpdated(local_display_route_list_);
   EXPECT_TRUE(controller_->has_local_display_route_);
   EXPECT_TRUE(IsIconShown());
   // Removing the local route should hide the icon.
-  controller_->OnRoutesUpdated(non_local_display_route_list_,
-                               empty_route_id_list_);
+  controller_->OnRoutesUpdated(non_local_display_route_list_);
   EXPECT_FALSE(controller_->has_local_display_route_);
   EXPECT_FALSE(IsIconShown());
 
@@ -149,13 +147,12 @@
   EXPECT_FALSE(IsIconShown());
 
   controller_->OnIssue(issue_);
-  controller_->OnRoutesUpdated(local_display_route_list_, empty_route_id_list_);
+  controller_->OnRoutesUpdated(local_display_route_list_);
   controller_->OnIssuesCleared();
   // When the issue disappears, the icon should remain visible if there's
   // a local route.
   EXPECT_TRUE(IsIconShown());
-  controller_->OnRoutesUpdated(std::vector<media_router::MediaRoute>(),
-                               empty_route_id_list_);
+  controller_->OnRoutesUpdated(std::vector<media_router::MediaRoute>());
   EXPECT_FALSE(IsIconShown());
 }
 
@@ -177,12 +174,11 @@
 
   controller_->OnDialogShown();
   EXPECT_TRUE(IsIconShown());
-  controller_->OnRoutesUpdated(local_display_route_list_, empty_route_id_list_);
+  controller_->OnRoutesUpdated(local_display_route_list_);
   // Hiding the dialog while there are local routes shouldn't hide the icon.
   controller_->OnDialogHidden();
   EXPECT_TRUE(IsIconShown());
-  controller_->OnRoutesUpdated(non_local_display_route_list_,
-                               empty_route_id_list_);
+  controller_->OnRoutesUpdated(non_local_display_route_list_);
   EXPECT_FALSE(IsIconShown());
 
   controller_->OnDialogShown();
@@ -217,14 +213,13 @@
   SetAlwaysShowActionPref(true);
   EXPECT_TRUE(IsIconShown());
 
-  controller_->OnRoutesUpdated(local_display_route_list_, empty_route_id_list_);
+  controller_->OnRoutesUpdated(local_display_route_list_);
   SetAlwaysShowActionPref(false);
   // Unchecking the option while having a local route shouldn't hide the icon.
   EXPECT_TRUE(IsIconShown());
 
   SetAlwaysShowActionPref(true);
-  controller_->OnRoutesUpdated(non_local_display_route_list_,
-                               empty_route_id_list_);
+  controller_->OnRoutesUpdated(non_local_display_route_list_);
   // Removing the local route should not hide the icon.
   EXPECT_TRUE(IsIconShown());
 
@@ -236,12 +231,10 @@
        EphemeralIconForMirroringSessions) {
   EXPECT_FALSE(IsIconShown());
   // Creating a cast route should not show the action icon.
-  controller_->OnRoutesUpdated({local_display_cast_route_list_},
-                               empty_route_id_list_);
+  controller_->OnRoutesUpdated({local_display_cast_route_list_});
   EXPECT_FALSE(IsIconShown());
 
   // Creating a local mirroring route should show the action icon.
-  controller_->OnRoutesUpdated(local_display_mirroring_route_list_,
-                               empty_route_id_list_);
+  controller_->OnRoutesUpdated(local_display_mirroring_route_list_);
   EXPECT_TRUE(IsIconShown());
 }
diff --git a/chrome/browser/ui/toolbar/mock_media_router_action_controller.h b/chrome/browser/ui/toolbar/mock_media_router_action_controller.h
index fddacf7..cd3189d 100644
--- a/chrome/browser/ui/toolbar/mock_media_router_action_controller.h
+++ b/chrome/browser/ui/toolbar/mock_media_router_action_controller.h
@@ -20,10 +20,8 @@
   ~MockMediaRouterActionController() override;
 
   MOCK_METHOD1(OnIssueUpdated, void(const media_router::Issue* issue));
-  MOCK_METHOD2(OnRoutesUpdated,
-               void(const std::vector<media_router::MediaRoute>& routes,
-                    const std::vector<media_router::MediaRoute::Id>&
-                        joinable_route_ids));
+  MOCK_METHOD1(OnRoutesUpdated,
+               void(const std::vector<media_router::MediaRoute>& routes));
   MOCK_METHOD0(OnDialogShown, void());
   MOCK_METHOD0(OnDialogHidden, void());
   MOCK_METHOD0(OnContextMenuShown, void());
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc b/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc
index 844a8117..abfd725 100644
--- a/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc
@@ -325,7 +325,7 @@
   void NotifyMediaRoutesChanged(
       const std::vector<media_router::MediaRoute>& routes) {
     for (auto* observer : routes_observers_)
-      observer->OnRoutesUpdated(routes, {});
+      observer->OnRoutesUpdated(routes);
   }
 
  private:
diff --git a/chrome/browser/ui/views/location_bar/content_setting_image_view.cc b/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
index efa86e5a..348f26f1 100644
--- a/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
+++ b/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
@@ -113,6 +113,8 @@
   if (content_setting_image_model_->ShouldNotifyAccessibility(web_contents)) {
     GetViewAccessibility().OverrideName(l10n_util::GetStringUTF16(
         content_setting_image_model_->explanatory_string_id()));
+    GetViewAccessibility().OverrideDescription(
+        l10n_util::GetStringUTF16(IDS_A11Y_OMNIBOX_CHIP_HINT));
     NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true);
     content_setting_image_model_->AccessibilityWasNotified(web_contents);
   }
diff --git a/chrome/browser/ui/views/media_router/cast_toolbar_button.cc b/chrome/browser/ui/views/media_router/cast_toolbar_button.cc
index 84b41b4d..18990a8 100644
--- a/chrome/browser/ui/views/media_router/cast_toolbar_button.cc
+++ b/chrome/browser/ui/views/media_router/cast_toolbar_button.cc
@@ -117,8 +117,7 @@
 }
 
 void CastToolbarButton::OnRoutesUpdated(
-    const std::vector<media_router::MediaRoute>& routes,
-    const std::vector<media_router::MediaRoute::Id>& joinable_route_ids) {
+    const std::vector<media_router::MediaRoute>& routes) {
   has_local_display_route_ =
       std::find_if(routes.begin(), routes.end(),
                    [](const media_router::MediaRoute& route) {
diff --git a/chrome/browser/ui/views/media_router/cast_toolbar_button.h b/chrome/browser/ui/views/media_router/cast_toolbar_button.h
index 74b19e06..be2bef2 100644
--- a/chrome/browser/ui/views/media_router/cast_toolbar_button.h
+++ b/chrome/browser/ui/views/media_router/cast_toolbar_button.h
@@ -53,9 +53,8 @@
   void OnIssuesCleared() override;
 
   // media_router::MediaRoutesObserver:
-  void OnRoutesUpdated(const std::vector<media_router::MediaRoute>& routes,
-                       const std::vector<media_router::MediaRoute::Id>&
-                           joinable_route_ids) override;
+  void OnRoutesUpdated(
+      const std::vector<media_router::MediaRoute>& routes) override;
 
   // ToolbarButton:
   bool OnMousePressed(const ui::MouseEvent& event) override;
diff --git a/chrome/browser/ui/views/media_router/cast_toolbar_button_unittest.cc b/chrome/browser/ui/views/media_router/cast_toolbar_button_unittest.cc
index 2aa7a52..5629287 100644
--- a/chrome/browser/ui/views/media_router/cast_toolbar_button_unittest.cc
+++ b/chrome/browser/ui/views/media_router/cast_toolbar_button_unittest.cc
@@ -187,15 +187,15 @@
   button_->UpdateIcon();
   EXPECT_TRUE(gfx::test::AreImagesEqual(idle_icon_, GetIcon()));
 
-  button_->OnRoutesUpdated(local_display_route_list_, {});
+  button_->OnRoutesUpdated(local_display_route_list_);
   EXPECT_TRUE(gfx::test::AreImagesEqual(active_icon_, GetIcon()));
 
   // The idle icon should be shown when we only have non-local and/or
   // non-display routes.
-  button_->OnRoutesUpdated(non_local_display_route_list_, {});
+  button_->OnRoutesUpdated(non_local_display_route_list_);
   EXPECT_TRUE(gfx::test::AreImagesEqual(idle_icon_, GetIcon()));
 
-  button_->OnRoutesUpdated({}, {});
+  button_->OnRoutesUpdated({});
   EXPECT_TRUE(gfx::test::AreImagesEqual(idle_icon_, GetIcon()));
 }
 
diff --git a/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc b/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
index b41fd0f..02c1a38e 100644
--- a/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
+++ b/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
@@ -200,10 +200,9 @@
   action_controller_->OnIssuesCleared();
   EXPECT_FALSE(ToolbarIconExists());
 
-  action_controller_->OnRoutesUpdated(routes_, std::vector<MediaRoute::Id>());
+  action_controller_->OnRoutesUpdated(routes_);
   EXPECT_TRUE(ToolbarIconExists());
-  action_controller_->OnRoutesUpdated(std::vector<MediaRoute>(),
-                                      std::vector<MediaRoute::Id>());
+  action_controller_->OnRoutesUpdated(std::vector<MediaRoute>());
   EXPECT_FALSE(ToolbarIconExists());
 
   SetAlwaysShowActionPref(true);
@@ -214,7 +213,7 @@
 
 IN_PROC_BROWSER_TEST_F(MediaRouterUIBrowserTest,
                        EphemeralToolbarIconWithMultipleWindows) {
-  action_controller_->OnRoutesUpdated(routes_, std::vector<MediaRoute::Id>());
+  action_controller_->OnRoutesUpdated(routes_);
   EXPECT_TRUE(ToolbarIconExists());
 
   // Opening and closing a window shouldn't affect the state of the ephemeral
@@ -222,10 +221,9 @@
   // also work.
   Browser* browser2 = CreateBrowser(browser()->profile());
   EXPECT_TRUE(ToolbarIconExists());
-  action_controller_->OnRoutesUpdated(std::vector<MediaRoute>(),
-                                      std::vector<MediaRoute::Id>());
+  action_controller_->OnRoutesUpdated(std::vector<MediaRoute>());
   EXPECT_FALSE(ToolbarIconExists());
-  action_controller_->OnRoutesUpdated(routes_, std::vector<MediaRoute::Id>());
+  action_controller_->OnRoutesUpdated(routes_);
   EXPECT_TRUE(ToolbarIconExists());
   browser2->window()->Close();
   EXPECT_TRUE(ToolbarIconExists());
diff --git a/chrome/browser/ui/views/tabs/tab_style_views.cc b/chrome/browser/ui/views/tabs/tab_style_views.cc
index 94524e8..ee5ab23 100644
--- a/chrome/browser/ui/views/tabs/tab_style_views.cc
+++ b/chrome/browser/ui/views/tabs/tab_style_views.cc
@@ -302,6 +302,11 @@
         radius - inset, radius - inset);
     path.addRRect(rrect);
   } else {
+    // Avoid mallocs at every new path verb by preallocating an
+    // empirically-determined amount of space in the verb and point buffers.
+    const int kMaxPathPoints = 20;
+    path.incReserve(kMaxPathPoints);
+
     // We will go clockwise from the lower left. We start in the overlap region,
     // preventing a gap between toolbar and tabstrip.
     // TODO(dfried): verify that the we actually want to start the stroke for
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
index 11c00ea6..0b66105 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
@@ -1575,17 +1575,9 @@
   helper_.CheckWindowCreated();
 }
 
-// TODO(https://crbug.com/1277870): Flaky on Linux builders.
-#if defined(OS_LINUX)
-#define MAYBE_WebAppIntegration_InstOmniboxSiteA_WindowCreated_InListWinSiteA_InstPlcyTabShctSiteA_InListWinSiteA_LaunchFromIconSiteA_WindowCreated \
-  DISABLED_WebAppIntegration_InstOmniboxSiteA_WindowCreated_InListWinSiteA_InstPlcyTabShctSiteA_InListWinSiteA_LaunchFromIconSiteA_WindowCreated
-#else
-#define MAYBE_WebAppIntegration_InstOmniboxSiteA_WindowCreated_InListWinSiteA_InstPlcyTabShctSiteA_InListWinSiteA_LaunchFromIconSiteA_WindowCreated \
-  WebAppIntegration_InstOmniboxSiteA_WindowCreated_InListWinSiteA_InstPlcyTabShctSiteA_InListWinSiteA_LaunchFromIconSiteA_WindowCreated
-#endif
 IN_PROC_BROWSER_TEST_F(
     WebAppIntegrationBrowserTest,
-    MAYBE_WebAppIntegration_InstOmniboxSiteA_WindowCreated_InListWinSiteA_InstPlcyTabShctSiteA_InListWinSiteA_LaunchFromIconSiteA_WindowCreated) {
+    WebAppIntegration_InstOmniboxSiteA_WindowCreated_InListWinSiteA_InstPlcyTabShctSiteA_InListWinSiteA_LaunchFromIconSiteA_WindowCreated) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
   // Sheriffs: Disabling this test is supported.
@@ -1642,17 +1634,9 @@
   helper_.CheckWindowCreated();
 }
 
-// TODO(https://crbug.com/1277870): Flaky on Linux builders.
-#if defined(OS_LINUX)
-#define MAYBE_WebAppIntegration_InstMenuOptionSiteA_WindowCreated_InListWinSiteA_InstPlcyTabShctSiteA_InListWinSiteA_LaunchFromIconSiteA_WindowCreated \
-  DISABLED_WebAppIntegration_InstMenuOptionSiteA_WindowCreated_InListWinSiteA_InstPlcyTabShctSiteA_InListWinSiteA_LaunchFromIconSiteA_WindowCreated
-#else
-#define MAYBE_WebAppIntegration_InstMenuOptionSiteA_WindowCreated_InListWinSiteA_InstPlcyTabShctSiteA_InListWinSiteA_LaunchFromIconSiteA_WindowCreated \
-  WebAppIntegration_InstMenuOptionSiteA_WindowCreated_InListWinSiteA_InstPlcyTabShctSiteA_InListWinSiteA_LaunchFromIconSiteA_WindowCreated
-#endif
 IN_PROC_BROWSER_TEST_F(
     WebAppIntegrationBrowserTest,
-    MAYBE_WebAppIntegration_InstMenuOptionSiteA_WindowCreated_InListWinSiteA_InstPlcyTabShctSiteA_InListWinSiteA_LaunchFromIconSiteA_WindowCreated) {
+    WebAppIntegration_InstMenuOptionSiteA_WindowCreated_InListWinSiteA_InstPlcyTabShctSiteA_InListWinSiteA_LaunchFromIconSiteA_WindowCreated) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
   // Sheriffs: Disabling this test is supported.
diff --git a/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc b/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc
index 97f16a13..397171e 100644
--- a/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc
+++ b/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc
@@ -440,12 +440,11 @@
     GetTestConfig().override_policy_connector_is_managed = true;
     GetTestConfig().managed_device = true;
     SetUpProfileAndHandler();
-    const TestDeviceStatusCollector* status_collector =
-        new TestDeviceStatusCollector(
-            &local_state_, GetTestConfig().report_activity_times,
-            GetTestConfig().report_nics, GetTestConfig().report_hardware_data,
-            GetTestConfig().report_users, GetTestConfig().report_crash_info,
-            GetTestConfig().report_app_info_and_activity);
+    const TestDeviceStatusCollector status_collector(
+        &local_state_, GetTestConfig().report_activity_times,
+        GetTestConfig().report_nics, GetTestConfig().report_hardware_data,
+        GetTestConfig().report_users, GetTestConfig().report_crash_info,
+        GetTestConfig().report_app_info_and_activity);
     settings_.device_settings()->SetTrustedStatus(
         ash::CrosSettingsProvider::TRUSTED);
     settings_.device_settings()->SetBoolean(ash::kSystemLogUploadEnabled,
@@ -464,17 +463,17 @@
         GetTestConfig().crostini_ansible_playbook_filepath);
     crostini_features()->set_is_allowed_now(true);
 
-    const policy::SystemLogUploader* system_uploader =
-        new policy::SystemLogUploader(/*syslog_delegate=*/nullptr,
-                                      /*task_runner=*/task_runner_);
+    const policy::SystemLogUploader system_log_uploader(
+        /*syslog_delegate=*/nullptr,
+        /*task_runner=*/task_runner_);
     ON_CALL(testing::Const(handler_), GetDeviceCloudPolicyManager())
         .WillByDefault(Return(manager_.get()));
     EXPECT_CALL(*static_cast<const policy::MockDlpRulesManager*>(
                     handler_.GetDlpRulesManager()),
                 IsReportingEnabled)
         .WillRepeatedly(testing::Return(GetTestConfig().report_dlp_events));
-    return handler_.GetDeviceReportingInfo(manager_.get(), status_collector,
-                                           system_uploader, GetProfile());
+    return handler_.GetDeviceReportingInfo(manager_.get(), &status_collector,
+                                           &system_log_uploader, GetProfile());
   }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 5607ed5..5fc9bc8 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1639483122-20e6b9acc183f41a276a93022da6f201498b4d81.profdata
+chrome-linux-main-1639504726-00818556d27ae6f0ca222d22829579a009d0b826.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 0f5c002..ce96eb9 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1639483122-e06fa0da191d06543cc2bbb3b3801d304c46d5b0.profdata
+chrome-mac-main-1639504726-fb9e43adc0e4760052265cf82e7db19c61befe33.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 892f302..37e8b846 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1639493922-9a45d06329fbed3e310b5994955083e355361eb8.profdata
+chrome-win32-main-1639504726-e7cdc104acb454455e958b92ea4ea0702a57e906.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 4ad085a..48ccf98 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1639483122-b852875a95ce2ee39f0bd733dae39836e0f19b13.profdata
+chrome-win64-main-1639504726-a5cec93f623091d76267247a1ff888db818a2b02.profdata
diff --git a/chrome/installer/mac/BUILD.gn b/chrome/installer/mac/BUILD.gn
index 9bd016f..a2d2713 100644
--- a/chrome/installer/mac/BUILD.gn
+++ b/chrome/installer/mac/BUILD.gn
@@ -166,7 +166,7 @@
       # Help the compiler find lipo for creating fat binaries.
       "-B",
       mac_bin_path,
-      "-fno-lto",
+
       "-arch",
       "x86_64",
       "-arch",
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 9f95790..4d35024 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -6399,7 +6399,6 @@
       "../browser/ui/media_router/media_router_ui_service_factory_unittest.cc",
       "../browser/ui/media_router/query_result_manager_unittest.cc",
       "../browser/ui/passwords/manage_passwords_ui_controller_unittest.cc",
-      "../browser/ui/sharing_hub/sharing_hub_sub_menu_model_unittest.cc",
       "../browser/ui/toolbar/chrome_location_bar_model_delegate_unittest.cc",
       "../browser/ui/toolbar/media_router_action_controller_unittest.cc",
       "../browser/ui/toolbar/media_router_contextual_menu_unittest.cc",
@@ -8632,6 +8631,7 @@
         "//chromeos/dbus",
         "//chromeos/login/auth",
         "//chromeos/ui/frame:test_support",
+        "//components/exo/wayland:weston_test_stub",
         "//ui/display:display_manager_test_api",
         "//ui/display/manager",
       ]
@@ -9586,6 +9586,7 @@
       "//base/test:test_support",
       "//chrome/browser",
       "//chrome/browser/chromeos",
+      "//components/exo/wayland:weston_test_stub",
       "//testing/gmock",
       "//testing/gtest",
       "//third_party/icu",
@@ -9825,6 +9826,7 @@
     deps = [
       "//ash:test_support",
       "//chromeos/services/machine_learning/public/cpp:stub",
+      "//components/exo/wayland:weston_test",
       "//ui/base:test_support",
 
       # For crosapi specific test support.
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.js b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.js
index 73aa9c88..b4ef8c2 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.js
@@ -7,7 +7,7 @@
 
 import * as action from 'chrome://personalization/trusted/personalization_actions.js';
 import {WallpaperCollection} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
-import {fetchGooglePhotosAlbum, fetchLocalData, initializeBackdropData, initializeGooglePhotosData, selectWallpaper} from 'chrome://personalization/trusted/wallpaper/wallpaper_controller.js';
+import {fetchCollections, fetchGooglePhotosAlbum, fetchLocalData, getLocalImages, initializeBackdropData, initializeGooglePhotosData, selectWallpaper} from 'chrome://personalization/trusted/wallpaper/wallpaper_controller.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
@@ -730,3 +730,76 @@
                 filterAndFlattenState(['pendingSelected'])));
       });
 });
+
+suite('local images available but no internet connection', () => {
+  let wallpaperProvider;
+  let personalizationStore;
+
+  setup(() => {
+    wallpaperProvider = new TestWallpaperProvider();
+    personalizationStore = new TestPersonalizationStore({});
+    personalizationStore.setReducersEnabled(true);
+  });
+
+  test(
+      'error displays when fetch collections failed but local images loaded',
+      async () => {
+        loadTimeData.overrideValues({['networkError']: 'someError'});
+
+        // Set collections to null to simulate collections failure.
+        wallpaperProvider.setCollections(null);
+
+        // Assume that collections are loaded before local images.
+        const collectionsPromise =
+            fetchCollections(wallpaperProvider, personalizationStore);
+        const localImagesPromise =
+            getLocalImages(wallpaperProvider, personalizationStore);
+
+        await collectionsPromise;
+
+        assertFalse(personalizationStore.data.loading.collections);
+        assertEquals(null, personalizationStore.data.backdrop.collections);
+
+        await localImagesPromise;
+
+        assertFalse(personalizationStore.data.loading.local.images);
+        assertDeepEquals(
+            wallpaperProvider.localImages,
+            personalizationStore.data.local.images);
+
+        assertDeepEquals(
+            [
+              {
+                name: 'begin_load_local_images',
+              },
+              {
+                name: 'set_collections',
+                collections: null,
+              },
+              {name: 'set_local_images', images: wallpaperProvider.localImages},
+            ],
+            personalizationStore.actions,
+        );
+
+
+        assertDeepEquals(
+            [
+              // Begin load local images
+              {
+                'error': null,
+              },
+              // Set collections.
+              // Collections are completed loading with null value
+              // but local images are not yet done, no error displays.
+              {
+                'error': null,
+              },
+              // Set local images.
+              // Error displays once local images are loaded.
+              {
+                'error': loadTimeData.getString('networkError'),
+              },
+            ],
+            personalizationStore.states.map(filterAndFlattenState(['error'])));
+      });
+});
\ No newline at end of file
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.js b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.js
index be346ab9d..9490a018 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.js
@@ -6,7 +6,7 @@
 import {emptyState} from 'chrome://personalization/trusted/personalization_state.js';
 import {promisifyIframeFunctionsForTesting, WallpaperCollections} from 'chrome://personalization/trusted/wallpaper/wallpaper_collections_element.js';
 
-import {assertDeepEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 import {waitAfterNextRender} from '../../test_util.js';
 
 import {assertWindowObjectsEqual, baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
@@ -182,6 +182,45 @@
     assertDeepEquals(wallpaperProvider.localImages, data);
   });
 
+  test('sends collections and local images when no internet', async () => {
+    const {
+      sendCollections: sendCollectionsPromise,
+      sendLocalImages: sendLocalImagesPromise
+    } = promisifyIframeFunctionsForTesting();
+
+    wallpaperCollectionsElement = initElement(WallpaperCollections.is);
+
+    personalizationStore.data.loading = {
+      ...personalizationStore.data.loading,
+      collections: false,
+      local: {images: false}
+    };
+    personalizationStore.data.local.images = wallpaperProvider.localImages;
+    // Simulate online collections failed to load when no internet connection.
+    personalizationStore.data.backdrop.collections = null;
+    personalizationStore.notifyObservers();
+
+    // Wait for |sendCollections| to be called.
+    let [target, data] = await sendCollectionsPromise;
+    await waitAfterNextRender(wallpaperCollectionsElement);
+
+    const iframe =
+        wallpaperCollectionsElement.shadowRoot.querySelector('iframe');
+    assertFalse(iframe.hidden);
+
+    assertWindowObjectsEqual(iframe.contentWindow, target);
+    assertEquals(null, data);
+
+    // Wait for |sendLocalImages| to be called.
+    [target, data] = await sendLocalImagesPromise;
+    await waitAfterNextRender(wallpaperCollectionsElement);
+
+    assertFalse(iframe.hidden);
+
+    assertWindowObjectsEqual(iframe.contentWindow, target);
+    assertDeepEquals(wallpaperProvider.localImages, data);
+  });
+
   test('shows error when fails to load', async () => {
     wallpaperCollectionsElement = initElement(WallpaperCollections.is);
 
@@ -193,8 +232,10 @@
     personalizationStore.data.loading = {
       ...personalizationStore.data.loading,
       collections: false,
+      local: {images: false},
     };
     personalizationStore.data.backdrop.collections = null;
+    personalizationStore.data.local.images = null;
     personalizationStore.notifyObservers();
     await waitAfterNextRender(wallpaperCollectionsElement);
 
diff --git a/chrome/test/data/webui/welcome/BUILD.gn b/chrome/test/data/webui/welcome/BUILD.gn
index 39eeb0d..d172965 100644
--- a/chrome/test/data/webui/welcome/BUILD.gn
+++ b/chrome/test/data/webui/welcome/BUILD.gn
@@ -22,9 +22,9 @@
   out_dir = "$target_gen_dir/tsc"
   tsconfig_base = "tsconfig_base.json"
   path_mappings = [
-    "chrome://welcome/*|" + rebase_path(
-            "$root_gen_dir/chrome/browser/resources/welcome/preprocessed/*",
-            target_gen_dir),
+    "chrome://welcome/*|" +
+        rebase_path("$root_gen_dir/chrome/browser/resources/welcome/tsc/*",
+                    target_gen_dir),
     "chrome://webui-test/*|" +
         rebase_path("$root_gen_dir/chrome/test/data/webui/tsc/*",
                     target_gen_dir),
@@ -47,6 +47,6 @@
     "welcome_app_test.ts",
   ]
   definitions = [ "//tools/typescript/definitions/bookmarks.d.ts" ]
-  deps = [ "//chrome/browser/resources/welcome:build" ]
+  deps = [ "//chrome/browser/resources/welcome:build_ts" ]
   extra_deps = [ "..:generate_definitions" ]
 }
diff --git a/chrome/test/media_router/media_router_e2e_ui_browsertest.cc b/chrome/test/media_router/media_router_e2e_ui_browsertest.cc
index 12219d0..eaf0e0e 100644
--- a/chrome/test/media_router/media_router_e2e_ui_browsertest.cc
+++ b/chrome/test/media_router/media_router_e2e_ui_browsertest.cc
@@ -105,7 +105,7 @@
     test_ui_->ShowDialog();
   test_ui_->WaitForSink(receiver_);
   test_ui_->StopCasting(receiver_);
-  test_ui_->WaitUntilNoRoutes();
+  WaitUntilNoRoutes(web_contents);
   test_ui_->HideDialog();
 }
 
diff --git a/chrome/test/media_router/media_router_integration_browsertest.cc b/chrome/test/media_router/media_router_integration_browsertest.cc
index 0ef4b5d1..2423140 100644
--- a/chrome/test/media_router/media_router_integration_browsertest.cc
+++ b/chrome/test/media_router/media_router_integration_browsertest.cc
@@ -91,6 +91,23 @@
   return session_id;
 }
 
+// Routes observer that calls a callback once there are no routes.
+class NoRoutesObserver : public MediaRoutesObserver {
+ public:
+  NoRoutesObserver(MediaRouter* router, base::OnceClosure callback)
+      : MediaRoutesObserver(router), callback_(std::move(callback)) {}
+
+  ~NoRoutesObserver() override = default;
+
+  void OnRoutesUpdated(const std::vector<MediaRoute>& routes) override {
+    if (callback_ && routes.empty())
+      std::move(callback_).Run();
+  }
+
+ private:
+  base::OnceClosure callback_;
+};
+
 }  // namespace
 
 MediaRouterIntegrationBrowserTest::MediaRouterIntegrationBrowserTest() {
@@ -180,6 +197,23 @@
   run_loop.Run();
 }
 
+void MediaRouterIntegrationBrowserTest::WaitUntilNoRoutes(
+    WebContents* web_contents) {
+  if (!test_provider_->HasRoutes())
+    return;
+
+  // FIXME: There can't be a good reason to use the observer API to check for
+  // routes asynchronously, which is fragile.  However, some browser tests rely
+  // on this behavior.  Either add a callback parameter to TerminateRoute, or
+  // add pass callback to the TestProvider to run when all routes are gone.
+  base::RunLoop run_loop;
+  NoRoutesObserver no_routes_observer(
+      MediaRouterFactory::GetApiForBrowserContext(
+          web_contents->GetBrowserContext()),
+      run_loop.QuitClosure());
+  run_loop.Run();
+}
+
 void MediaRouterIntegrationBrowserTest::ExecuteJavaScriptAPI(
     WebContents* web_contents,
     const std::string& script) {
@@ -385,7 +419,7 @@
   WebContents* web_contents = StartSessionWithTestPageAndChooseSink();
   CheckSessionValidity(web_contents);
   ExecuteJavaScriptAPI(web_contents, kTerminateSessionScript);
-  test_ui_->WaitUntilNoRoutes();
+  WaitUntilNoRoutes(web_contents);
 }
 
 void MediaRouterIntegrationBrowserTest::RunSendMessageTest(
@@ -425,7 +459,7 @@
   ASSERT_EQ(session_id, reconnected_session_id);
 
   ExecuteJavaScriptAPI(web_contents, kTerminateSessionScript);
-  test_ui_->WaitUntilNoRoutes();
+  WaitUntilNoRoutes(web_contents);
 }
 
 void MediaRouterIntegrationBrowserTest::RunFailedReconnectSessionTest() {
@@ -442,7 +476,7 @@
                        base::StringPrintf(kCheckReconnectSessionFailsScript,
                                           session_id.c_str()));
   ExecuteJavaScriptAPI(web_contents, kTerminateSessionScript);
-  test_ui_->WaitUntilNoRoutes();
+  WaitUntilNoRoutes(web_contents);
 }
 
 void MediaRouterIntegrationBrowserTest::SetEnableMediaRouter(bool enable) {
@@ -693,7 +727,7 @@
   // If we tear down before route observers are notified of route termination,
   // MediaRouter will create another TerminateRoute() request which will have a
   // dangling Mojo callback at shutdown. So we must wait for the update.
-  test_ui_->WaitUntilNoRoutes();
+  WaitUntilNoRoutes(GetActiveWebContents());
 }
 
 IN_PROC_BROWSER_TEST_P(MediaRouterIntegrationIncognitoBrowserTest,
@@ -703,7 +737,7 @@
   // If we tear down before route observers are notified of route termination,
   // MediaRouter will create another TerminateRoute() request which will have a
   // dangling Mojo callback at shutdown. So we must wait for the update.
-  test_ui_->WaitUntilNoRoutes();
+  WaitUntilNoRoutes(GetActiveWebContents());
 }
 
 INSTANTIATE_MEDIA_ROUTER_INTEGRATION_BROWER_TEST_SUITE(
diff --git a/chrome/test/media_router/media_router_integration_browsertest.h b/chrome/test/media_router/media_router_integration_browsertest.h
index bb57cee..7d2314cf 100644
--- a/chrome/test/media_router/media_router_integration_browsertest.h
+++ b/chrome/test/media_router/media_router_integration_browsertest.h
@@ -186,6 +186,8 @@
   // Wait for a specific time.
   void Wait(base::TimeDelta timeout);
 
+  void WaitUntilNoRoutes(content::WebContents* web_contents);
+
   // Test API for manipulating the UI.
   raw_ptr<MediaRouterUiForTestBase> test_ui_ = nullptr;
 
diff --git a/chrome/test/media_router/media_router_integration_ui_browsertest.cc b/chrome/test/media_router/media_router_integration_ui_browsertest.cc
index 45a6aa0..d79670e 100644
--- a/chrome/test/media_router/media_router_integration_ui_browsertest.cc
+++ b/chrome/test/media_router/media_router_integration_ui_browsertest.cc
@@ -31,7 +31,7 @@
   ASSERT_EQ("Test Route", test_ui_->GetStatusTextForSink(receiver_));
 
   test_ui_->StopCasting(receiver_);
-  test_ui_->WaitUntilNoRoutes();
+  WaitUntilNoRoutes(GetActiveWebContents());
   // TODO(takumif): Remove the HideCastDialog() call once the dialog can close
   // on its own.
   test_ui_->HideDialog();
diff --git a/chrome/test/media_router/media_router_ui_for_test_base.cc b/chrome/test/media_router/media_router_ui_for_test_base.cc
index 005b8224..bec76a12 100644
--- a/chrome/test/media_router/media_router_ui_for_test_base.cc
+++ b/chrome/test/media_router/media_router_ui_for_test_base.cc
@@ -26,24 +26,6 @@
   return CreateMouseEvent(ui::ET_MOUSE_RELEASED);
 }
 
-// Routes observer that calls a callback once there are no routes.
-class NoRoutesObserver : public MediaRoutesObserver {
- public:
-  NoRoutesObserver(MediaRouter* router, base::OnceClosure callback)
-      : MediaRoutesObserver(router), callback_(std::move(callback)) {}
-  ~NoRoutesObserver() override = default;
-
-  void OnRoutesUpdated(
-      const std::vector<MediaRoute>& routes,
-      const std::vector<MediaRoute::Id>& joinable_route_ids) override {
-    if (callback_ && routes.empty())
-      std::move(callback_).Run();
-  }
-
- private:
-  base::OnceClosure callback_;
-};
-
 }  // namespace
 
 void MediaRouterUiForTestBase::TearDown() {
@@ -59,15 +41,6 @@
   StopCasting(GetSinkButton(sink_name));
 }
 
-void MediaRouterUiForTestBase::WaitUntilNoRoutes() {
-  base::RunLoop run_loop;
-  NoRoutesObserver no_routes_observer(
-      MediaRouterFactory::GetApiForBrowserContext(
-          web_contents_->GetBrowserContext()),
-      run_loop.QuitClosure());
-  run_loop.Run();
-}
-
 MediaRoute::Id MediaRouterUiForTestBase::GetRouteIdForSink(
     const std::string& sink_name) const {
   CastDialogSinkButton* sink_button = GetSinkButton(sink_name);
diff --git a/chrome/test/media_router/media_router_ui_for_test_base.h b/chrome/test/media_router/media_router_ui_for_test_base.h
index 59f704c..76d7521 100644
--- a/chrome/test/media_router/media_router_ui_for_test_base.h
+++ b/chrome/test/media_router/media_router_ui_for_test_base.h
@@ -49,7 +49,6 @@
   virtual void WaitForAnyRoute() = 0;
   virtual void WaitForDialogShown() = 0;
   virtual void WaitForDialogHidden() = 0;
-  void WaitUntilNoRoutes();
 
   // These methods require that the dialog is shown, and the sink specified by
   // |sink_name| is in the dialog.
diff --git a/chrome/test/ppapi/ppapi_test.h b/chrome/test/ppapi/ppapi_test.h
index 8f0bc4f..65019b9 100644
--- a/chrome/test/ppapi/ppapi_test.h
+++ b/chrome/test/ppapi/ppapi_test.h
@@ -187,21 +187,6 @@
   void SetUpCommandLine(base::CommandLine* command_line) override;
 };
 
-// Test Non-SFI Mode, using PNaCl toolchain to produce nexes.
-class PPAPINaClPNaClNonSfiTest : public PPAPINaClTest {
- public:
-  void SetUpCommandLine(base::CommandLine* command_line) override;
-
-  std::string BuildQuery(const std::string& base,
-                         const std::string& test_case) override;
-};
-
-class PPAPIPrivateNaClPNaClNonSfiTest : public PPAPINaClPNaClNonSfiTest {
- protected:
-  void SetUpCommandLine(base::CommandLine* command_line) override;
-};
-
-
 class PPAPINaClTestDisallowedSockets : public PPAPITestBase {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override;
diff --git a/chrome/updater/mac/install_from_archive.mm b/chrome/updater/mac/install_from_archive.mm
index c1e348c..70d090ef 100644
--- a/chrome/updater/mac/install_from_archive.mm
+++ b/chrome/updater/mac/install_from_archive.mm
@@ -156,7 +156,7 @@
     absl::optional<base::FilePath> ksadmin_path =
         GetKSAdminPath(GetUpdaterScope());
     if (ksadmin_path) {
-      env_path = base::StrCat({env_path, ":", (*ksadmin_path).value()});
+      env_path = base::StrCat({env_path, ":", ksadmin_path->DirName().value()});
     }
 
     base::LaunchOptions options;
diff --git a/chromecast/app/BUILD.gn b/chromecast/app/BUILD.gn
index 0b84bbd..d8f560f 100644
--- a/chromecast/app/BUILD.gn
+++ b/chromecast/app/BUILD.gn
@@ -23,7 +23,6 @@
     "//chromecast/common",
     "//chromecast/common:resource_delegate",
     "//chromecast/gpu",
-    "//chromecast/renderer",
     "//chromecast/utility",
     "//components/crash/core/common:crash_key",
     "//content/public/app",
@@ -32,47 +31,18 @@
     "//ui/base",
   ]
 
+  if (enable_cast_media_runtime) {
+    deps += [ "//chromecast/renderer:renderer_base" ]
+  } else {
+    deps += [ "//chromecast/renderer" ]
+  }
+
   if (!is_fuchsia) {
     # TODO(crbug.com/1226159): Complete crash reporting integration on Fuchsia.
     deps += [ "//components/crash/core/app" ]
   }
 }
 
-if (enable_cast_media_runtime) {
-  cast_source_set("app_core") {
-    sources = [
-      "cast_main_delegate.cc",
-      "cast_main_delegate.h",
-    ]
-
-    public_deps = [ "//base" ]
-
-    deps = [
-      ":cast_crash_client",
-      "//chromecast:chromecast_buildflags",
-      "//chromecast/base",
-      "//chromecast/browser",
-      "//chromecast/browser/migration",
-      "//chromecast/cast_core:cast_runtime_content_client_factories",
-      "//chromecast/common",
-      "//chromecast/common:resource_delegate",
-      "//chromecast/gpu",
-      "//chromecast/renderer:renderer_base",
-      "//chromecast/utility",
-      "//components/crash/core/common:crash_key",
-      "//content/public/app",
-      "//content/public/browser",
-      "//content/public/common",
-      "//ui/base",
-    ]
-
-    if (!is_fuchsia) {
-      # TODO(crbug.com/753619): Enable crash reporting on Fuchsia.
-      deps += [ "//components/crash/core/app" ]
-    }
-  }
-}
-
 cast_source_set("cast_crash_client") {
   sources = []
 
diff --git a/chromecast/browser/cast_display_configurator.cc b/chromecast/browser/cast_display_configurator.cc
index f0c789d..6349cf4 100644
--- a/chromecast/browser/cast_display_configurator.cc
+++ b/chromecast/browser/cast_display_configurator.cc
@@ -246,7 +246,15 @@
     bool config_success) {
   DCHECK(display);
   DCHECK(mode);
-  DCHECK_EQ(display, display_);
+
+  // Discard events for previous configurations. It is safe to discard since a
+  // new configuration round was initiated and we're waiting for another
+  // OnDisplayConfigured() event with the up-to-date display to arrive.
+  //
+  // This typically only happens when there's crashes and the state updates at
+  // the same time old notifications are received.
+  if (display != display_)
+    return;
 
   const gfx::Rect bounds(origin, mode->size());
   DVLOG(1) << __func__ << " success=" << config_success
diff --git a/chromecast/cast_core/BUILD.gn b/chromecast/cast_core/BUILD.gn
index 50b60a0..07da09b 100644
--- a/chromecast/cast_core/BUILD.gn
+++ b/chromecast/cast_core/BUILD.gn
@@ -4,26 +4,6 @@
 
 import("//chromecast/chromecast.gni")
 
-group("cast_core") {
-  deps = [
-    ":browser",
-    ":grpc_webui",
-    ":message_port",
-    ":metrics_recorder",
-    ":renderer",
-    ":runtime_service",
-    ":streaming_receiver_session_client",
-  ]
-
-  if (is_linux) {
-    deps += [ ":application_dispatch" ]
-  }
-
-  if (enable_cast_media_runtime) {
-    deps += [ ":cast_runtime_content_client_factories" ]
-  }
-}
-
 cast_source_set("cors_exempt_headers") {
   sources = [
     "cors_exempt_headers.cc",
@@ -150,72 +130,109 @@
   }
 }
 
-if (is_linux) {
-  cast_source_set("core_service_bindings") {
-    sources = [
-      "bindings_manager_web_runtime.cc",
-      "bindings_manager_web_runtime.h",
-    ]
+cast_source_set("core_service_bindings") {
+  sources = [
+    "bindings_manager_web_runtime.cc",
+    "bindings_manager_web_runtime.h",
+  ]
 
-    public_deps = [
-      ":message_port",
-      "//base",
-      "//chromecast/bindings/public/mojom",
-      "//components/cast/api_bindings:manager",
-      "//components/cast/message_port",
-      "//components/cast/message_port:blink_message_port_adapter",
-      "//mojo/public/cpp/bindings",
-      "//third_party/cast_core/public/src/proto/v2:core_application_service_proto",
-      "//third_party/cast_core/public/src/proto/web:message_channel_proto",
-    ]
+  public_deps = [
+    ":message_port",
+    "//base",
+    "//chromecast/bindings:bindings_manager",
+    "//chromecast/bindings/public/mojom",
+    "//components/cast/api_bindings:manager",
+    "//components/cast/message_port",
+    "//components/cast/message_port:blink_message_port_adapter",
+    "//mojo/public/cpp/bindings",
+    "//third_party/cast_core/public/src/proto/v2:core_application_service_proto",
+    "//third_party/cast_core/public/src/proto/web:message_channel_proto",
+  ]
 
+  if (is_linux || is_chromeos || is_android) {
     deps = [ "//chromecast/bindings:bindings_manager_cast" ]
   }
+}
 
-  cast_source_set("application_dispatch") {
-    sources = [
-      "runtime_application_base.cc",
-      "runtime_application_base.h",
-      "runtime_application_dispatcher.cc",
-      "runtime_application_dispatcher.h",
-      "streaming_runtime_application.cc",
-      "streaming_runtime_application.h",
-      "web_runtime_application.cc",
-      "web_runtime_application.h",
-    ]
+cast_source_set("runtime_application") {
+  sources = [
+    "runtime_application.cc",
+    "runtime_application.h",
+  ]
 
-    public_deps = [
-      ":grpc_impl",
-      ":grpc_support",
-      ":grpc_webui",
-      ":metrics_recorder",
-      ":runtime_application",
-      "//base",
-      "//chromecast/browser:public",
-      "//chromecast/common:feature_constants",
-      "//components/cast_streaming/public/mojom",
-      "//components/url_rewrite/browser",
-      "//media/mojo/mojom",
-      "//third_party/cast_core/public/src/proto/common:application_config_proto",
-      "//third_party/cast_core/public/src/proto/core:cast_core_service_proto",
-      "//third_party/cast_core/public/src/proto/metrics:metrics_recorder_proto",
-      "//third_party/cast_core/public/src/proto/v2:core_application_service_proto",
-      "//third_party/grpc:grpc++",
-    ]
+  public_deps = [
+    "//third_party/cast_core/public/src/proto/common:application_config_proto",
+    "//third_party/cast_core/public/src/proto/runtime:runtime_service_proto",
+    "//url",
+  ]
+}
 
-    deps = [
-      ":core_service_bindings",
-      ":streaming_receiver_session_client",
-      "//chromecast/browser:browser_base",
-      "//chromecast/cast_core/url_rewrite",
-      "//components/cast/message_port",
-      "//components/cast_streaming/browser",
-      "//components/cast_streaming/public",
-      "//content/public/browser",
-      "//mojo/public/cpp/bindings",
-      "//third_party/blink/public/common:headers",
-      "//third_party/openscreen/src/cast/common:public",
-    ]
+cast_source_set("cast_runtime_service") {
+  sources = [
+    "cast_runtime_service.cc",
+    "cast_runtime_service.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//chromecast/media/cma/backend/proxy:headers",
+    "//chromecast/service",
+  ]
+}
+
+cast_source_set("cast_runtime_service_impl") {
+  sources = [
+    "cast_runtime_service_impl.cc",
+    "cast_runtime_service_impl.h",
+    "runtime_application_base.cc",
+    "runtime_application_base.h",
+    "runtime_application_dispatcher.cc",
+    "runtime_application_dispatcher.h",
+    "streaming_runtime_application.cc",
+    "streaming_runtime_application.h",
+    "web_runtime_application.cc",
+    "web_runtime_application.h",
+  ]
+
+  public_deps = [
+    ":cast_runtime_service",
+    ":grpc_impl",
+    ":grpc_support",
+    ":grpc_webui",
+    ":metrics_recorder",
+    ":runtime_application",
+    "//base",
+    "//chromecast/browser:public",
+    "//chromecast/common:feature_constants",
+    "//components/cast_streaming/public/mojom",
+    "//components/url_rewrite/browser",
+    "//media/mojo/mojom",
+    "//third_party/cast_core/public/src/proto/common:application_config_proto",
+    "//third_party/cast_core/public/src/proto/core:cast_core_service_proto",
+    "//third_party/cast_core/public/src/proto/metrics:metrics_recorder_proto",
+    "//third_party/cast_core/public/src/proto/v2:core_application_service_proto",
+    "//third_party/grpc:grpc++",
+  ]
+
+  deps = [
+    ":core_service_bindings",
+    ":streaming_receiver_session_client",
+    "//chromecast/browser:browser_base",
+    "//chromecast/cast_core/url_rewrite",
+    "//chromecast/metrics:cast_event_builder_simple",
+    "//components/cast/message_port",
+    "//components/cast_streaming/browser",
+    "//components/cast_streaming/public",
+    "//content/public/browser",
+    "//mojo/public/cpp/bindings",
+    "//third_party/blink/public/common:headers",
+    "//third_party/openscreen/src/cast/common:public",
+  ]
+
+  if (chromecast_branding == "public") {
+    sources += [ "cast_runtime_service_simple.cc" ]
+  } else {
+    deps += [ "//chromecast/internal/cast_core/runtime:cast_runtime_service" ]
   }
 }
 
@@ -241,6 +258,15 @@
     "//content/public/renderer",
     "//media",
   ]
+
+  if (enable_cast_media_runtime) {
+    sources += [ "cast_content_renderer_client_factory.cc" ]
+  }
+
+  # TODO(vigeni): Doesn't look like this is needed.
+  if (chromecast_branding != "public") {
+    deps += [ "//chromecast/internal/cast_core/runtime:renderer" ]
+  }
 }
 
 cast_source_set("browser") {
@@ -251,11 +277,10 @@
   ]
 
   deps = [
+    ":cast_runtime_service",
     ":cors_exempt_headers",
     ":runtime_application",
-    ":runtime_service",
     "//base",
-    "//chromecast/browser",
     "//components/url_rewrite/browser",
     "//components/url_rewrite/common",
     "//content/public/common",
@@ -263,8 +288,20 @@
     "//third_party/cast_core/public/src/proto/runtime:runtime_service_proto",
   ]
 
+  public_deps = [
+    "//chromecast/browser",
+  ]
+
+  if (enable_cast_media_runtime) {
+    sources += [ "cast_content_browser_client_factory.cc" ]
+  } else {
+    deps += [ "//chromecast/browser:simple_client" ]
+  }
+
   if (chromecast_branding == "public") {
     sources += [ "cast_runtime_content_browser_client_simple.cc" ]
+  } else {
+    deps += [ "//chromecast/internal/cast_core/runtime:browser" ]
   }
 }
 
@@ -293,100 +330,22 @@
   ]
 }
 
-cast_source_set("runtime_application") {
-  sources = [
-    "runtime_application.cc",
-    "runtime_application.h",
-  ]
-
-  public_deps = [
-    "//third_party/cast_core/public/src/proto/common:application_config_proto",
-    "//third_party/cast_core/public/src/proto/runtime:runtime_service_proto",
-    "//url",
-  ]
-}
-
-cast_source_set("runtime_service") {
-  sources = [
-    "cast_runtime_service.cc",
-    "cast_runtime_service.h",
-  ]
-
-  deps = [
-    "//base",
-    "//chromecast:chromecast_buildflags",
-    "//chromecast/browser:browser_base",
-    "//chromecast/metrics:cast_event_builder_simple",
-    "//chromecast/service",
-  ]
-
-  public_deps = [ "//chromecast/media/cma/backend/proxy:headers" ]
-
-  if (!enable_cast_media_runtime) {
-    sources += [ "cast_runtime_service_simple.cc" ]
-  } else {
-    sources += [
-      "cast_runtime_service_impl.cc",
-      "cast_runtime_service_impl.h",
-    ]
-
-    public_deps += [
-      ":application_dispatch",
-      ":metrics_recorder",
-    ]
-
-    deps += [ "//chromecast/metrics:cast_event_builder_simple" ]
-
-    if (chromecast_branding == "public") {
-      sources += [ "cast_runtime_service_impl_factory.cc" ]
-    }
-  }
-}
-
-if (enable_cast_media_runtime) {
-  cast_source_set("cast_runtime_content_client_factories") {
-    sources = [
-      "cast_content_browser_client_factory_runtime.cc",
-      "cast_content_renderer_client_factory_runtime.cc",
-    ]
-
-    deps = [
-      ":browser",
-      ":cors_exempt_headers",
-      ":renderer",
-      "//base",
-      "//chromecast/browser",
-      "//chromecast/renderer:renderer_base",
-    ]
-
-    if (chromecast_branding != "public") {
-      deps += [ "//chromecast/internal/cast_core/services" ]
-    }
-  }
-}
-
 # When built for use with Cast Core, enble_cast_media_runtime must be true.
 cast_executable("core_runtime_simple") {
   sources = [ "cast_runtime_main.cc" ]
 
   deps = [
-    ":cast_core",
+    ":browser",
+    ":cast_runtime_service_impl",
+    ":renderer",
     "//chromecast:cast_shell_pak",
     "//chromecast:chromecast_locales_pak",
+    "//chromecast/app",
     "//chromecast/base:default_create_sys_info",
     "//chromecast/browser:simple_main_parts",
     "//chromecast/utility:simple_client",
     "//content/public/app",
   ]
-
-  if (enable_cast_media_runtime) {
-    deps += [ "//chromecast/app:app_core" ]
-  } else {
-    deps += [
-      "//chromecast/app",
-      "//chromecast/browser:simple_client",
-    ]
-  }
 }
 
 executable("demo_platform_service") {
@@ -404,24 +363,15 @@
   deps = [ "//third_party/grpc:grpc++" ]
 }
 
-cast_source_set("cast_core_test_utils") {
+# TODO(b/194439829): Upstream additional metrics unit tests.
+cast_source_set("unit_tests") {
   testonly = true
 
   sources = [
+    "cast_runtime_histogram_flattener_unittest.cc",
     "cast_runtime_metrics_test_helpers.cc",
     "cast_runtime_metrics_test_helpers.h",
-  ]
-
-  deps = [
-    "//third_party/cast_core/public/src/proto/metrics:metrics_recorder_proto",
-    "//third_party/metrics_proto",
-  ]
-}
-
-# TODO(b/194439829): Upstream additional metrics unit tests.
-test("cast_cast_core_unittests") {
-  sources = [
-    "cast_runtime_histogram_flattener_unittest.cc",
+    "cast_runtime_service_dummy.cc",
     "cors_exempt_headers_unittest.cc",
     "grpc_resource_data_source_unittest.cc",
     "streaming_receiver_session_client_unittest.cc",
@@ -429,7 +379,7 @@
 
   deps = [
     ":browser",
-    ":cast_core_test_utils",
+    ":cast_runtime_service",
     ":cors_exempt_headers",
     ":grpc_webui",
     ":metrics_recorder",
@@ -437,7 +387,6 @@
     "//base",
     "//base/test:test_support",
     "//chromecast/base/metrics",
-    "//chromecast/browser:simple_client",
     "//chromecast/browser:simple_main_parts",
     "//chromecast/browser:test_support",
     "//chromecast/cast_core/url_rewrite:unit_tests",
@@ -449,9 +398,14 @@
     "//mojo/public/cpp/bindings",
     "//testing/gmock",
     "//testing/gtest",
+    "//third_party/cast_core/public/src/proto/metrics:metrics_recorder_proto",
     "//third_party/cast_core/public/src/proto/v2:core_application_service_proto",
     "//third_party/grpc:grpc++",
     "//third_party/inspector_protocol:crdtp",
     "//third_party/metrics_proto",
   ]
 }
+
+test("cast_cast_core_unittests") {
+  deps = [ ":unit_tests" ]
+}
diff --git a/chromecast/cast_core/cast_content_browser_client_factory_runtime.cc b/chromecast/cast_core/cast_content_browser_client_factory.cc
similarity index 100%
rename from chromecast/cast_core/cast_content_browser_client_factory_runtime.cc
rename to chromecast/cast_core/cast_content_browser_client_factory.cc
diff --git a/chromecast/cast_core/cast_content_renderer_client_factory_runtime.cc b/chromecast/cast_core/cast_content_renderer_client_factory.cc
similarity index 100%
rename from chromecast/cast_core/cast_content_renderer_client_factory_runtime.cc
rename to chromecast/cast_core/cast_content_renderer_client_factory.cc
diff --git a/chromecast/cast_core/cast_runtime_content_browser_client.cc b/chromecast/cast_core/cast_runtime_content_browser_client.cc
index 4ab7b0a..1af1429 100644
--- a/chromecast/cast_core/cast_runtime_content_browser_client.cc
+++ b/chromecast/cast_core/cast_runtime_content_browser_client.cc
@@ -34,15 +34,18 @@
     CastWebService* web_service,
     DisplaySettingsManager* display_settings_manager,
     shell::AccessibilityServiceImpl* accessibility_service) {
+  DCHECK(!cast_runtime_service_);
   auto network_context_getter = base::BindRepeating(
       [](CastRuntimeContentBrowserClient* client)
           -> network::mojom::NetworkContext* {
         return client->GetSystemNetworkContext();
       },
       this);
-  return CastRuntimeService::Create(
+  auto cast_runtime_service = CastRuntimeService::Create(
       GetMediaTaskRunner(), web_service, media_pipeline_backend_manager(),
       std::move(network_context_getter), pref_service, video_plane_controller);
+  cast_runtime_service_ = cast_runtime_service.get();
+  return cast_runtime_service;
 }
 
 void CastRuntimeContentBrowserClient::OverrideWebkitPrefs(
@@ -105,8 +108,9 @@
 std::unique_ptr<blink::URLLoaderThrottle>
 CastRuntimeContentBrowserClient::CreateUrlRewriteRulesThrottle(
     content::WebContents* web_contents) {
-  RuntimeApplication* app =
-      CastRuntimeService::GetInstance()->GetRuntimeApplication();
+  DCHECK(cast_runtime_service_);
+
+  RuntimeApplication* app = cast_runtime_service_->GetRuntimeApplication();
   DCHECK(app);
 
   url_rewrite::UrlRequestRewriteRulesManager* url_rewrite_rules_manager =
diff --git a/chromecast/cast_core/cast_runtime_content_browser_client.h b/chromecast/cast_core/cast_runtime_content_browser_client.h
index df16eeef..61fb5dd3 100644
--- a/chromecast/cast_core/cast_runtime_content_browser_client.h
+++ b/chromecast/cast_core/cast_runtime_content_browser_client.h
@@ -9,6 +9,7 @@
 
 namespace chromecast {
 
+class CastRuntimeService;
 class CastFeatureListCreator;
 
 class CastRuntimeContentBrowserClient : public shell::CastContentBrowserClient {
@@ -49,6 +50,10 @@
  private:
   std::unique_ptr<blink::URLLoaderThrottle> CreateUrlRewriteRulesThrottle(
       content::WebContents* web_contents);
+
+  // An instance of |CastRuntimeService| created once during the lifetime of the
+  // runtime.
+  CastRuntimeService* cast_runtime_service_ = nullptr;
 };
 
 }  // namespace chromecast
diff --git a/chromecast/cast_core/cast_runtime_service.cc b/chromecast/cast_core/cast_runtime_service.cc
index db60528..0772752 100644
--- a/chromecast/cast_core/cast_runtime_service.cc
+++ b/chromecast/cast_core/cast_runtime_service.cc
@@ -5,42 +5,7 @@
 #include "chromecast/cast_core/cast_runtime_service.h"
 
 namespace chromecast {
-namespace {
-
-static std::string kFakeAudioChannelEndpoint = "";
-
-}  // namespace
-
-CastRuntimeService::CastRuntimeService() = default;
 
 CastRuntimeService::~CastRuntimeService() = default;
 
-WebCryptoServer* CastRuntimeService::GetWebCryptoServer() {
-  return nullptr;
-}
-
-receiver::MediaManager* CastRuntimeService::GetMediaManager() {
-  return nullptr;
-}
-
-CastWebService* CastRuntimeService::GetCastWebService() {
-  return nullptr;
-}
-
-RuntimeApplication* CastRuntimeService::GetRuntimeApplication() {
-  return nullptr;
-}
-
-void CastRuntimeService::InitializeInternal() {}
-
-void CastRuntimeService::FinalizeInternal() {}
-
-void CastRuntimeService::StartInternal() {}
-
-void CastRuntimeService::StopInternal() {}
-
-const std::string& CastRuntimeService::GetAudioChannelEndpoint() {
-  return kFakeAudioChannelEndpoint;
-}
-
 }  // namespace chromecast
diff --git a/chromecast/cast_core/cast_runtime_service.h b/chromecast/cast_core/cast_runtime_service.h
index a4301305..f7b63923 100644
--- a/chromecast/cast_core/cast_runtime_service.h
+++ b/chromecast/cast_core/cast_runtime_service.h
@@ -48,6 +48,7 @@
   using NetworkContextGetter =
       base::RepeatingCallback<network::mojom::NetworkContext*()>;
 
+  // Creates an instance of |CastRuntimeService| interface.
   static std::unique_ptr<CastRuntimeService> Create(
       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
       CastWebService* web_service,
@@ -56,28 +57,20 @@
       PrefService* pref_service,
       media::VideoPlaneController* video_plane_controller);
 
-  // Returns current instance of CastRuntimeService in the browser process.
-  static CastRuntimeService* GetInstance();
-
-  CastRuntimeService();
   ~CastRuntimeService() override;
 
-  virtual WebCryptoServer* GetWebCryptoServer();
-  virtual receiver::MediaManager* GetMediaManager();
+  // Returns WebCryptoServer.
+  virtual WebCryptoServer* GetWebCryptoServer() = 0;
+
+  // Returns MediaManager.
+  virtual receiver::MediaManager* GetMediaManager() = 0;
+
   // Returns a pointer to CastWebService object with lifespan
   // equal to CastRuntimeService main object.
-  virtual CastWebService* GetCastWebService();
+  virtual CastWebService* GetCastWebService() = 0;
+
   // Returns a pointer to RuntimeApplication.
-  virtual RuntimeApplication* GetRuntimeApplication();
-
-  // CastService overrides.
-  void InitializeInternal() override;
-  void FinalizeInternal() override;
-  void StartInternal() override;
-  void StopInternal() override;
-
-  // CastRuntimeAudioChannelEndpointManager overrides.
-  const std::string& GetAudioChannelEndpoint() override;
+  virtual RuntimeApplication* GetRuntimeApplication() = 0;
 };
 
 }  // namespace chromecast
diff --git a/chromecast/cast_core/cast_runtime_service_dummy.cc b/chromecast/cast_core/cast_runtime_service_dummy.cc
new file mode 100644
index 0000000..c6b0fe3
--- /dev/null
+++ b/chromecast/cast_core/cast_runtime_service_dummy.cc
@@ -0,0 +1,50 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/cast_core/cast_runtime_service.h"
+
+#include "base/no_destructor.h"
+#include "base/task/single_thread_task_runner.h"
+
+namespace chromecast {
+
+namespace {
+
+class CastRuntimeServiceDummy : public CastRuntimeService {
+ public:
+  ~CastRuntimeServiceDummy() override = default;
+
+  // CastRuntimeService implementation:
+  WebCryptoServer* GetWebCryptoServer() override { return nullptr; }
+  receiver::MediaManager* GetMediaManager() override { return nullptr; }
+  CastWebService* GetCastWebService() override { return nullptr; }
+  RuntimeApplication* GetRuntimeApplication() override { return nullptr; }
+
+  // CastService implementation:
+  void InitializeInternal() override {}
+  void FinalizeInternal() override {}
+  void StartInternal() override {}
+  void StopInternal() override {}
+
+  // CastRuntimeAudioChannelEndpointManager implementation:
+  const std::string& GetAudioChannelEndpoint() override {
+    static const std::string kFakeAudioChannelEndpoint = "";
+    return kFakeAudioChannelEndpoint;
+  }
+};
+
+}  // namespace
+
+// static
+std::unique_ptr<CastRuntimeService> CastRuntimeService::Create(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    CastWebService* web_service,
+    media::MediaPipelineBackendManager* media_pipeline_backend_manager,
+    CastRuntimeService::NetworkContextGetter network_context_getter,
+    PrefService* pref_service,
+    media::VideoPlaneController* video_plane_controller) {
+  return std::make_unique<CastRuntimeServiceDummy>();
+}
+
+}  // namespace chromecast
diff --git a/chromecast/cast_core/cast_runtime_service_impl.cc b/chromecast/cast_core/cast_runtime_service_impl.cc
index e6a5a60..4e334890 100644
--- a/chromecast/cast_core/cast_runtime_service_impl.cc
+++ b/chromecast/cast_core/cast_runtime_service_impl.cc
@@ -17,14 +17,6 @@
 
 }  // namespace
 
-// static
-CastRuntimeService* CastRuntimeService::GetInstance() {
-  DCHECK(shell::CastBrowserProcess::GetInstance());
-  auto* cast_service = shell::CastBrowserProcess::GetInstance()->cast_service();
-  DCHECK(cast_service);
-  return static_cast<CastRuntimeService*>(cast_service);
-}
-
 CastRuntimeServiceImpl::CastRuntimeServiceImpl(
     CastWebService* web_service,
     NetworkContextGetter network_context_getter)
@@ -33,9 +25,11 @@
 
 CastRuntimeServiceImpl::~CastRuntimeServiceImpl() = default;
 
-void CastRuntimeServiceImpl::StartInternal() {
-  CastRuntimeService::StartInternal();
+void CastRuntimeServiceImpl::InitializeInternal() {}
 
+void CastRuntimeServiceImpl::FinalizeInternal() {}
+
+void CastRuntimeServiceImpl::StartInternal() {
   auto* command_line = base::CommandLine::ForCurrentProcess();
   std::string runtime_id =
       command_line->GetSwitchValueASCII(kCastCoreRuntimeIdSwitch);
@@ -47,8 +41,6 @@
 }
 
 void CastRuntimeServiceImpl::StopInternal() {
-  CastRuntimeService::StopInternal();
-
   app_dispatcher_.Stop();
 }
 
@@ -60,6 +52,14 @@
   return app_dispatcher_.GetCastMediaServiceGrpcEndpoint();
 }
 
+WebCryptoServer* CastRuntimeServiceImpl::GetWebCryptoServer() {
+  return nullptr;
+}
+
+receiver::MediaManager* CastRuntimeServiceImpl::GetMediaManager() {
+  return nullptr;
+}
+
 CastWebService* CastRuntimeServiceImpl::GetCastWebService() {
   return web_service_;
 }
diff --git a/chromecast/cast_core/cast_runtime_service_impl.h b/chromecast/cast_core/cast_runtime_service_impl.h
index 242a8ba..46353972 100644
--- a/chromecast/cast_core/cast_runtime_service_impl.h
+++ b/chromecast/cast_core/cast_runtime_service_impl.h
@@ -28,16 +28,24 @@
                          NetworkContextGetter network_context_getter);
   ~CastRuntimeServiceImpl() override;
 
-  // CastService overrides.
+ protected:
+  // CastService implementation:
+  void InitializeInternal() override;
+  void FinalizeInternal() override;
   void StartInternal() override;
   void StopInternal() override;
-  const std::string& GetAudioChannelEndpoint() override;
+
+  // CastRuntimeMetricsRecorder::EventBuilderFactory implementation:
+  std::unique_ptr<CastEventBuilder> CreateEventBuilder() override;
+
+  // CastRuntimeService implementation:
+  WebCryptoServer* GetWebCryptoServer() override;
+  receiver::MediaManager* GetMediaManager() override;
   CastWebService* GetCastWebService() override;
   RuntimeApplication* GetRuntimeApplication() override;
 
- protected:
-  // CastRuntimeMetricsRecorder::EventBuilderFactory overrides.
-  std::unique_ptr<CastEventBuilder> CreateEventBuilder() override;
+  // CastRuntimeAudioChannelEndpointManager implementation:
+  const std::string& GetAudioChannelEndpoint() override;
 
  private:
   CastWebService* const web_service_;
diff --git a/chromecast/cast_core/cast_runtime_service_impl_factory.cc b/chromecast/cast_core/cast_runtime_service_impl_factory.cc
deleted file mode 100644
index efb682a..0000000
--- a/chromecast/cast_core/cast_runtime_service_impl_factory.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/task/single_thread_task_runner.h"
-#include "chromecast/cast_core/cast_runtime_service.h"
-#include "chromecast/cast_core/cast_runtime_service_impl.h"
-
-namespace chromecast {
-
-std::unique_ptr<CastRuntimeService> CastRuntimeService::Create(
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-    CastWebService* web_service,
-    media::MediaPipelineBackendManager* media_pipeline_backend_manager,
-    CastRuntimeService::NetworkContextGetter network_context_getter,
-    PrefService* pref_service,
-    media::VideoPlaneController* video_plane_controller) {
-  return std::make_unique<CastRuntimeServiceImpl>(
-      web_service, std::move(network_context_getter));
-}
-
-}  // namespace chromecast
diff --git a/chromecast/cast_core/cast_runtime_service_simple.cc b/chromecast/cast_core/cast_runtime_service_simple.cc
index d64de3f..efb682a 100644
--- a/chromecast/cast_core/cast_runtime_service_simple.cc
+++ b/chromecast/cast_core/cast_runtime_service_simple.cc
@@ -2,14 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromecast/cast_core/cast_runtime_service.h"
-
-#include "base/no_destructor.h"
 #include "base/task/single_thread_task_runner.h"
+#include "chromecast/cast_core/cast_runtime_service.h"
+#include "chromecast/cast_core/cast_runtime_service_impl.h"
 
 namespace chromecast {
 
-// static
 std::unique_ptr<CastRuntimeService> CastRuntimeService::Create(
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     CastWebService* web_service,
@@ -17,14 +15,8 @@
     CastRuntimeService::NetworkContextGetter network_context_getter,
     PrefService* pref_service,
     media::VideoPlaneController* video_plane_controller) {
-  return std::make_unique<CastRuntimeService>();
-}
-
-CastRuntimeService* CastRuntimeService::GetInstance() {
-  // TODO(b/186668532): Instead use the CastService singleton instead of
-  // creating a new one with NoDestructor.
-  static base::NoDestructor<CastRuntimeService> g_instance;
-  return g_instance.get();
+  return std::make_unique<CastRuntimeServiceImpl>(
+      web_service, std::move(network_context_getter));
 }
 
 }  // namespace chromecast
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index b42413277..76b227d 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -2191,10 +2191,7 @@
         Use the QR code or <ph name="LINK_BEGIN">&lt;a id="rsuCodeDialogLink"&gt;</ph>this link<ph name="LINK_END">&lt;/a&gt;</ph> to retrieve an 8 character unlock code to perform RMA Server Unlock
       </message>
       <message name="IDS_SHIMLESS_RMA_RSU_CODE_LABEL" desc="Label for the RSU code input box.">
-        Enter the unlock code:
-      </message>
-      <message name="IDS_SHIMLESS_RMA_RSU_CODE_PLACEHOLDER" translateable="false" desc="Placeholder text for empty RSU code input box.">
-        Enter RSU Code
+        Enter the 8-character unlock code
       </message>
       <message name="IDS_SHIMLESS_RMA_RSU_CHALLENGE_DIALOG_TITLE" translateable="false" desc="Title for the dialog that shows the RSU challenge code link.">
         Go to this link for your RSU unlock code
diff --git a/chromeos/chromeos_strings_grd/IDS_SHIMLESS_RMA_RSU_CODE_LABEL.png.sha1 b/chromeos/chromeos_strings_grd/IDS_SHIMLESS_RMA_RSU_CODE_LABEL.png.sha1
index 740709f..3aaba88 100644
--- a/chromeos/chromeos_strings_grd/IDS_SHIMLESS_RMA_RSU_CODE_LABEL.png.sha1
+++ b/chromeos/chromeos_strings_grd/IDS_SHIMLESS_RMA_RSU_CODE_LABEL.png.sha1
@@ -1 +1 @@
-c1105993eb549b25da9b7b1b790cc6817f7c1b23
\ No newline at end of file
+32e93e3a03f8c7fbb1126237f873a0041b7542aa
\ No newline at end of file
diff --git a/chromeos/dbus/rmad/fake_rmad_client.cc b/chromeos/dbus/rmad/fake_rmad_client.cc
index 6a9e3f0b..c513c3ac 100644
--- a/chromeos/dbus/rmad/fake_rmad_client.cc
+++ b/chromeos/dbus/rmad/fake_rmad_client.cc
@@ -210,7 +210,8 @@
 }
 
 FakeRmadClient::FakeRmadClient() {
-  abort_rma_reply_.set_error(rmad::RMAD_ERROR_OK);
+  // Default to abortable.
+  SetAbortable(true);
 }
 FakeRmadClient::~FakeRmadClient() = default;
 
@@ -337,7 +338,8 @@
 }
 
 void FakeRmadClient::SetAbortable(bool abortable) {
-  abort_rma_reply_.set_error(abortable ? rmad::RMAD_ERROR_OK
+  // Abort RMA returns 'not in RMA' on success.
+  abort_rma_reply_.set_error(abortable ? rmad::RMAD_ERROR_RMA_NOT_REQUIRED
                                        : rmad::RMAD_ERROR_CANNOT_CANCEL_RMA);
 }
 
diff --git a/chromeos/dbus/rmad/fake_rmad_client_unittest.cc b/chromeos/dbus/rmad/fake_rmad_client_unittest.cc
index 134cb88..ecafbec 100644
--- a/chromeos/dbus/rmad/fake_rmad_client_unittest.cc
+++ b/chromeos/dbus/rmad/fake_rmad_client_unittest.cc
@@ -472,12 +472,12 @@
   }
 }
 
-TEST_F(FakeRmadClientTest, Abortable_Default_Ok) {
+TEST_F(FakeRmadClientTest, Abortable_Default_Rma_Not_Required) {
   base::RunLoop run_loop;
   client_->AbortRma(base::BindLambdaForTesting(
       [&](absl::optional<rmad::AbortRmaReply> response) {
         EXPECT_TRUE(response.has_value());
-        EXPECT_EQ(response->error(), rmad::RMAD_ERROR_OK);
+        EXPECT_EQ(response->error(), rmad::RMAD_ERROR_RMA_NOT_REQUIRED);
         run_loop.Quit();
       }));
   run_loop.RunUntilIdle();
@@ -495,13 +495,13 @@
   run_loop.RunUntilIdle();
 }
 
-TEST_F(FakeRmadClientTest, Abortable_SetTrue_Ok) {
+TEST_F(FakeRmadClientTest, Abortable_SetTrue_Rma_Not_Required) {
   fake_client_()->SetAbortable(true);
   base::RunLoop run_loop;
   client_->AbortRma(base::BindLambdaForTesting(
       [&](absl::optional<rmad::AbortRmaReply> response) {
         EXPECT_TRUE(response.has_value());
-        EXPECT_EQ(response->error(), rmad::RMAD_ERROR_OK);
+        EXPECT_EQ(response->error(), rmad::RMAD_ERROR_RMA_NOT_REQUIRED);
         run_loop.Quit();
       }));
   run_loop.RunUntilIdle();
diff --git a/chromeos/services/bluetooth_config/adapter_state_controller_impl.cc b/chromeos/services/bluetooth_config/adapter_state_controller_impl.cc
index e2a0781fa..8399cf7 100644
--- a/chromeos/services/bluetooth_config/adapter_state_controller_impl.cc
+++ b/chromeos/services/bluetooth_config/adapter_state_controller_impl.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "components/device_event_log/device_event_log.h"
+#include "device/bluetooth/chromeos/bluetooth_utils.h"
 
 namespace chromeos {
 namespace bluetooth_config {
@@ -104,8 +105,7 @@
                      weak_ptr_factory_.GetWeakPtr(), enabled),
       base::BindOnce(&AdapterStateControllerImpl::OnSetPoweredError,
                      weak_ptr_factory_.GetWeakPtr(), enabled));
-  // TODO(gordonseto): Add power metric here.
-
+  device::RecordPoweredState(enabled);
   // State has changed to kEnabling or kDisabling; notify observers.
   NotifyAdapterStateChanged();
 }
@@ -114,6 +114,10 @@
   BLUETOOTH_LOG(EVENT) << "Bluetooth " << (enabled ? "enabled" : "disabled")
                        << " successfully";
   in_progress_state_change_ = PowerStateChange::kNoChange;
+  device::PoweredStateOperation power_operation =
+      enabled ? device::PoweredStateOperation::kEnable
+              : device::PoweredStateOperation::kDisable;
+  device::RecordPoweredStateOperationResult(power_operation, /*success=*/true);
 
   // Adapter->IsPowered() won't immediately be updated to the new value when
   // SetPowered() finishes and this method is called. Don't call
@@ -125,7 +129,10 @@
 void AdapterStateControllerImpl::OnSetPoweredError(bool enabled) {
   BLUETOOTH_LOG(ERROR) << "Error attempting to "
                        << (enabled ? "enable" : "disable") << " Bluetooth";
-  // TODO(gordonseto): Add power metric here.
+  device::PoweredStateOperation power_operation =
+      enabled ? device::PoweredStateOperation::kEnable
+              : device::PoweredStateOperation::kDisable;
+  device::RecordPoweredStateOperationResult(power_operation, /*success=*/false);
   in_progress_state_change_ = PowerStateChange::kNoChange;
 
   // State is no longer kEnabling or kDisabling; notify observers.
diff --git a/chromeos/services/bluetooth_config/adapter_state_controller_impl_unittest.cc b/chromeos/services/bluetooth_config/adapter_state_controller_impl_unittest.cc
index 69faa33..3945270 100644
--- a/chromeos/services/bluetooth_config/adapter_state_controller_impl_unittest.cc
+++ b/chromeos/services/bluetooth_config/adapter_state_controller_impl_unittest.cc
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -123,6 +124,8 @@
 
   size_t GetNumObserverEvents() const { return fake_observer_.num_calls(); }
 
+  base::HistogramTester histogram_tester;
+
  private:
   base::test::TaskEnvironment task_environment_;
 
@@ -154,70 +157,140 @@
 TEST_F(AdapterStateControllerImplTest, SetBluetoothEnabledState) {
   EXPECT_EQ(mojom::BluetoothSystemState::kEnabled, GetAdapterState());
 
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", false,
+                                     0);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", true,
+                                     0);
+
   SetBluetoothEnabledState(/*enabled=*/false);
   EXPECT_EQ(mojom::BluetoothSystemState::kDisabling, GetAdapterState());
   EXPECT_EQ(1u, GetNumObserverEvents());
 
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", false,
+                                     1);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", true,
+                                     0);
+
   InvokeSetPoweredCallback(/*expected_pending_state=*/false,
                            /*success=*/true);
   EXPECT_EQ(mojom::BluetoothSystemState::kDisabled, GetAdapterState());
   EXPECT_EQ(2u, GetNumObserverEvents());
+  histogram_tester.ExpectBucketCount(
+      "Bluetooth.ChromeOS.PoweredState.Disable.Result", true, 1);
+  histogram_tester.ExpectBucketCount(
+      "Bluetooth.ChromeOS.PoweredState.Enable.Result", true, 0);
 
   SetBluetoothEnabledState(/*enabled=*/true);
   EXPECT_EQ(mojom::BluetoothSystemState::kEnabling, GetAdapterState());
   EXPECT_EQ(3u, GetNumObserverEvents());
 
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", false,
+                                     1);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", true,
+                                     1);
+
   InvokeSetPoweredCallback(/*expected_pending_state=*/true,
                            /*success=*/true);
   EXPECT_EQ(mojom::BluetoothSystemState::kEnabled, GetAdapterState());
   EXPECT_EQ(4u, GetNumObserverEvents());
+  histogram_tester.ExpectBucketCount(
+      "Bluetooth.ChromeOS.PoweredState.Disable.Result", true, 1);
+  histogram_tester.ExpectBucketCount(
+      "Bluetooth.ChromeOS.PoweredState.Enable.Result", true, 1);
 }
 
 TEST_F(AdapterStateControllerImplTest, SetBluetoothEnabledState_Error) {
   EXPECT_EQ(mojom::BluetoothSystemState::kEnabled, GetAdapterState());
 
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", false,
+                                     0);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", true,
+                                     0);
+
   SetBluetoothEnabledState(/*enabled=*/false);
   EXPECT_EQ(mojom::BluetoothSystemState::kDisabling, GetAdapterState());
   EXPECT_EQ(1u, GetNumObserverEvents());
 
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", false,
+                                     1);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", true,
+                                     0);
+
   InvokeSetPoweredCallback(/*expected_pending_state=*/false,
                            /*success=*/false);
   EXPECT_EQ(mojom::BluetoothSystemState::kEnabled, GetAdapterState());
   EXPECT_EQ(2u, GetNumObserverEvents());
+  histogram_tester.ExpectBucketCount(
+      "Bluetooth.ChromeOS.PoweredState.Disable.Result", false, 1);
+  histogram_tester.ExpectBucketCount(
+      "Bluetooth.ChromeOS.PoweredState.Enable.Result", true, 0);
 }
 
 TEST_F(AdapterStateControllerImplTest, MultiplePowerChanges_SameChange) {
   EXPECT_EQ(mojom::BluetoothSystemState::kEnabled, GetAdapterState());
 
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", false,
+                                     0);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", true,
+                                     0);
+
   // Start disabling once.
   SetBluetoothEnabledState(/*enabled=*/false);
   EXPECT_EQ(mojom::BluetoothSystemState::kDisabling, GetAdapterState());
   EXPECT_EQ(1u, GetNumObserverEvents());
 
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", false,
+                                     1);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", true,
+                                     0);
+
   // Try disabling again, even though this is already in progress.
   SetBluetoothEnabledState(/*enabled=*/false);
   EXPECT_EQ(mojom::BluetoothSystemState::kDisabling, GetAdapterState());
   EXPECT_EQ(1u, GetNumObserverEvents());
 
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", false,
+                                     1);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", true,
+                                     0);
+
   InvokeSetPoweredCallback(/*expected_pending_state=*/false,
                            /*success=*/true);
   EXPECT_EQ(mojom::BluetoothSystemState::kDisabled, GetAdapterState());
   EXPECT_EQ(2u, GetNumObserverEvents());
+  histogram_tester.ExpectBucketCount(
+      "Bluetooth.ChromeOS.PoweredState.Disable.Result", true, 1);
+  histogram_tester.ExpectBucketCount(
+      "Bluetooth.ChromeOS.PoweredState.Enable.Result", true, 0);
 }
 
 TEST_F(AdapterStateControllerImplTest, MultiplePowerChanges_DifferentChange) {
   EXPECT_EQ(mojom::BluetoothSystemState::kEnabled, GetAdapterState());
 
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", false,
+                                     0);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", true,
+                                     0);
+
   // Start disabling.
   SetBluetoothEnabledState(/*enabled=*/false);
   EXPECT_EQ(mojom::BluetoothSystemState::kDisabling, GetAdapterState());
   EXPECT_EQ(1u, GetNumObserverEvents());
 
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", false,
+                                     1);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", true,
+                                     0);
+
   // Before the disable request finishes, start enabling; this simulates a user
   // very quickly clicking the on/off toggle.
   SetBluetoothEnabledState(/*enabled=*/true);
   EXPECT_EQ(mojom::BluetoothSystemState::kDisabling, GetAdapterState());
   EXPECT_EQ(1u, GetNumObserverEvents());
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", false,
+                                     1);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", true,
+                                     0);
 
   // Invoke the first callback; because there was a queued request, we should
   // now be enabling.
@@ -225,12 +298,25 @@
                            /*success=*/true);
   EXPECT_EQ(mojom::BluetoothSystemState::kEnabling, GetAdapterState());
   EXPECT_EQ(3u, GetNumObserverEvents());
+  histogram_tester.ExpectBucketCount(
+      "Bluetooth.ChromeOS.PoweredState.Disable.Result", true, 1);
+  histogram_tester.ExpectBucketCount(
+      "Bluetooth.ChromeOS.PoweredState.Enable.Result", true, 0);
 
   // Invoke the second request; we should now be enabled.
   InvokeSetPoweredCallback(/*expected_pending_state=*/true,
                            /*success=*/true);
   EXPECT_EQ(mojom::BluetoothSystemState::kEnabled, GetAdapterState());
   EXPECT_EQ(4u, GetNumObserverEvents());
+  histogram_tester.ExpectBucketCount(
+      "Bluetooth.ChromeOS.PoweredState.Disable.Result", true, 1);
+  histogram_tester.ExpectBucketCount(
+      "Bluetooth.ChromeOS.PoweredState.Enable.Result", true, 1);
+
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", false,
+                                     1);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.PoweredState", true,
+                                     1);
 }
 
 }  // namespace bluetooth_config
diff --git a/chromeos/services/bluetooth_config/device_conversion_util.cc b/chromeos/services/bluetooth_config/device_conversion_util.cc
index 0693810..a1d29a93 100644
--- a/chromeos/services/bluetooth_config/device_conversion_util.cc
+++ b/chromeos/services/bluetooth_config/device_conversion_util.cc
@@ -89,6 +89,10 @@
 
 mojom::DeviceBatteryInfoPtr ComputeBatteryInfo(
     const device::BluetoothDevice* device) {
+  // Only provide battery information for devices that are connected.
+  if (!device->IsConnected())
+    return nullptr;
+
   mojom::BatteryPropertiesPtr default_battery =
       ComputeBatteryInfoForBatteryType(
           device, device::BluetoothDevice::BatteryType::kDefault);
diff --git a/chromeos/services/bluetooth_config/device_conversion_util_unittest.cc b/chromeos/services/bluetooth_config/device_conversion_util_unittest.cc
index 4e36a76..6e548e0 100644
--- a/chromeos/services/bluetooth_config/device_conversion_util_unittest.cc
+++ b/chromeos/services/bluetooth_config/device_conversion_util_unittest.cc
@@ -46,6 +46,11 @@
     return mock_device_.get();
   }
 
+  void ChangeDeviceConnected(bool connected) {
+    ON_CALL(*mock_device_, IsConnected())
+        .WillByDefault(testing::Return(connected));
+  }
+
  private:
   scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> mock_adapter_;
   std::unique_ptr<testing::NiceMock<device::MockBluetoothDevice>> mock_device_;
@@ -73,7 +78,7 @@
 TEST_F(DeviceConversionUtilTest, TestConversion_DefaultBattery) {
   device::BluetoothDevice* device = InitDevice(
       /*bluetooth_class=*/0u, /*name=*/"name", /*address=*/"address",
-      /*paired=*/true, /*connected=*/true, /*is_blocked_by_policy=*/false);
+      /*paired=*/true, /*connected=*/false, /*is_blocked_by_policy=*/false);
 
   device::BluetoothDevice::BatteryInfo battery_info(
       /*battery_type=*/device::BluetoothDevice::BatteryType::kDefault,
@@ -86,6 +91,15 @@
       GenerateBluetoothDeviceMojoProperties(device);
   ASSERT_TRUE(properties);
 
+  // If device is not connected, |battery_info| should be null.
+  EXPECT_FALSE(properties->battery_info);
+
+  // Set the device to connected.
+  ChangeDeviceConnected(/*connected=*/true);
+
+  properties = GenerateBluetoothDeviceMojoProperties(device);
+  ASSERT_TRUE(properties);
+
   EXPECT_TRUE(properties->battery_info->default_properties);
   EXPECT_EQ(properties->battery_info->default_properties->battery_percentage,
             65);
@@ -97,7 +111,7 @@
 TEST_F(DeviceConversionUtilTest, TestConversion_MultipleBatteries) {
   device::BluetoothDevice* device = InitDevice(
       /*bluetooth_class=*/0u, /*name=*/"name", /*address=*/"address",
-      /*paired=*/true, /*connected=*/true, /*is_blocked_by_policy=*/false);
+      /*paired=*/true, /*connected=*/false, /*is_blocked_by_policy=*/false);
 
   device::BluetoothDevice::BatteryInfo left_battery_info(
       /*battery_type=*/device::BluetoothDevice::BatteryType::
@@ -126,6 +140,15 @@
       GenerateBluetoothDeviceMojoProperties(device);
   ASSERT_TRUE(properties);
 
+  // If device is not connected, |battery_info| should be null.
+  EXPECT_FALSE(properties->battery_info);
+
+  // Set the device to connected.
+  ChangeDeviceConnected(/*connected=*/true);
+
+  properties = GenerateBluetoothDeviceMojoProperties(device);
+  ASSERT_TRUE(properties);
+
   EXPECT_FALSE(properties->battery_info->default_properties);
   EXPECT_TRUE(properties->battery_info->left_bud_info);
   EXPECT_EQ(properties->battery_info->left_bud_info->battery_percentage, 65);
diff --git a/chromeos/services/bluetooth_config/device_name_manager_impl.cc b/chromeos/services/bluetooth_config/device_name_manager_impl.cc
index aed5730..fa23efd4 100644
--- a/chromeos/services/bluetooth_config/device_name_manager_impl.cc
+++ b/chromeos/services/bluetooth_config/device_name_manager_impl.cc
@@ -9,6 +9,7 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
+#include "device/bluetooth/chromeos/bluetooth_utils.h"
 
 namespace chromeos {
 namespace bluetooth_config {
@@ -59,6 +60,8 @@
     BLUETOOTH_LOG(ERROR) << "SetDeviceNickname for device with id " << device_id
                          << " failed because nickname is invalid, nickname: "
                          << nickname;
+    device::RecordSetDeviceNickName(
+        device::SetNicknameResult::kInvalidNicknameFormat);
     return;
   }
 
@@ -66,12 +69,15 @@
     BLUETOOTH_LOG(ERROR) << "SetDeviceNickname for device failed because "
                             "device_id was not found, device_id: "
                          << device_id;
+    device::RecordSetDeviceNickName(device::SetNicknameResult::kDeviceNotFound);
     return;
   }
 
   if (!local_state_) {
     BLUETOOTH_LOG(ERROR) << "SetDeviceNickname for device failed because "
                             "no local_state_ was set.";
+    device::RecordSetDeviceNickName(
+        device::SetNicknameResult::kPrefsUnavailable);
     return;
   }
 
@@ -82,6 +88,7 @@
   device_id_to_nickname_map->SetStringKey(device_id, nickname);
 
   NotifyDeviceNicknameChanged(device_id, nickname);
+  device::RecordSetDeviceNickName(device::SetNicknameResult::kSuccess);
 }
 
 void DeviceNameManagerImpl::SetPrefs(PrefService* local_state) {
diff --git a/chromeos/services/bluetooth_config/device_name_manager_impl_unittest.cc b/chromeos/services/bluetooth_config/device_name_manager_impl_unittest.cc
index fab2fd9..332d0e3 100644
--- a/chromeos/services/bluetooth_config/device_name_manager_impl_unittest.cc
+++ b/chromeos/services/bluetooth_config/device_name_manager_impl_unittest.cc
@@ -6,8 +6,10 @@
 
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "device/bluetooth/chromeos/bluetooth_utils.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -110,6 +112,8 @@
     return fake_observer_.last_device_nickname_changed();
   }
 
+  base::HistogramTester histogram_tester;
+
  private:
   std::vector<const device::BluetoothDevice*> GetMockDevices() {
     std::vector<const device::BluetoothDevice*> devices;
@@ -140,17 +144,34 @@
   EXPECT_EQ(GetLastDeviceIdNicknameChanged(), device_id);
   EXPECT_EQ(GetLastDeviceNicknameChanged(), kTestNickname);
 
+  histogram_tester.ExpectBucketCount(
+      "Bluetooth.ChromeOS.SetNickname.Result",
+      device::SetNicknameResult::kInvalidNicknameFormat, 0);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.SetNickname.Result",
+                                     device::SetNicknameResult::kSuccess, 1);
+
   // Set an empty nickname, this should fail and the nickname should be
   // unchanged.
   manager->SetDeviceNickname(device_id, "");
   EXPECT_EQ(manager->GetDeviceNickname(device_id), kTestNickname);
   EXPECT_EQ(GetNumDeviceNicknameObserverEvents(), 1u);
+  histogram_tester.ExpectBucketCount(
+      "Bluetooth.ChromeOS.SetNickname.Result",
+      device::SetNicknameResult::kInvalidNicknameFormat, 1);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.SetNickname.Result",
+                                     device::SetNicknameResult::kSuccess, 1);
 
   // Set nickname above character limit, this should also fail and the nickname
   // should be unchanged.
   manager->SetDeviceNickname(device_id, "123456789012345678901234567890123");
   EXPECT_EQ(manager->GetDeviceNickname(device_id), kTestNickname);
   EXPECT_EQ(GetNumDeviceNicknameObserverEvents(), 1u);
+
+  histogram_tester.ExpectBucketCount(
+      "Bluetooth.ChromeOS.SetNickname.Result",
+      device::SetNicknameResult::kInvalidNicknameFormat, 2);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.SetNickname.Result",
+                                     device::SetNicknameResult::kSuccess, 1);
 }
 
 TEST_F(DeviceNameManagerImplTest, NicknameIsPersistedBetweenManagerInstances) {
@@ -164,6 +185,11 @@
   EXPECT_EQ(GetNumDeviceNicknameObserverEvents(), 1u);
   EXPECT_EQ(GetLastDeviceIdNicknameChanged(), device_id);
   EXPECT_EQ(GetLastDeviceNicknameChanged(), kTestNickname);
+  histogram_tester.ExpectBucketCount(
+      "Bluetooth.ChromeOS.SetNickname.Result",
+      device::SetNicknameResult::kInvalidNicknameFormat, 0);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.SetNickname.Result",
+                                     device::SetNicknameResult::kSuccess, 1);
 
   // Create a new manager and destroy the old one.
   manager = CreateDeviceNameManager();
@@ -179,6 +205,11 @@
   manager->SetDeviceNickname(device_id, kTestNickname);
   EXPECT_FALSE(manager->GetDeviceNickname(device_id));
   EXPECT_EQ(GetNumDeviceNicknameObserverEvents(), 0u);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.SetNickname.Result",
+                                     device::SetNicknameResult::kDeviceNotFound,
+                                     1);
+  histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.SetNickname.Result",
+                                     device::SetNicknameResult::kSuccess, 0);
 }
 
 }  // namespace bluetooth_config
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 11bb50c0..8707a86f 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -791,7 +791,6 @@
         "browser_ui/client_certificate/android/ssl_client_certificate_request_browsertest.cc",
         "test/android/browsertests_apk/components_browser_tests_jni_onload.cc",
       ]
-      sources -= [ "autofill/content/browser/risk/fingerprint_browsertest.cc" ]
       deps += [
         "//components/autofill_assistant/browser",
         "//components/autofill_assistant/browser:proto",
@@ -839,6 +838,15 @@
     if (!is_chromeos_lacros) {
       assert_no_deps = [ "//chrome/*" ]
     }
+
+    if (is_fuchsia) {
+      additional_manifest_fragments = [
+        "//build/config/fuchsia/test/font_capabilities.test-cmx",
+        "//build/config/fuchsia/test/jit_capabilities.test-cmx",
+        "//build/config/fuchsia/test/network_capabilities.test-cmx",
+        "//build/config/fuchsia/test/present_view_capabilities.test-cmx",
+      ]
+    }
   }
 
   test("components_perftests") {
diff --git a/components/autofill/content/browser/risk/fingerprint_browsertest.cc b/components/autofill/content/browser/risk/fingerprint_browsertest.cc
index f9ac936..28bcd1c 100644
--- a/components/autofill/content/browser/risk/fingerprint_browsertest.cc
+++ b/components/autofill/content/browser/risk/fingerprint_browsertest.cc
@@ -95,7 +95,13 @@
         fingerprint->machine_characteristics();
     EXPECT_TRUE(machine.has_operating_system_build());
     EXPECT_TRUE(machine.has_browser_install_time_hours());
+
+#if defined(OS_FUCHSIA) || defined(OS_ANDROID)
+    // GetFontList() returns an empty list on Fuchsia and Android.
+    EXPECT_EQ(machine.font_size(), 0);
+#else
     EXPECT_GT(machine.font_size(), 0);
+#endif  // defined(OS_FUCHSIA) || defined(OS_ANDROID)
 
     // TODO(isherman): http://crbug.com/358548 and EXPECT_EQ.
     EXPECT_GE(machine.plugin_size(), 0);
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index 06febbac..73ac6b3 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -1340,7 +1340,8 @@
     else if (!field->only_fill_when_focused())
       did_autofill_all_possible_fields = false;
 
-    autofilled_field_types.insert(type.GetStorableType());
+    if (field->is_autofilled)
+      autofilled_field_types.insert(type.GetStorableType());
 
     // Keep track of the frames of detected and autofilled (credit card) fields.
     frames_of_detected_fields.insert(field->host_frame);
@@ -1474,8 +1475,12 @@
         frames_of_autofilled_credit_card_fields.size());
 
     if (card_form) {
-      AutofillMetrics::LogCreditCardNumberFills(autofilled_field_types);
-      AutofillMetrics::LogCreditCardSeamlessFills(autofilled_field_types);
+      AutofillMetrics::LogCreditCardNumberFills(
+          autofilled_field_types,
+          AutofillMetrics::MeasurementTime::kSubmissionTime);
+      AutofillMetrics::LogCreditCardSeamlessFills(
+          autofilled_field_types,
+          AutofillMetrics::MeasurementTime::kSubmissionTime);
     }
   }
 }
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.cc b/components/autofill/core/browser/metrics/autofill_metrics.cc
index 1f11a4ff..a75ae7e 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics.cc
@@ -196,6 +196,21 @@
   }
 }
 
+std::string AppendMeasurementTimeToMetricName(
+    base::StringPiece metric_name,
+    AutofillMetrics::MeasurementTime measurement_time) {
+  base::StringPiece measurement_time_string;
+  switch (measurement_time) {
+    case AutofillMetrics::MeasurementTime::kFillTime:
+      measurement_time_string = "AtFillTime";
+      break;
+    case AutofillMetrics::MeasurementTime::kSubmissionTime:
+      measurement_time_string = "AtSubmissionTime";
+      break;
+  }
+  return base::JoinString({metric_name, measurement_time_string}, ".");
+}
+
 }  // namespace
 
 // First, translates |field_type| to the corresponding logical |group| from
@@ -2431,7 +2446,8 @@
 
 // static
 void AutofillMetrics::LogCreditCardSeamlessFills(
-    const ServerFieldTypeSet& autofilled_types) {
+    const ServerFieldTypeSet& autofilled_types,
+    MeasurementTime measurement_time) {
   bool name = autofilled_types.contains(CREDIT_CARD_NAME_FULL) ||
               (autofilled_types.contains(CREDIT_CARD_NAME_FIRST) &&
                autofilled_types.contains(CREDIT_CARD_NAME_LAST));
@@ -2442,6 +2458,8 @@
               (autofilled_types.contains(CREDIT_CARD_EXP_2_DIGIT_YEAR) ||
                autofilled_types.contains(CREDIT_CARD_EXP_4_DIGIT_YEAR)));
   bool cvc = autofilled_types.contains(CREDIT_CARD_VERIFICATION_CODE);
+  if (!name && !number && !exp && !cvc)
+    return;
   CreditCardSeamlessFillMetric emit;
   if (name && number && exp && cvc) {
     emit = CreditCardSeamlessFillMetric::kFullFill;
@@ -2456,14 +2474,21 @@
   } else {
     emit = CreditCardSeamlessFillMetric::kPartialFill;
   }
-  base::UmaHistogramEnumeration("Autofill.CreditCard.SeamlessFills", emit);
+  base::UmaHistogramEnumeration(
+      AppendMeasurementTimeToMetricName("Autofill.CreditCard.SeamlessFills",
+                                        measurement_time),
+      emit);
 }
 
 // static
 void AutofillMetrics::LogCreditCardNumberFills(
-    const ServerFieldTypeSet& autofilled_types) {
+    const ServerFieldTypeSet& autofilled_types,
+    MeasurementTime measurement_time) {
   bool emit = autofilled_types.contains(CREDIT_CARD_NUMBER);
-  base::UmaHistogramBoolean("Autofill.CreditCard.NumberFills", emit);
+  base::UmaHistogramBoolean(
+      AppendMeasurementTimeToMetricName("Autofill.CreditCard.NumberFills",
+                                        measurement_time),
+      emit);
 }
 
 // static
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.h b/components/autofill/core/browser/metrics/autofill_metrics.h
index 31173032e..5bc419d8 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics.h
+++ b/components/autofill/core/browser/metrics/autofill_metrics.h
@@ -42,6 +42,11 @@
 
 class AutofillMetrics {
  public:
+  enum class MeasurementTime {
+    kFillTime,
+    kSubmissionTime,
+  };
+
   enum AutofillProfileAction {
     EXISTING_PROFILE_USED,
     EXISTING_PROFILE_UPDATED,
@@ -1774,11 +1779,13 @@
   // details. Note that this function does not check whether the form contains a
   // credit card field.
   static void LogCreditCardSeamlessFills(
-      const ServerFieldTypeSet& autofilled_types);
+      const ServerFieldTypeSet& autofilled_types,
+      MeasurementTime measurement_time);
 
   // Logs the Autofill.CreditCard.NumberFills metric.
   static void LogCreditCardNumberFills(
-      const ServerFieldTypeSet& autofilled_types);
+      const ServerFieldTypeSet& autofilled_types,
+      MeasurementTime measurement_time);
 
   // This should be called when determining the heuristic types for a form's
   // fields.
diff --git a/components/browser_ui/styles/android/BUILD.gn b/components/browser_ui/styles/android/BUILD.gn
index 5b52da6..a8dcf9e 100644
--- a/components/browser_ui/styles/android/BUILD.gn
+++ b/components/browser_ui/styles/android/BUILD.gn
@@ -33,6 +33,7 @@
     "java/res/color/default_text_color_hint_list.xml",
     "java/res/color/default_text_color_link_tint_list.xml",
     "java/res/color/default_text_color_list.xml",
+    "java/res/color/default_text_color_on_accent1_list.xml",
     "java/res/color/default_text_color_secondary_list.xml",
     "java/res/color/progress_bar_bg_color.xml",
     "java/res/color/selection_control_button_tint.xml",
diff --git a/components/browser_ui/styles/android/java/res/color/default_text_color_on_accent1_list.xml b/components/browser_ui/styles/android/java/res/color/default_text_color_on_accent1_list.xml
new file mode 100644
index 0000000..3cc6de85
--- /dev/null
+++ b/components/browser_ui/styles/android/java/res/color/default_text_color_on_accent1_list.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:alpha="@dimen/default_disabled_alpha" android:color="@macro/default_text_color"
+        android:state_enabled="false" />
+    <item android:color="@macro/default_text_color_on_accent1"/>
+</selector>
diff --git a/components/browser_ui/styles/android/java/res/values/semantic_colors_dynamic.xml b/components/browser_ui/styles/android/java/res/values/semantic_colors_dynamic.xml
index fa0db4d..4c1c5bf 100644
--- a/components/browser_ui/styles/android/java/res/values/semantic_colors_dynamic.xml
+++ b/components/browser_ui/styles/android/java/res/values/semantic_colors_dynamic.xml
@@ -13,6 +13,7 @@
     <macro name="default_icon_color_accent1">?attr/colorPrimary</macro>
     <macro name="default_text_color">?attr/colorOnSurface</macro>
     <macro name="default_text_color_accent1">?attr/colorPrimary</macro>
+    <macro name="default_text_color_on_accent1">?attr/colorOnPrimary</macro>
     <macro name="hairline_stroke_color">?attr/colorOutline</macro>
     <macro name="divider_line_bg_color">?attr/colorSurfaceVariant</macro>
     -->
@@ -26,6 +27,7 @@
     <macro name="default_icon_color_accent1">@color/default_icon_color_accent1_baseline</macro>
     <macro name="default_text_color">@color/default_text_color_baseline</macro>
     <macro name="default_text_color_accent1">@color/default_text_color_blue_baseline</macro>
+    <macro name="default_text_color_on_accent1">@color/default_text_color_on_accent1_baseline</macro>
     <macro name="hairline_stroke_color">@color/hairline_stroke_color_baseline</macro>
     <macro name="divider_line_bg_color">@color/divider_line_bg_color_baseline</macro>
 
diff --git a/components/browser_ui/styles/android/java/res/values/styles.xml b/components/browser_ui/styles/android/java/res/values/styles.xml
index 9615ac9..ab4e2a5 100644
--- a/components/browser_ui/styles/android/java/res/values/styles.xml
+++ b/components/browser_ui/styles/android/java/res/values/styles.xml
@@ -273,6 +273,21 @@
         <item name="android:textColor">@color/default_text_color_link_tint_list</item>
     </style>
 
+    <!-- Formerly inverse text styles -->
+    <!-- Text styles used on dark background on light theme, and light background on dark theme. -->
+    <style name="TextAppearance.Headline.Primary.OnAccent1" tools:ignore="UnusedResources">
+        <item name="android:textColor">@color/default_text_color_on_accent1_list</item>
+    </style>
+    <style name="TextAppearance.TextMediumThick.Primary.OnAccent1" tools:ignore="UnusedResources">
+        <item name="android:textColor">@color/default_text_color_on_accent1_list</item>
+    </style>
+    <style name="TextAppearance.TextMedium.Primary.OnAccent1" tools:ignore="UnusedResources">
+        <item name="android:textColor">@color/default_text_color_on_accent1_list</item>
+    </style>
+    <style name="TextAppearance.TextSmall.Primary.OnAccent1" tools:ignore="UnusedResources">
+        <item name="android:textColor">@color/default_text_color_on_accent1_list</item>
+    </style>
+
     <!-- Other text styles -->
     <style name="TextAppearance.BlackLink" tools:ignore="UnusedResources">
         <item name="android:textColor">@color/default_text_color_secondary_list</item>
diff --git a/components/browser_ui/widget/android/java/res/layout/textbubble_text.xml b/components/browser_ui/widget/android/java/res/layout/textbubble_text.xml
index 0f220b0..e3da969 100644
--- a/components/browser_ui/widget/android/java/res/layout/textbubble_text.xml
+++ b/components/browser_ui/widget/android/java/res/layout/textbubble_text.xml
@@ -18,7 +18,7 @@
           android:layout_height="wrap_content"
           android:padding="16dp"
           android:layout_weight="1"
-          android:textAppearance="@style/TextAppearance.TextMediumThick.Primary.Baseline.Inverse" />
+          android:textAppearance="@style/TextAppearance.TextMediumThick.Primary.OnAccent1" />
 
      <org.chromium.ui.widget.ButtonCompat
           android:id="@+id/button_snooze"
diff --git a/components/browser_ui/widget/android/java/res/layout/textbubble_text_with_image.xml b/components/browser_ui/widget/android/java/res/layout/textbubble_text_with_image.xml
index 4b94abf..92301c9c 100644
--- a/components/browser_ui/widget/android/java/res/layout/textbubble_text_with_image.xml
+++ b/components/browser_ui/widget/android/java/res/layout/textbubble_text_with_image.xml
@@ -48,6 +48,6 @@
         android:id="@+id/message"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textAppearance="@style/TextAppearance.TextMediumThick.Primary.Baseline.Inverse" />
+        android:textAppearance="@style/TextAppearance.TextMediumThick.Primary.OnAccent1" />
 
 </LinearLayout>
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer.cc b/components/certificate_transparency/chrome_ct_policy_enforcer.cc
index 6a70a0d..e8e0ccff 100644
--- a/components/certificate_transparency/chrome_ct_policy_enforcer.cc
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer.cc
@@ -12,6 +12,7 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/feature_list.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/numerics/safe_conversions.h"
@@ -20,6 +21,7 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "base/version.h"
+#include "components/certificate_transparency/ct_features.h"
 #include "components/certificate_transparency/ct_known_logs.h"
 #include "crypto/sha2.h"
 #include "net/cert/ct_policy_status.h"
@@ -215,13 +217,33 @@
     issuance_date = std::min(sct->timestamp, issuance_date);
   }
 
+  // Certificates issued after this date (February 1, 2022, OO:OO:OO GMT)
+  // will be subject to the new CT policy, which:
+  // -Removes the One Google log requirement.
+  // -Introduces a log operator diversity (at least 2 SCTs that come from
+  // different operators are required).
+  // -Uses days for certificate lifetime calculations instead of rounding to
+  // months.
+  // Increases the SCT requirements for certificates with a lifetime between
+  // 180 days and 15 months, from 2 to 3.
+  const base::Time kPolicyUpdateDate =
+      base::Time::UnixEpoch() + base::Seconds(1643673600);
+  bool use_2022_policy =
+      base::FeatureList::IsEnabled(
+          features::kCertificateTransparency2022PolicyAllCerts) ||
+      (base::FeatureList::IsEnabled(
+           features::kCertificateTransparency2022Policy) &&
+       issuance_date >= kPolicyUpdateDate);
+
   bool has_valid_google_sct = false;
   bool has_valid_nongoogle_sct = false;
   bool has_valid_embedded_sct = false;
   bool has_valid_nonembedded_sct = false;
   bool has_embedded_google_sct = false;
   bool has_embedded_nongoogle_sct = false;
+  bool has_diverse_log_operators = false;
   std::vector<base::StringPiece> embedded_log_ids;
+  std::string first_seen_operator;
   for (const auto& sct : verified_scts) {
     base::Time disqualification_date;
     bool is_disqualified =
@@ -233,14 +255,16 @@
       continue;
     }
 
-    if (IsLogOperatedByGoogle(sct->log_id)) {
-      has_valid_google_sct |= !is_disqualified;
-      if (sct->origin == net::ct::SignedCertificateTimestamp::SCT_EMBEDDED)
-        has_embedded_google_sct = true;
-    } else {
-      has_valid_nongoogle_sct |= !is_disqualified;
-      if (sct->origin == net::ct::SignedCertificateTimestamp::SCT_EMBEDDED)
-        has_embedded_nongoogle_sct = true;
+    if (!use_2022_policy) {
+      if (IsLogOperatedByGoogle(sct->log_id)) {
+        has_valid_google_sct |= !is_disqualified;
+        if (sct->origin == net::ct::SignedCertificateTimestamp::SCT_EMBEDDED)
+          has_embedded_google_sct = true;
+      } else {
+        has_valid_nongoogle_sct |= !is_disqualified;
+        if (sct->origin == net::ct::SignedCertificateTimestamp::SCT_EMBEDDED)
+          has_embedded_nongoogle_sct = true;
+      }
     }
     if (sct->origin != net::ct::SignedCertificateTimestamp::SCT_EMBEDDED) {
       has_valid_nonembedded_sct = true;
@@ -254,22 +278,42 @@
         embedded_log_ids.push_back(sct->log_id);
       }
     }
+
+    if (use_2022_policy && !has_diverse_log_operators) {
+      std::string sct_operator = GetOperatorForLog(sct->log_id, sct->timestamp);
+      if (first_seen_operator.empty()) {
+        first_seen_operator = sct_operator;
+      } else {
+        has_diverse_log_operators |= first_seen_operator != sct_operator;
+      }
+    }
   }
 
   // Option 1:
   // An SCT presented via the TLS extension OR embedded within a stapled OCSP
   //   response is from a log qualified at time of check;
-  // AND there is at least one SCT from a Google Log that is qualified at
-  //   time of check, presented via any method;
-  // AND there is at least one SCT from a non-Google Log that is qualified
-  //   at the time of check, presented via any method.
+  // With previous policy:
+  //   AND there is at least one SCT from a Google Log that is qualified at
+  //     time of check, presented via any method;
+  //   AND there is at least one SCT from a non-Google Log that is qualified
+  //     at the time of check, presented via any method.
+  // With new policy:
+  //   AND there are at least two SCTs from logs with different operators,
+  //   presented by any method.
   //
   // Note: Because SCTs embedded via TLS or OCSP can be updated on the fly,
   // the issuance date is irrelevant, as any policy changes can be
-  // accomodated.
-  if (has_valid_nonembedded_sct && has_valid_google_sct &&
-      has_valid_nongoogle_sct) {
-    return CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;
+  // accommodated.
+  if (has_valid_nonembedded_sct) {
+    if (use_2022_policy) {
+      if (has_diverse_log_operators) {
+        return CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;
+      }
+    } else {
+      if (has_valid_google_sct && has_valid_nongoogle_sct) {
+        return CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;
+      }
+    }
   }
   // Note: If has_valid_nonembedded_sct was true, but Option 2 isn't met,
   // then the result will be that there weren't diverse enough SCTs, as that
@@ -289,44 +333,62 @@
                : CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS;
   }
 
-  // ... AND there is at least one embedded SCT from a Google Log once or
-  //   currently qualified;
-  // AND there is at least one embedded SCT from a non-Google Log once or
-  //   currently qualified;
-  // ...
-  //
-  // Note: This policy language is only enforced after the below issuance
-  // date, as that's when the diversity policy first came into effect for
-  // SCTs embedded in certificates.
-  // The date when diverse SCTs requirement is effective from.
-  // 2015-07-01 00:00:00 UTC.
-  const base::Time kDiverseSCTRequirementStartDate =
-      base::Time::UnixEpoch() + base::Seconds(1435708800);
-  if (issuance_date >= kDiverseSCTRequirementStartDate &&
-      !(has_embedded_google_sct && has_embedded_nongoogle_sct)) {
-    // Note: This also covers the case for non-embedded SCTs, as it's only
-    // possible to reach here if both sets are not diverse enough.
-    return CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS;
-  }
-
-  size_t lifetime_in_months = 0;
-  bool has_partial_month = false;
-  RoundedDownMonthDifference(cert.valid_start(), cert.valid_expiry(),
-                             &lifetime_in_months, &has_partial_month);
-
-  // ... AND the certificate embeds SCTs from AT LEAST the number of logs
-  //   once or currently qualified shown in Table 1 of the CT Policy.
   size_t num_required_embedded_scts = 5;
-  if (lifetime_in_months > 39 ||
-      (lifetime_in_months == 39 && has_partial_month)) {
-    num_required_embedded_scts = 5;
-  } else if (lifetime_in_months > 27 ||
-             (lifetime_in_months == 27 && has_partial_month)) {
-    num_required_embedded_scts = 4;
-  } else if (lifetime_in_months >= 15) {
-    num_required_embedded_scts = 3;
+  if (use_2022_policy) {
+    // ... AND there are at least two SCTs from logs with different
+    // operators ...
+    if (!has_diverse_log_operators) {
+      return CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS;
+    }
+    // ... AND the certificate embeds SCTs from AT LEAST the number of logs
+    //   once or currently qualified shown in Table 1 of the CT Policy.
+    base::TimeDelta lifetime = cert.valid_expiry() - cert.valid_start();
+    if (lifetime >= base::Days(180)) {
+      num_required_embedded_scts = 3;
+    } else {
+      num_required_embedded_scts = 2;
+    }
   } else {
-    num_required_embedded_scts = 2;
+    // ... AND there is at least one embedded SCT from a Google Log once or
+    //   currently qualified;
+    // AND there is at least one embedded SCT from a non-Google Log once or
+    //   currently qualified;
+    // ...
+    //
+    // Note: This policy language is only enforced after the below issuance
+    // date, as that's when the diversity policy first came into effect for
+    // SCTs embedded in certificates.
+    // The date when diverse SCTs requirement is effective from.
+    // 2015-07-01 00:00:00 UTC.
+    // TODO(carlosil): There are no more valid certificates from before this
+    // date, so this date and the associated logic should be cleaned up.
+    const base::Time kDiverseSCTRequirementStartDate =
+        base::Time::UnixEpoch() + base::Seconds(1435708800);
+    if (issuance_date >= kDiverseSCTRequirementStartDate &&
+        !(has_embedded_google_sct && has_embedded_nongoogle_sct)) {
+      // Note: This also covers the case for non-embedded SCTs, as it's only
+      // possible to reach here if both sets are not diverse enough.
+      return CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS;
+    }
+
+    size_t lifetime_in_months = 0;
+    bool has_partial_month = false;
+    RoundedDownMonthDifference(cert.valid_start(), cert.valid_expiry(),
+                               &lifetime_in_months, &has_partial_month);
+
+    // ... AND the certificate embeds SCTs from AT LEAST the number of logs
+    //   once or currently qualified shown in Table 1 of the CT Policy.
+    if (lifetime_in_months > 39 ||
+        (lifetime_in_months == 39 && has_partial_month)) {
+      num_required_embedded_scts = 5;
+    } else if (lifetime_in_months > 27 ||
+               (lifetime_in_months == 27 && has_partial_month)) {
+      num_required_embedded_scts = 4;
+    } else if (lifetime_in_months >= 15) {
+      num_required_embedded_scts = 3;
+    } else {
+      num_required_embedded_scts = 2;
+    }
   }
 
   // Sort the embedded log IDs and remove duplicates, so that only a single
@@ -352,11 +414,11 @@
 
 std::string ChromeCTPolicyEnforcer::GetOperatorForLog(
     std::string log_id,
-    base::TimeDelta timestamp) {
+    base::Time timestamp) const {
   DCHECK(log_operator_history_.find(log_id) != log_operator_history_.end());
-  OperatorHistoryEntry log_history = log_operator_history_[log_id];
+  OperatorHistoryEntry log_history = log_operator_history_.at(log_id);
   for (auto operator_entry : log_history.previous_operators_) {
-    if (timestamp < operator_entry.second)
+    if (timestamp - base::Time::UnixEpoch() < operator_entry.second)
       return operator_entry.first;
   }
   // Either the log has only ever had one operator, or the timestamp is after
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer.h b/components/certificate_transparency/chrome_ct_policy_enforcer.h
index 234910e9..807578c 100644
--- a/components/certificate_transparency/chrome_ct_policy_enforcer.h
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer.h
@@ -87,6 +87,11 @@
     return log_operator_history_;
   }
 
+  void SetOperatorHistoryForTesting(
+      std::map<std::string, OperatorHistoryEntry> log_operator_history) {
+    log_operator_history_ = std::move(log_operator_history);
+  }
+
  private:
   // Returns true if the log identified by |log_id| (the SHA-256 hash of the
   // log's DER-encoded SPKI) has been disqualified, and sets
@@ -107,7 +112,7 @@
       const net::X509Certificate& cert,
       const net::ct::SCTList& verified_scts) const;
 
-  std::string GetOperatorForLog(std::string log_id, base::TimeDelta timestamp);
+  std::string GetOperatorForLog(std::string log_id, base::Time timestamp) const;
 
   // Map of SHA-256(SPKI) to log disqualification date.
   std::vector<std::pair<std::string, base::TimeDelta>> disqualified_logs_;
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc b/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
index 49dee17..94bd7060 100644
--- a/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
@@ -10,9 +10,12 @@
 
 #include "base/build_time.h"
 #include "base/cxx17_backports.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "base/time/time.h"
 #include "base/version.h"
+#include "components/certificate_transparency/ct_features.h"
 #include "components/certificate_transparency/ct_known_logs.h"
 #include "crypto/rsa_private_key.h"
 #include "crypto/sha2.h"
@@ -537,6 +540,311 @@
                                                     NetLogWithSource()));
 }
 
+class ChromeCTPolicyEnforcerTest2022Policy : public ChromeCTPolicyEnforcerTest {
+ public:
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kCertificateTransparency2022Policy);
+    ChromeCTPolicyEnforcerTest::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(ChromeCTPolicyEnforcerTest2022Policy,
+       2022PolicyNotInEffectBeforeTargetDate) {
+  // Old policy should enforce one Google log requirement.
+  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
+      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           2, std::vector<std::string>(), true, &scts);
+  std::map<std::string, OperatorHistoryEntry> operator_history;
+  for (size_t i = 0; i < scts.size(); i++) {
+    OperatorHistoryEntry entry;
+    entry.current_operator_ = "Operator " + base::NumberToString(i);
+    operator_history[scts[i]->log_id] = entry;
+    // Set timestamp to 1 day before new policy comes in effect.
+    EXPECT_TRUE(base::Time::FromUTCExploded({2022, 1, 0, 31, 0, 0, 0, 0},
+                                            &scts[i]->timestamp));
+  }
+  chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_F(ChromeCTPolicyEnforcerTest2022Policy,
+       2022PolicyInEffectAfterTargetDate) {
+  // New policy should allow SCTs from all non-Google operators to comply as
+  // long as diversity requirement is fulfilled.
+  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
+      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           2, std::vector<std::string>(), true, &scts);
+  std::map<std::string, OperatorHistoryEntry> operator_history;
+  for (size_t i = 0; i < scts.size(); i++) {
+    OperatorHistoryEntry entry;
+    entry.current_operator_ = "Operator " + base::NumberToString(i);
+    operator_history[scts[i]->log_id] = entry;
+    // Set timestamp to the day new policy comes in effect.
+    EXPECT_TRUE(base::Time::FromUTCExploded({2022, 2, 0, 1, 0, 0, 0, 0},
+                                            &scts[i]->timestamp));
+  }
+  chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+class ChromeCTPolicyEnforcerTest2022PolicyAllCerts
+    : public ChromeCTPolicyEnforcerTest {
+ public:
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kCertificateTransparency2022PolicyAllCerts);
+    ChromeCTPolicyEnforcerTest::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts, UpdatedSCTRequirements) {
+  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
+      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
+
+  std::unique_ptr<crypto::RSAPrivateKey> private_key(
+      crypto::RSAPrivateKey::Create(1024));
+  ASSERT_TRUE(private_key);
+
+  // Test multiple validity periods
+  base::Time time_2015_3_0_25_11_25_0_0 =
+      CreateTime({2015, 3, 0, 25, 11, 25, 0, 0});
+
+  base::Time time_2015_9_0_20_11_25_0_0 =
+      CreateTime({2015, 9, 0, 20, 11, 25, 0, 0});
+
+  base::Time time_2015_9_0_21_11_25_0_0 =
+      CreateTime({2015, 9, 0, 21, 11, 25, 0, 0});
+
+  base::Time time_2016_3_0_25_11_25_0_0 =
+      CreateTime({2016, 3, 0, 25, 11, 25, 0, 0});
+
+  const struct TestData {
+    base::Time validity_start;
+    base::Time validity_end;
+    size_t scts_required;
+  } kTestData[] = {{// Cert valid for -12 months (nonsensical), needs 2 SCTs.
+                    time_2016_3_0_25_11_25_0_0, time_2015_3_0_25_11_25_0_0, 2},
+                   {// Cert valid for 179 days, needs 2 SCTs.
+                    time_2015_3_0_25_11_25_0_0, time_2015_9_0_20_11_25_0_0, 2},
+                   {// Cert valid for exactly 180 days, needs 3 SCTs.
+                    time_2015_3_0_25_11_25_0_0, time_2015_9_0_21_11_25_0_0, 3},
+                   {// Cert valid for over 180 days, needs 3 SCTs.
+                    time_2015_3_0_25_11_25_0_0, time_2016_3_0_25_11_25_0_0, 3}};
+
+  for (size_t i = 0; i < base::size(kTestData); ++i) {
+    SCOPED_TRACE(i);
+    const base::Time& validity_start = kTestData[i].validity_start;
+    const base::Time& validity_end = kTestData[i].validity_end;
+    size_t scts_required = kTestData[i].scts_required;
+
+    // Create a self-signed certificate with exactly the validity period.
+    std::string cert_data;
+    ASSERT_TRUE(net::x509_util::CreateSelfSignedCert(
+        private_key->key(), net::x509_util::DIGEST_SHA256, "CN=test",
+        i * 10 + scts_required, validity_start, validity_end, {}, &cert_data));
+    scoped_refptr<X509Certificate> cert(X509Certificate::CreateFromBytes(
+        base::as_bytes(base::make_span(cert_data))));
+    ASSERT_TRUE(cert);
+
+    std::map<std::string, OperatorHistoryEntry> operator_history;
+    for (size_t j = 0; j <= scts_required - 1; ++j) {
+      SCTList scts;
+      FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, j,
+                               std::vector<std::string>(), false, &scts);
+      // Add different operators to the logs so the SCTs comply with operator
+      // diversity.
+      for (size_t k = 0; k < scts.size(); k++) {
+        OperatorHistoryEntry entry;
+        entry.current_operator_ = "Operator " + base::NumberToString(k);
+        operator_history[scts[k]->log_id] = entry;
+      }
+      chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+      CTPolicyCompliance expected;
+      if (j == scts_required) {
+        // If the scts provided are as many as are required, the cert should be
+        // declared as compliant.
+        expected = CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;
+      } else if (j == 1) {
+        // If a single SCT is provided, it should trip the diversity check.
+        expected = CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS;
+      } else {
+        // In any other case, the 'not enough' check should trip.
+        expected = CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS;
+      }
+      EXPECT_EQ(expected, policy_enforcer_->CheckCompliance(cert.get(), scts,
+                                                            NetLogWithSource()))
+          << " for: " << (validity_end - validity_start).InDays() << " and "
+          << scts_required << " scts=" << scts.size() << " j=" << j;
+    }
+  }
+}
+
+TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
+       DoesNotConformToCTPolicyAllLogsSameOperator) {
+  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
+      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           2, std::vector<std::string>(), true, &scts);
+  std::map<std::string, OperatorHistoryEntry> operator_history;
+  for (size_t i = 0; i < scts.size(); i++) {
+    OperatorHistoryEntry entry;
+    entry.current_operator_ = "Operator";
+    operator_history[scts[i]->log_id] = entry;
+  }
+  chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
+       ConformsToCTPolicyDifferentOperators) {
+  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
+      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           2, std::vector<std::string>(), true, &scts);
+  std::map<std::string, OperatorHistoryEntry> operator_history;
+  for (size_t i = 0; i < scts.size(); i++) {
+    OperatorHistoryEntry entry;
+    entry.current_operator_ = "Operator " + base::NumberToString(i);
+    operator_history[scts[i]->log_id] = entry;
+  }
+  chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
+       ConformsToPolicyDueToOperatorSwitch) {
+  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
+      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           2, std::vector<std::string>(), true, &scts);
+  std::map<std::string, OperatorHistoryEntry> operator_history;
+  // Set all logs to the same operator.
+  for (auto sct : scts) {
+    OperatorHistoryEntry entry;
+    entry.current_operator_ = "Same Operator";
+    operator_history[sct->log_id] = entry;
+  }
+  // Set the previous operator of one of the logs to a different one, with an
+  // end time after the SCT timestamp.
+  operator_history[scts[1]->log_id].previous_operators_.emplace_back(
+      "Different Operator",
+      scts[1]->timestamp + base::Seconds(1) - base::Time::UnixEpoch());
+  chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
+       DoesNotConformToPolicyDueToOperatorSwitch) {
+  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
+      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           2, std::vector<std::string>(), true, &scts);
+  std::map<std::string, OperatorHistoryEntry> operator_history;
+  // Set logs to different operators
+  for (size_t i = 0; i < scts.size(); i++) {
+    OperatorHistoryEntry entry;
+    entry.current_operator_ = "Operator " + base::NumberToString(i);
+    operator_history[scts[i]->log_id] = entry;
+  }
+  // Set the previous operator of one of the logs to the same as the other log,
+  // with an end time after the SCT timestamp.
+  operator_history[scts[1]->log_id].previous_operators_.emplace_back(
+      "Operator 0",
+      scts[1]->timestamp + base::Seconds(1) - base::Time::UnixEpoch());
+  chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts, MultipleOperatorSwitches) {
+  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
+      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           2, std::vector<std::string>(), true, &scts);
+  std::map<std::string, OperatorHistoryEntry> operator_history;
+  // Set logs to different operators
+  for (size_t i = 0; i < scts.size(); i++) {
+    OperatorHistoryEntry entry;
+    entry.current_operator_ = "Operator " + base::NumberToString(i);
+    operator_history[scts[i]->log_id] = entry;
+  }
+  // Set multiple previous operators, the first should be ignored since it
+  // stopped operating before the SCT timestamp.
+  operator_history[scts[1]->log_id].previous_operators_.emplace_back(
+      "Different Operator",
+      scts[1]->timestamp - base::Seconds(1) - base::Time::UnixEpoch());
+  operator_history[scts[1]->log_id].previous_operators_.emplace_back(
+      "Operator 0",
+      scts[1]->timestamp + base::Seconds(1) - base::Time::UnixEpoch());
+  chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
+       MultipleOperatorSwitchesBeforeSCTTimestamp) {
+  ChromeCTPolicyEnforcer* chrome_policy_enforcer =
+      static_cast<ChromeCTPolicyEnforcer*>(policy_enforcer_.get());
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           2, std::vector<std::string>(), true, &scts);
+  std::map<std::string, OperatorHistoryEntry> operator_history;
+  // Set all logs to the same operator.
+  for (auto sct : scts) {
+    OperatorHistoryEntry entry;
+    entry.current_operator_ = "Same Operator";
+    operator_history[sct->log_id] = entry;
+  }
+  // Set multiple previous operators, all of them should be ignored since they
+  // all stopped operating before the SCT timestamp.
+  operator_history[scts[1]->log_id].previous_operators_.emplace_back(
+      "Different Operator",
+      scts[1]->timestamp - base::Seconds(2) - base::Time::UnixEpoch());
+  operator_history[scts[1]->log_id].previous_operators_.emplace_back(
+      "Yet Another Different Operator",
+      scts[1]->timestamp - base::Seconds(1) - base::Time::UnixEpoch());
+  chrome_policy_enforcer->SetOperatorHistoryForTesting(operator_history);
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
 }  // namespace
 
 }  // namespace certificate_transparency
diff --git a/components/certificate_transparency/ct_features.cc b/components/certificate_transparency/ct_features.cc
index 21834dc..ccdb588 100644
--- a/components/certificate_transparency/ct_features.cc
+++ b/components/certificate_transparency/ct_features.cc
@@ -19,5 +19,12 @@
     base::FEATURE_ENABLED_BY_DEFAULT};
 #endif
 
+const base::Feature kCertificateTransparency2022Policy{
+    "CertificateTransparency2022Policy", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kCertificateTransparency2022PolicyAllCerts{
+    "CertificateTransparency2022PolicyAllCerts",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
 }  // namespace certificate_transparency
diff --git a/components/certificate_transparency/ct_features.h b/components/certificate_transparency/ct_features.h
index ca2e926..79774e1e 100644
--- a/components/certificate_transparency/ct_features.h
+++ b/components/certificate_transparency/ct_features.h
@@ -12,6 +12,19 @@
 
 extern const base::Feature kCertificateTransparencyComponentUpdater;
 
+// If enabled, the 2022 CT policy which removes the one Google log
+// requirement, introduces log operator diversity requirements, and increases
+// the number of embedded SCTs required for certificates with a lifetime over
+// 180 days (from 2 to 3) will be used for any certificate issued after February
+// 1, 2022.
+extern const base::Feature kCertificateTransparency2022Policy;
+
+// If enabled, the 2022 CT policy which removes the one Google log
+// requirement, introduces log operator diversity requirements, and increases
+// the number of embedded SCTs required for certificates with a lifetime over
+// 180 days (from 2 to 3) will be used for all certificates.
+extern const base::Feature kCertificateTransparency2022PolicyAllCerts;
+
 }  // namespace features
 }  // namespace certificate_transparency
 
diff --git a/components/enterprise/BUILD.gn b/components/enterprise/BUILD.gn
index 50faae4..cc23d57 100644
--- a/components/enterprise/BUILD.gn
+++ b/components/enterprise/BUILD.gn
@@ -22,7 +22,8 @@
     "browser/reporting/real_time_uploader.h",
     "browser/reporting/report_generator.cc",
     "browser/reporting/report_generator.h",
-    "browser/reporting/report_request_definition.h",
+    "browser/reporting/report_request.cc",
+    "browser/reporting/report_request.h",
     "browser/reporting/report_request_queue_generator.cc",
     "browser/reporting/report_request_queue_generator.h",
     "browser/reporting/report_scheduler.cc",
diff --git a/components/enterprise/browser/reporting/report_generator.cc b/components/enterprise/browser/reporting/report_generator.cc
index cc79d72..8b789a15 100644
--- a/components/enterprise/browser/reporting/report_generator.cc
+++ b/components/enterprise/browser/reporting/report_generator.cc
@@ -32,14 +32,10 @@
 
 void ReportGenerator::Generate(ReportType report_type,
                                ReportCallback callback) {
-  CreateBasicRequest(std::make_unique<ReportRequest>(), report_type,
+  CreateBasicRequest(std::make_unique<ReportRequest>(report_type), report_type,
                      std::move(callback));
 }
 
-void ReportGenerator::SetMaximumReportSizeForTesting(size_t size) {
-  report_request_queue_generator_.SetMaximumReportSizeForTesting(size);
-}
-
 void ReportGenerator::CreateBasicRequest(
     std::unique_ptr<ReportRequest> basic_request,
     ReportType report_type,
@@ -47,12 +43,15 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   delegate_->SetAndroidAppInfos(basic_request.get());
 #else
-  basic_request->set_computer_name(this->GetMachineName());
-  basic_request->set_os_user_name(GetOSUserName());
-  basic_request->set_serial_number(GetSerialNumber());
-  basic_request->set_allocated_os_report(GetOSReport().release());
-  basic_request->set_allocated_browser_device_identifier(
-      policy::GetBrowserDeviceIdentifier().release());
+  basic_request->GetDeviceReportRequest().set_computer_name(
+      this->GetMachineName());
+  basic_request->GetDeviceReportRequest().set_os_user_name(GetOSUserName());
+  basic_request->GetDeviceReportRequest().set_serial_number(GetSerialNumber());
+  basic_request->GetDeviceReportRequest().set_allocated_os_report(
+      GetOSReport().release());
+  basic_request->GetDeviceReportRequest()
+      .set_allocated_browser_device_identifier(
+          policy::GetBrowserDeviceIdentifier().release());
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if defined(OS_ANDROID) || defined(OS_IOS)
@@ -112,7 +111,8 @@
     ReportType report_type,
     ReportCallback callback,
     std::unique_ptr<em::BrowserReport> browser_report) {
-  basic_request->set_allocated_browser_report(browser_report.release());
+  basic_request->GetDeviceReportRequest().set_allocated_browser_report(
+      browser_report.release());
 
   if (report_type != ReportType::kBrowserVersion) {
     // Generate a queue of requests containing detailed profile information.
@@ -123,7 +123,7 @@
 
   // Return a queue containing only the basic request and browser report without
   // detailed profile information.
-  ReportRequests requests;
+  ReportRequestQueue requests;
   requests.push(std::move(basic_request));
   std::move(callback).Run(std::move(requests));
 }
@@ -133,8 +133,9 @@
     base::OnceCallback<void(std::unique_ptr<ReportRequest>)> callback,
     base::SysInfo::HardwareInfo hardware_info) {
 #if defined(OS_ANDROID) || defined(OS_IOS)
-  basic_request->set_brand_name(hardware_info.manufacturer);
-  basic_request->set_device_model(hardware_info.model);
+  basic_request->GetDeviceReportRequest().set_brand_name(
+      hardware_info.manufacturer);
+  basic_request->GetDeviceReportRequest().set_device_model(hardware_info.model);
 #endif  // defined(OS_ANDROID) || defined(OS_IOS)
 
   std::move(callback).Run(std::move(basic_request));
diff --git a/components/enterprise/browser/reporting/report_generator.h b/components/enterprise/browser/reporting/report_generator.h
index d95a005..71cbb18 100644
--- a/components/enterprise/browser/reporting/report_generator.h
+++ b/components/enterprise/browser/reporting/report_generator.h
@@ -6,14 +6,13 @@
 #define COMPONENTS_ENTERPRISE_BROWSER_REPORTING_REPORT_GENERATOR_H_
 
 #include <memory>
-#include <queue>
 #include <string>
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/system/sys_info.h"
 #include "components/enterprise/browser/reporting/browser_report_generator.h"
-#include "components/enterprise/browser/reporting/report_request_definition.h"
+#include "components/enterprise/browser/reporting/report_request.h"
 #include "components/enterprise/browser/reporting/report_request_queue_generator.h"
 #include "components/enterprise/browser/reporting/report_type.h"
 #include "components/policy/proto/device_management_backend.pb.h"
@@ -24,9 +23,7 @@
 
 class ReportGenerator {
  public:
-  using ReportRequest = definition::ReportRequest;
-  using ReportRequests = std::queue<std::unique_ptr<ReportRequest>>;
-  using ReportCallback = base::OnceCallback<void(ReportRequests)>;
+  using ReportCallback = base::OnceCallback<void(ReportRequestQueue)>;
 
   class Delegate {
    public:
@@ -54,8 +51,6 @@
   // information that are needed by that particular type.
   virtual void Generate(ReportType report_type, ReportCallback callback);
 
-  void SetMaximumReportSizeForTesting(size_t size);
-
  protected:
   // Creates a basic request that will be used by all Profiles.
   void CreateBasicRequest(std::unique_ptr<ReportRequest> basic_request,
diff --git a/components/enterprise/browser/reporting/report_request.cc b/components/enterprise/browser/reporting/report_request.cc
new file mode 100644
index 0000000..2840097
--- /dev/null
+++ b/components/enterprise/browser/reporting/report_request.cc
@@ -0,0 +1,61 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/enterprise/browser/reporting/report_request.h"
+
+#include "components/enterprise/browser/reporting/report_type.h"
+
+namespace enterprise_reporting {
+
+namespace em = enterprise_management;
+
+ReportRequest::ReportRequest(ReportType type) {
+  switch (type) {
+    case ReportType::kFull:
+    case ReportType::kBrowserVersion:
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+      proto_.emplace<em::ChromeOsUserReportRequest>();
+#else
+      proto_.emplace<em::ChromeDesktopReportRequest>();
+#endif
+      return;
+    case ReportType::kProfileReport:
+      proto_.emplace<em::ChromeProfileReportRequest>();
+      return;
+  }
+}
+
+ReportRequest::ReportRequest(const em::ChromeDesktopReportRequest& proto)
+    : proto_(proto) {}
+ReportRequest::ReportRequest(const em::ChromeOsUserReportRequest& proto)
+    : proto_(proto) {}
+ReportRequest::ReportRequest(const em::ChromeProfileReportRequest& proto)
+    : proto_(proto) {}
+
+ReportRequest::~ReportRequest() = default;
+
+const ReportRequest::DeviceReportRequestProto&
+ReportRequest::GetDeviceReportRequest() const {
+  return absl::get<ReportRequest::DeviceReportRequestProto>(proto_);
+}
+ReportRequest::DeviceReportRequestProto&
+ReportRequest::GetDeviceReportRequest() {
+  return absl::get<ReportRequest::DeviceReportRequestProto>(proto_);
+}
+
+const em::ChromeProfileReportRequest&
+ReportRequest::GetChromeProfileReportRequest() const {
+  return absl::get<em::ChromeProfileReportRequest>(proto_);
+}
+em::ChromeProfileReportRequest& ReportRequest::GetChromeProfileReportRequest() {
+  return absl::get<em::ChromeProfileReportRequest>(proto_);
+}
+
+std::unique_ptr<ReportRequest> ReportRequest::Clone() const {
+  return absl::visit(
+      [](const auto& proto) { return std::make_unique<ReportRequest>(proto); },
+      proto_);
+}
+
+}  // namespace enterprise_reporting
diff --git a/components/enterprise/browser/reporting/report_request.h b/components/enterprise/browser/reporting/report_request.h
new file mode 100644
index 0000000..92b0486
--- /dev/null
+++ b/components/enterprise/browser/reporting/report_request.h
@@ -0,0 +1,69 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_ENTERPRISE_BROWSER_REPORTING_REPORT_REQUEST_H_
+#define COMPONENTS_ENTERPRISE_BROWSER_REPORTING_REPORT_REQUEST_H_
+
+#include <memory>
+#include <queue>
+
+#include "build/chromeos_buildflags.h"
+#include "device_management_backend.pb.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
+
+namespace enterprise_reporting {
+
+enum class ReportType;
+
+// A class is used to allow reports of multiple types to be stored in a single
+// queue for the uploader.
+class ReportRequest {
+ public:
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  using DeviceReportRequestProto =
+      enterprise_management::ChromeOsUserReportRequest;
+#else
+  using DeviceReportRequestProto =
+      enterprise_management::ChromeDesktopReportRequest;
+#endif
+
+  explicit ReportRequest(ReportType type);
+  explicit ReportRequest(
+      const enterprise_management::ChromeDesktopReportRequest& proto);
+  explicit ReportRequest(
+      const enterprise_management::ChromeOsUserReportRequest& proto);
+  explicit ReportRequest(
+      const enterprise_management::ChromeProfileReportRequest& proto);
+
+  ReportRequest(const ReportRequest&) = delete;
+  ReportRequest& operator=(const ReportRequest&) = delete;
+  ~ReportRequest();
+
+  const DeviceReportRequestProto& GetDeviceReportRequest() const;
+  DeviceReportRequestProto& GetDeviceReportRequest();
+
+  const enterprise_management::ChromeProfileReportRequest&
+  GetChromeProfileReportRequest() const;
+  enterprise_management::ChromeProfileReportRequest&
+  GetChromeProfileReportRequest();
+
+  // Clone the request proto.
+  // This is required when a report is splitted into multiple requests.
+  // As all requests need to contain shared information like browser version or
+  // machine name. Long term, we can improves the splitting algorithm so Clone
+  // is no longer necessary.
+  std::unique_ptr<ReportRequest> Clone() const;
+
+ private:
+  absl::variant<enterprise_management::ChromeDesktopReportRequest,
+                enterprise_management::ChromeOsUserReportRequest,
+                enterprise_management::ChromeProfileReportRequest>
+      proto_;
+};
+
+using ReportRequestQueue = std::queue<std::unique_ptr<ReportRequest>>;
+
+}  // namespace enterprise_reporting
+
+#endif  // COMPONENTS_ENTERPRISE_BROWSER_REPORTING_REPORT_REQUEST_H_
diff --git a/components/enterprise/browser/reporting/report_request_definition.h b/components/enterprise/browser/reporting/report_request_definition.h
deleted file mode 100644
index 0775104..0000000
--- a/components/enterprise/browser/reporting/report_request_definition.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_ENTERPRISE_BROWSER_REPORTING_REPORT_REQUEST_DEFINITION_H_
-#define COMPONENTS_ENTERPRISE_BROWSER_REPORTING_REPORT_REQUEST_DEFINITION_H_
-
-#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
-#include "components/policy/proto/device_management_backend.pb.h"
-
-namespace enterprise_reporting {
-
-namespace definition {
-
-// Both ChromeOsUserReportRequest and ChromeDesktopReportRequest are used to
-// upload usage data to DM Server. By the reference to this macro, most classes
-// in enterprise_reporting namespace can share the same logic for various
-// operation systems.
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-using ReportRequest = enterprise_management::ChromeOsUserReportRequest;
-#else
-using ReportRequest = enterprise_management::ChromeDesktopReportRequest;
-#endif
-
-}  // namespace definition
-
-}  // namespace enterprise_reporting
-
-#endif  // COMPONENTS_ENTERPRISE_BROWSER_REPORTING_REPORT_REQUEST_DEFINITION_H_
diff --git a/components/enterprise/browser/reporting/report_request_queue_generator.cc b/components/enterprise/browser/reporting/report_request_queue_generator.cc
index 5557250..9caa827 100644
--- a/components/enterprise/browser/reporting/report_request_queue_generator.cc
+++ b/components/enterprise/browser/reporting/report_request_queue_generator.cc
@@ -54,23 +54,26 @@
   maximum_report_size_ = maximum_report_size;
 }
 
-ReportRequestQueueGenerator::ReportRequests
-ReportRequestQueueGenerator::Generate(const ReportRequest& basic_request) {
-  ReportRequests requests;
-  size_t basic_request_size = basic_request.ByteSizeLong();
+ReportRequestQueue ReportRequestQueueGenerator::Generate(
+    const ReportRequest& basic_request) {
+  ReportRequestQueue requests;
+  size_t basic_request_size =
+      basic_request.GetDeviceReportRequest().ByteSizeLong();
   base::UmaHistogramMemoryKB(kBasicRequestSizeMetricsName,
                              basic_request_size / 1024);
 
   if (basic_request_size <= maximum_report_size_) {
-    requests.push(std::make_unique<ReportRequest>(basic_request));
-    int profile_infos_size =
-        basic_request.browser_report().chrome_user_profile_infos_size();
+    requests.push(basic_request.Clone());
+    int profile_infos_size = basic_request.GetDeviceReportRequest()
+                                 .browser_report()
+                                 .chrome_user_profile_infos_size();
     for (int index = 0; index < profile_infos_size; index++) {
       GenerateProfileReportWithIndex(index, basic_request, &requests);
     }
 
-    base::UmaHistogramMemoryKB(kRequestSizeMetricsName,
-                               requests.back()->ByteSizeLong() / 1024);
+    base::UmaHistogramMemoryKB(
+        kRequestSizeMetricsName,
+        requests.back()->GetDeviceReportRequest().ByteSizeLong() / 1024);
   }
 
   base::UmaHistogramExactLinear(kRequestCountMetricsName, requests.size(),
@@ -81,13 +84,16 @@
 void ReportRequestQueueGenerator::GenerateProfileReportWithIndex(
     int profile_index,
     const ReportRequest& basic_request,
-    ReportRequests* requests) {
-  DCHECK_LT(profile_index,
-            basic_request.browser_report().chrome_user_profile_infos_size());
+    ReportRequestQueue* requests) {
+  DCHECK_LT(profile_index, basic_request.GetDeviceReportRequest()
+                               .browser_report()
+                               .chrome_user_profile_infos_size());
 
-  size_t basic_request_size = basic_request.ByteSizeLong();
-  auto basic_profile =
-      basic_request.browser_report().chrome_user_profile_infos(profile_index);
+  size_t basic_request_size =
+      basic_request.GetDeviceReportRequest().ByteSizeLong();
+  auto basic_profile = basic_request.GetDeviceReportRequest()
+                           .browser_report()
+                           .chrome_user_profile_infos(profile_index);
   auto profile_report = profile_report_generator_.MaybeGenerate(
       base::FilePath::FromUTF8Unsafe(basic_profile.id()), basic_profile.name(),
       ReportType::kFull);
@@ -100,13 +106,15 @@
   // is added. There are still few bytes difference but close enough.
   size_t profile_report_incremental_size =
       profile_report->ByteSizeLong() - basic_profile.ByteSizeLong();
-  size_t current_request_size = requests->back()->ByteSizeLong();
+  size_t current_request_size =
+      requests->back()->GetDeviceReportRequest().ByteSizeLong();
 
   if (current_request_size + profile_report_incremental_size <=
       maximum_report_size_) {
     // The new full Profile report can be appended into the current request.
     requests->back()
-        ->mutable_browser_report()
+        ->GetDeviceReportRequest()
+        .mutable_browser_report()
         ->mutable_chrome_user_profile_infos(profile_index)
         ->Swap(profile_report.get());
   } else if (basic_request_size + profile_report_incremental_size <=
@@ -114,11 +122,13 @@
     // The new full Profile report is too big to be appended into the current
     // request, move it to the next request if possible. Record metrics for the
     // current request's size.
-    base::UmaHistogramMemoryKB(kRequestSizeMetricsName,
-                               requests->back()->ByteSizeLong() / 1024);
-    requests->push(std::make_unique<ReportRequest>(basic_request));
+    base::UmaHistogramMemoryKB(
+        kRequestSizeMetricsName,
+        requests->back()->GetDeviceReportRequest().ByteSizeLong() / 1024);
+    requests->push(basic_request.Clone());
     requests->back()
-        ->mutable_browser_report()
+        ->GetDeviceReportRequest()
+        .mutable_browser_report()
         ->mutable_chrome_user_profile_infos(profile_index)
         ->Swap(profile_report.get());
   } else {
diff --git a/components/enterprise/browser/reporting/report_request_queue_generator.h b/components/enterprise/browser/reporting/report_request_queue_generator.h
index 47a172d6..2092234 100644
--- a/components/enterprise/browser/reporting/report_request_queue_generator.h
+++ b/components/enterprise/browser/reporting/report_request_queue_generator.h
@@ -5,12 +5,9 @@
 #ifndef COMPONENTS_ENTERPRISE_BROWSER_REPORTING_REPORT_REQUEST_QUEUE_GENERATOR_H_
 #define COMPONENTS_ENTERPRISE_BROWSER_REPORTING_REPORT_REQUEST_QUEUE_GENERATOR_H_
 
-#include <memory>
-#include <queue>
-
 #include "build/build_config.h"
 #include "components/enterprise/browser/reporting/profile_report_generator.h"
-#include "components/enterprise/browser/reporting/report_request_definition.h"
+#include "components/enterprise/browser/reporting/report_request.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 
 namespace enterprise_reporting {
@@ -22,9 +19,6 @@
 // TODO(crbug.com/1103732): Unit tests for this class are still in
 // chrome/browser/enterprise/reporting.
 class ReportRequestQueueGenerator {
-  using ReportRequest = definition::ReportRequest;
-  using ReportRequests = std::queue<std::unique_ptr<ReportRequest>>;
-
  public:
   explicit ReportRequestQueueGenerator(
       ReportingDelegateFactory* delegate_factory);
@@ -42,14 +36,14 @@
 
   // Generate a queue of requests including full profile info based on given
   // basic request.
-  ReportRequests Generate(const ReportRequest& basic_request);
+  ReportRequestQueue Generate(const ReportRequest& basic_request);
 
  private:
   // Generate request with full profile info at |profile_index| according to
   // |basic_request|, then store it into |requests|.
   void GenerateProfileReportWithIndex(int profile_index,
                                       const ReportRequest& basic_request,
-                                      ReportRequests* requests);
+                                      ReportRequestQueue* requests);
 
  private:
   size_t maximum_report_size_;
diff --git a/components/enterprise/browser/reporting/report_scheduler.cc b/components/enterprise/browser/reporting/report_scheduler.cc
index 6a8096c..842c3e2 100644
--- a/components/enterprise/browser/reporting/report_scheduler.cc
+++ b/components/enterprise/browser/reporting/report_scheduler.cc
@@ -261,8 +261,7 @@
   }
 }
 
-void ReportScheduler::OnReportGenerated(
-    ReportGenerator::ReportRequests requests) {
+void ReportScheduler::OnReportGenerated(ReportRequestQueue requests) {
   DCHECK_NE(active_trigger_, kTriggerNone);
   if (requests.empty()) {
     SYSLOG(ERROR)
diff --git a/components/enterprise/browser/reporting/report_scheduler.h b/components/enterprise/browser/reporting/report_scheduler.h
index aadb7e3..a4e3b60 100644
--- a/components/enterprise/browser/reporting/report_scheduler.h
+++ b/components/enterprise/browser/reporting/report_scheduler.h
@@ -140,7 +140,7 @@
 
   // Continues processing a report (contained in the |requests| collection) by
   // sending it to the uploader.
-  void OnReportGenerated(ReportGenerator::ReportRequests requests);
+  void OnReportGenerated(ReportRequestQueue requests);
 
   // Finishes processing following report upload. |status| indicates the result
   // of the attempted upload.
diff --git a/components/enterprise/browser/reporting/report_uploader.cc b/components/enterprise/browser/reporting/report_uploader.cc
index 1f0e0fb..73c6e4ef 100644
--- a/components/enterprise/browser/reporting/report_uploader.cc
+++ b/components/enterprise/browser/reporting/report_uploader.cc
@@ -10,6 +10,7 @@
 #include "base/time/time.h"
 #include "build/chromeos_buildflags.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
+#include "device_management_backend.pb.h"
 
 namespace em = enterprise_management;
 
@@ -40,7 +41,7 @@
       maximum_number_of_retries_(maximum_number_of_retries) {}
 ReportUploader::~ReportUploader() = default;
 
-void ReportUploader::SetRequestAndUpload(ReportRequests requests,
+void ReportUploader::SetRequestAndUpload(ReportRequestQueue requests,
                                          ReportCallback callback) {
   requests_ = std::move(requests);
   callback_ = std::move(callback);
@@ -48,16 +49,21 @@
 }
 
 void ReportUploader::Upload() {
-  auto request = std::make_unique<ReportRequest>(*requests_.front());
   auto callback = base::BindRepeating(&ReportUploader::OnRequestFinished,
                                       weak_ptr_factory_.GetWeakPtr());
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+  auto request =
+      std::make_unique<enterprise_management::ChromeOsUserReportRequest>(
+          requests_.front()->GetDeviceReportRequest());
   client_->UploadChromeOsUserReport(std::move(request), std::move(callback));
 #else
+  auto request =
+      std::make_unique<enterprise_management::ChromeDesktopReportRequest>(
+          requests_.front()->GetDeviceReportRequest());
   client_->UploadChromeDesktopReport(std::move(request), std::move(callback));
 #endif
-}
+}  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 void ReportUploader::OnRequestFinished(bool status) {
   if (status) {
diff --git a/components/enterprise/browser/reporting/report_uploader.h b/components/enterprise/browser/reporting/report_uploader.h
index 4882ac9..7fb17e5 100644
--- a/components/enterprise/browser/reporting/report_uploader.h
+++ b/components/enterprise/browser/reporting/report_uploader.h
@@ -5,13 +5,10 @@
 #ifndef COMPONENTS_ENTERPRISE_BROWSER_REPORTING_REPORT_UPLOADER_H_
 #define COMPONENTS_ENTERPRISE_BROWSER_REPORTING_REPORT_UPLOADER_H_
 
-#include <memory>
-#include <queue>
-
 #include "base/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/timer/timer.h"
-#include "components/enterprise/browser/reporting/report_request_definition.h"
+#include "components/enterprise/browser/reporting/report_request.h"
 #include "net/base/backoff_entry.h"
 
 namespace base {
@@ -43,8 +40,6 @@
                        // invalid dm token.
   };
 
-  using ReportRequest = definition::ReportRequest;
-  using ReportRequests = std::queue<std::unique_ptr<ReportRequest>>;
   // A callback to notify the upload result.
   using ReportCallback = base::OnceCallback<void(ReportStatus status)>;
 
@@ -58,7 +53,7 @@
 
   // Sets a list of requests and upload it. Request will be uploaded one after
   // another.
-  virtual void SetRequestAndUpload(ReportRequests requests,
+  virtual void SetRequestAndUpload(ReportRequestQueue requests,
                                    ReportCallback callback);
 
  private:
@@ -81,7 +76,7 @@
 
   raw_ptr<policy::CloudPolicyClient> client_;
   ReportCallback callback_;
-  ReportRequests requests_;
+  ReportRequestQueue requests_;
 
   net::BackoffEntry backoff_entry_;
   base::OneShotTimer backoff_request_timer_;
diff --git a/components/enterprise/browser/reporting/report_uploader_unittest.cc b/components/enterprise/browser/reporting/report_uploader_unittest.cc
index 1d68e4b..d71d481 100644
--- a/components/enterprise/browser/reporting/report_uploader_unittest.cc
+++ b/components/enterprise/browser/reporting/report_uploader_unittest.cc
@@ -10,6 +10,8 @@
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "components/enterprise/browser/reporting/report_request.h"
+#include "components/enterprise/browser/reporting/report_type.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -55,11 +57,12 @@
       ReportUploader::ReportStatus expected_status) {
     DCHECK_LE(number_of_request, 2)
         << "Please update kBrowserVersionNames above.";
-    ReportUploader::ReportRequests requests;
+    ReportRequestQueue requests;
     for (int i = 0; i < number_of_request; i++) {
-      auto request = std::make_unique<ReportUploader::ReportRequest>();
-      request->mutable_browser_report()->set_browser_version(
-          kBrowserVersionNames[i]);
+      auto request = std::make_unique<ReportRequest>(ReportType::kFull);
+      request->GetDeviceReportRequest()
+          .mutable_browser_report()
+          ->set_browser_version(kBrowserVersionNames[i]);
       requests.push(std::move(request));
     }
     has_responded_ = false;
@@ -231,23 +234,25 @@
   {
     InSequence s;
     // First report
-    EXPECT_CALL(client_,
-                UploadReportProxy(
-                    Property(&ReportUploader::ReportRequest::browser_report,
-                             Property(&em::BrowserReport::browser_version,
-                                      Eq(kBrowserVersionNames[0]))),
-                    _))
+    EXPECT_CALL(
+        client_,
+        UploadReportProxy(
+            Property(&ReportRequest::DeviceReportRequestProto::browser_report,
+                     Property(&em::BrowserReport::browser_version,
+                              Eq(kBrowserVersionNames[0]))),
+            _))
         .Times(3)
         .WillOnce(WithArgs<1>(policy::ScheduleStatusCallback(false)))
         .WillOnce(WithArgs<1>(policy::ScheduleStatusCallback(false)))
         .WillOnce(WithArgs<1>(policy::ScheduleStatusCallback(true)));
     // Second report
-    EXPECT_CALL(client_,
-                UploadReportProxy(
-                    Property(&ReportUploader::ReportRequest::browser_report,
-                             Property(&em::BrowserReport::browser_version,
-                                      Eq(kBrowserVersionNames[1]))),
-                    _))
+    EXPECT_CALL(
+        client_,
+        UploadReportProxy(
+            Property(&ReportRequest::DeviceReportRequestProto::browser_report,
+                     Property(&em::BrowserReport::browser_version,
+                              Eq(kBrowserVersionNames[1]))),
+            _))
         .Times(2)
         .WillOnce(WithArgs<1>(policy::ScheduleStatusCallback(false)))
         .WillOnce(WithArgs<1>(policy::ScheduleStatusCallback(false)));
diff --git a/components/exo/BUILD.gn b/components/exo/BUILD.gn
index 44a7095d..7fc9e3cf 100644
--- a/components/exo/BUILD.gn
+++ b/components/exo/BUILD.gn
@@ -13,10 +13,7 @@
 
 buildflag_header("buildflags") {
   header = "buildflags.h"
-  flags = [
-    "ENABLE_COLOR_MANAGER=$enable_color_manager",
-    "ENABLE_WESTON_TEST=$enable_weston_test",
-  ]
+  flags = [ "ENABLE_COLOR_MANAGER=$enable_color_manager" ]
 }
 
 if (use_xkbcommon) {
@@ -261,6 +258,7 @@
       "//ash:test_support",
       "//ash/public/cpp",
       "//chromeos/ui/base",
+      "//components/exo/wayland:weston_test_stub",
     ]
   }
 }
diff --git a/components/exo/buildflags.gni b/components/exo/buildflags.gni
index ed8e7ed2..fff3c98 100644
--- a/components/exo/buildflags.gni
+++ b/components/exo/buildflags.gni
@@ -9,9 +9,4 @@
   # If true, enables zcr_color_manager_v1. This is a temporary flag meant to
   # guard an in-progress implementation, to be replaced by a feature flag.
   enable_color_manager = false
-
-  # If true, enables weston-test. This is a test-only wayland extension that
-  # enables things like event injection.
-  enable_weston_test =
-      is_chromeos_ash && !is_chromeos_device && !is_official_build
 }
diff --git a/components/exo/server/BUILD.gn b/components/exo/server/BUILD.gn
index f9e4b458..4adb85c 100644
--- a/components/exo/server/BUILD.gn
+++ b/components/exo/server/BUILD.gn
@@ -8,7 +8,6 @@
 assert(is_chromeos_ash)
 
 source_set("server") {
-  testonly = enable_weston_test
   sources = [
     "wayland_server_controller.cc",
     "wayland_server_controller.h",
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index a068765..6edecb06 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -794,6 +794,7 @@
     params.activatable = views::Widget::InitParams::Activatable::kYes;
 
   params.delegate = new views::WidgetDelegate();
+  params.delegate->SetOwnedByWidget(true);
   params.delegate->SetContentsView(std::move(overlay_params.contents_view));
   params.name = "Overlay";
 
diff --git a/components/exo/wayland/BUILD.gn b/components/exo/wayland/BUILD.gn
index 4d4c05a48..32f8226 100644
--- a/components/exo/wayland/BUILD.gn
+++ b/components/exo/wayland/BUILD.gn
@@ -158,6 +158,7 @@
       "wayland_keyboard_delegate.h",
       "wayland_positioner.cc",
       "wayland_positioner.h",
+      "weston_test.h",
       "wl_shell.cc",
       "wl_shell.h",
       "xdg_shell.cc",
@@ -220,21 +221,34 @@
       "//third_party/wayland-protocols:idle_inhibit_protocol",
       "//ui/base/cursor/mojom:cursor_type",
     ]
-
-    if (enable_weston_test) {
-      testonly = true
-      deps += [
-        "//third_party/wayland-protocols:weston_test",
-        "//ui/base:test_support",
-      ]
-      sources += [
-        "weston_test.cc",
-        "weston_test.h",
-      ]
-    }
   }
 }
 
+static_library("weston_test") {
+  testonly = true
+  defines = [ "WESTON_TEST_IMPLEMENTATION" ]
+  deps = [
+    ":wayland",
+    "//base",
+    "//components/exo",
+    "//third_party/wayland:wayland_server",
+    "//third_party/wayland-protocols:weston_test",
+    "//ui/base:test_support",
+    "//ui/wm",
+  ]
+  sources = [ "weston_test.cc" ]
+  if (is_chromeos_ash) {
+    deps += [ "//ash" ]
+  }
+}
+
+static_library("weston_test_stub") {
+  testonly = false
+  defines = [ "WESTON_TEST_IMPLEMENTATION" ]
+  sources = [ "weston_test_stub.cc" ]
+  deps = [ ":wayland" ]
+}
+
 source_set("unit_tests") {
   testonly = true
 
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index 1461f8a..4a6b7a4b 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -79,6 +79,7 @@
 #include <idle-inhibit-unstable-v1-server-protocol.h>
 #include "ash/constants/ash_features.h"
 #include "base/system/sys_info.h"
+#include "components/exo/wayland/weston_test.h"
 #include "components/exo/wayland/wl_shell.h"
 #include "components/exo/wayland/xdg_shell.h"
 #include "components/exo/wayland/zcr_cursor_shapes.h"
@@ -101,11 +102,6 @@
 #include "components/exo/wayland/zxdg_output_manager.h"
 #include "components/exo/wayland/zxdg_shell.h"
 
-#if BUILDFLAG(ENABLE_WESTON_TEST)
-#include <weston-test-server-protocol.h>
-#include "components/exo/wayland/weston_test.h"
-#endif
-
 #if BUILDFLAG(ENABLE_COLOR_MANAGER)
 #include <chrome-color-management-server-protocol.h>
 #include "components/exo/wayland/zcr_color_manager.h"
@@ -389,12 +385,7 @@
                      1, display_, bind_zwp_idle_inhibit_manager);
   }
 
-#if BUILDFLAG(ENABLE_WESTON_TEST)
-  weston_test_data_ = std::make_unique<WestonTestState>();
-  wl_global_create(wl_display_.get(), &weston_test_interface,
-                   kWestonTestVersion, weston_test_data_.get(),
-                   bind_weston_test);
-#endif
+  weston_test_holder_ = std::make_unique<WestonTest>(wl_display_.get());
 
   zcr_keyboard_extension_data_ =
       std::make_unique<WaylandKeyboardExtension>(serial_tracker_.get());
diff --git a/components/exo/wayland/server.h b/components/exo/wayland/server.h
index 5630e7e..354ba35 100644
--- a/components/exo/wayland/server.h
+++ b/components/exo/wayland/server.h
@@ -38,7 +38,7 @@
 struct WaylandXdgShell;
 struct WaylandZxdgShell;
 struct WaylandRemoteShellData;
-struct WestonTestState;
+class WestonTest;
 class WaylandWatcher;
 
 // This class is a thin wrapper around a Wayland display server. All Wayland
@@ -135,9 +135,7 @@
   std::unique_ptr<WaylandZxdgShell> zxdg_shell_data_;
   std::unique_ptr<WaylandXdgShell> xdg_shell_data_;
   std::unique_ptr<WaylandRemoteShellData> remote_shell_data_;
-#if BUILDFLAG(ENABLE_WESTON_TEST)
-  std::unique_ptr<WestonTestState> weston_test_data_;
-#endif
+  std::unique_ptr<WestonTest> weston_test_holder_;
 #endif
 };
 
diff --git a/components/exo/wayland/weston_test.cc b/components/exo/wayland/weston_test.cc
index 4b08e0e..3e6ed9f 100644
--- a/components/exo/wayland/weston_test.cc
+++ b/components/exo/wayland/weston_test.cc
@@ -5,6 +5,7 @@
 #include "components/exo/wayland/weston_test.h"
 
 #include <linux/input.h>
+#include <stdint.h>
 #include <wayland-server-core.h>
 #include <weston-test-server-protocol.h>
 
@@ -23,8 +24,30 @@
 
 namespace exo {
 namespace wayland {
+
+// Tracks button and mouse states for testing.
+struct WestonTest::WestonTestState {
+  WestonTestState() {}
+
+  WestonTestState(const WestonTestState&) = delete;
+  WestonTestState& operator=(const WestonTestState&) = delete;
+
+  bool left_button_pressed = false;
+  bool middle_button_pressed = false;
+  bool right_button_pressed = false;
+
+  bool control_pressed = false;
+  bool alt_pressed = false;
+  bool shift_pressed = false;
+  bool command_pressed = false;
+};
+
 namespace {
 
+using WestonTestState = WestonTest::WestonTestState;
+
+constexpr uint32_t kWestonTestVersion = 1;
+
 static void weston_test_move_surface(struct wl_client* client,
                                      struct wl_resource* resource,
                                      struct wl_resource* surface_resource,
@@ -240,8 +263,6 @@
     weston_test_device_add,   weston_test_capture_screenshot,
     weston_test_send_touch};
 
-}  // namespace
-
 void bind_weston_test(wl_client* client,
                       void* data,
                       uint32_t version,
@@ -253,5 +274,15 @@
                                  nullptr);
 }
 
+}  // namespace
+
+WestonTest::WestonTest(wl_display* display)
+    : data_(std::make_unique<WestonTestState>()) {
+  wl_global_create(display, &weston_test_interface, kWestonTestVersion,
+                   data_.get(), bind_weston_test);
+}
+
+WestonTest::~WestonTest() = default;
+
 }  // namespace wayland
 }  // namespace exo
diff --git a/components/exo/wayland/weston_test.h b/components/exo/wayland/weston_test.h
index 745280f3..3c9b7bc 100644
--- a/components/exo/wayland/weston_test.h
+++ b/components/exo/wayland/weston_test.h
@@ -5,39 +5,27 @@
 #ifndef COMPONENTS_EXO_WAYLAND_WESTON_TEST_H_
 #define COMPONENTS_EXO_WAYLAND_WESTON_TEST_H_
 
-#include <stdint.h>
+#include <memory>
 
-struct wl_client;
+#include "base/component_export.h"
 
+struct wl_display;
 namespace exo {
-class Display;
-
 namespace wayland {
 
-// Tracks button and mouse states for testing.
-struct WestonTestState {
-  WestonTestState() {}
+class COMPONENT_EXPORT(WESTON_TEST) WestonTest {
+ public:
+  explicit WestonTest(wl_display* display);
+  WestonTest(const WestonTest&) = delete;
+  WestonTest& operator=(const WestonTest&) = delete;
+  ~WestonTest();
 
-  WestonTestState(const WestonTestState&) = delete;
-  WestonTestState& operator=(const WestonTestState&) = delete;
+  struct WestonTestState;
 
-  bool left_button_pressed = false;
-  bool middle_button_pressed = false;
-  bool right_button_pressed = false;
-
-  bool control_pressed = false;
-  bool alt_pressed = false;
-  bool shift_pressed = false;
-  bool command_pressed = false;
+ private:
+  std::unique_ptr<WestonTestState> data_;
 };
 
-constexpr uint32_t kWestonTestVersion = 1;
-
-void bind_weston_test(wl_client* client,
-                      void* data,
-                      uint32_t version,
-                      uint32_t id);
-
 }  // namespace wayland
 }  // namespace exo
 
diff --git a/components/exo/wayland/weston_test_stub.cc b/components/exo/wayland/weston_test_stub.cc
new file mode 100644
index 0000000..0bbaa95
--- /dev/null
+++ b/components/exo/wayland/weston_test_stub.cc
@@ -0,0 +1,17 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/wayland/weston_test.h"
+
+namespace exo {
+namespace wayland {
+
+struct WestonTest::WestonTestState {};
+
+WestonTest::WestonTest(wl_display* display) {}
+
+WestonTest::~WestonTest() = default;
+
+}  // namespace wayland
+}  // namespace exo
diff --git a/components/leveldb_proto/public/shared_proto_database_client_list.cc b/components/leveldb_proto/public/shared_proto_database_client_list.cc
index 2dd09cd..28996d09 100644
--- a/components/leveldb_proto/public/shared_proto_database_client_list.cc
+++ b/components/leveldb_proto/public/shared_proto_database_client_list.cc
@@ -15,7 +15,6 @@
 
 namespace leveldb_proto {
 
-
 // static
 std::string SharedProtoDatabaseClientList::ProtoDbTypeToString(
     ProtoDbType db_type) {
@@ -107,6 +106,8 @@
       return "VideoTutorialsV2Database";
     case ProtoDbType::COUPON_DATABASE:
       return "CouponDatabase";
+    case ProtoDbType::PAGE_ENTITY_METADATA_STORE:
+      return "PageEntityMetadataDatabase";
     case ProtoDbType::LAST:
       NOTREACHED();
       return std::string();
diff --git a/components/leveldb_proto/public/shared_proto_database_client_list.h b/components/leveldb_proto/public/shared_proto_database_client_list.h
index dc7f30a..abe40fb 100644
--- a/components/leveldb_proto/public/shared_proto_database_client_list.h
+++ b/components/leveldb_proto/public/shared_proto_database_client_list.h
@@ -66,6 +66,7 @@
   SIGNAL_STORAGE_CONFIG_DATABASE = 39,
   VIDEO_TUTORIALS_V2_DATABASE = 40,
   COUPON_DATABASE = 41,
+  PAGE_ENTITY_METADATA_STORE = 42,
   LAST,
 };
 
diff --git a/components/media_router/browser/android/media_router_android.cc b/components/media_router/browser/android/media_router_android.cc
index e3cb4ea..f977968 100644
--- a/components/media_router/browser/android/media_router_android.cc
+++ b/components/media_router/browser/android/media_router_android.cc
@@ -198,9 +198,6 @@
 void MediaRouterAndroid::RegisterMediaRoutesObserver(
     MediaRoutesObserver* observer) {
   DVLOG(2) << "Added MediaRoutesObserver: " << observer;
-  if (!observer->source_id().empty())
-    NOTIMPLEMENTED() << "Joinable routes query not implemented.";
-
   routes_observers_.AddObserver(observer);
 }
 
@@ -254,7 +251,7 @@
 
   active_routes_.push_back(route);
   for (auto& observer : routes_observers_)
-    observer.OnRoutesUpdated(active_routes_, std::vector<MediaRoute::Id>());
+    observer.OnRoutesUpdated(active_routes_);
   if (is_local) {
     MediaRouterMetrics::RecordCreateRouteResultCode(
         result->result_code(), mojom::MediaRouteProviderId::ANDROID_CAF);
@@ -341,7 +338,7 @@
     }
 
   for (auto& observer : routes_observers_)
-    observer.OnRoutesUpdated(active_routes_, std::vector<MediaRoute::Id>());
+    observer.OnRoutesUpdated(active_routes_);
 }
 
 std::unique_ptr<media::FlingingController>
diff --git a/components/media_router/browser/media_router_base.cc b/components/media_router/browser/media_router_base.cc
index a96dc1f..a7788b4 100644
--- a/components/media_router/browser/media_router_base.cc
+++ b/components/media_router/browser/media_router_base.cc
@@ -29,13 +29,9 @@
   ~InternalMediaRoutesObserver() override {}
 
   // MediaRoutesObserver
-  void OnRoutesUpdated(
-      const std::vector<MediaRoute>& routes,
-      const std::vector<MediaRoute::Id>& joinable_route_ids) override {
+  void OnRoutesUpdated(const std::vector<MediaRoute>& routes) override {
     current_routes = routes;
     off_the_record_route_ids.clear();
-    // TODO(crbug.com/611486): Have the MRPM pass a list of joinable route ids
-    // via |joinable_route_ids|, and check here if it is non-empty.
     has_route = !routes.empty();
     for (const auto& route : routes) {
       if (route.is_off_the_record())
diff --git a/components/media_router/browser/media_router_base_unittest.cc b/components/media_router/browser/media_router_base_unittest.cc
index a6c860d38..5910b36 100644
--- a/components/media_router/browser/media_router_base_unittest.cc
+++ b/components/media_router/browser/media_router_base_unittest.cc
@@ -123,17 +123,15 @@
   MediaRoute route1("route_1", source1, "sink_1", "", false, false);
   MediaRoute route2("route_2", source2, "sink_2", "", true, false);
   std::vector<MediaRoute> routes = {route1, route2};
-  std::vector<MediaRoute::Id> joinable_route_ids = {"route_1"};
 
   EXPECT_TRUE(router_.GetCurrentRoutes().empty());
-  routes_observer_->OnRoutesUpdated(routes, joinable_route_ids);
+  routes_observer_->OnRoutesUpdated(routes);
   std::vector<MediaRoute> current_routes = router_.GetCurrentRoutes();
   ASSERT_EQ(current_routes.size(), 2u);
   EXPECT_EQ(current_routes[0], route1);
   EXPECT_EQ(current_routes[1], route2);
 
-  routes_observer_->OnRoutesUpdated(std::vector<MediaRoute>(),
-                                    std::vector<MediaRoute::Id>());
+  routes_observer_->OnRoutesUpdated(std::vector<MediaRoute>());
   EXPECT_TRUE(router_.GetCurrentRoutes().empty());
 }
 
diff --git a/components/media_router/browser/media_routes_observer.cc b/components/media_router/browser/media_routes_observer.cc
index 74a0999..3846491 100644
--- a/components/media_router/browser/media_routes_observer.cc
+++ b/components/media_router/browser/media_routes_observer.cc
@@ -9,9 +9,8 @@
 
 namespace media_router {
 
-MediaRoutesObserver::MediaRoutesObserver(MediaRouter* router,
-                                         const MediaSource::Id& source_id)
-    : router_(router), source_id_(source_id) {
+MediaRoutesObserver::MediaRoutesObserver(MediaRouter* router)
+    : router_(router) {
   DCHECK(router_);
   router_->RegisterMediaRoutesObserver(this);
 }
diff --git a/components/media_router/browser/media_routes_observer.h b/components/media_router/browser/media_routes_observer.h
index 8c10005..426cc67 100644
--- a/components/media_router/browser/media_routes_observer.h
+++ b/components/media_router/browser/media_routes_observer.h
@@ -22,9 +22,7 @@
 // |source_id| is supplied, then the idea of joinable routes no longer applies.
 class MediaRoutesObserver {
  public:
-  explicit MediaRoutesObserver(MediaRouter* router)
-      : MediaRoutesObserver(router, MediaSource::Id()) {}
-  MediaRoutesObserver(MediaRouter* router, const MediaSource::Id& source_id);
+  explicit MediaRoutesObserver(MediaRouter* router);
 
   MediaRoutesObserver(const MediaRoutesObserver&) = delete;
   MediaRoutesObserver& operator=(const MediaRoutesObserver&) = delete;
@@ -32,22 +30,17 @@
   virtual ~MediaRoutesObserver();
 
   // Invoked when the list of routes and their associated sinks have been
-  // updated with the context of the |source_id|.  This will return a list of
-  // |routes| and a list of |joinable_route_ids|.  A route is joinable only if
-  // it is joinable in the context of the |source_id|.
+  // updated.
+  //
   // Implementations may not perform operations that modify the Media Router's
   // observer list. In particular, invoking this observer's destructor within
   // OnRoutesUpdated will result in undefined behavior.
-  virtual void OnRoutesUpdated(
-      const std::vector<MediaRoute>& routes,
-      const std::vector<MediaRoute::Id>& joinable_route_ids) {}
+  virtual void OnRoutesUpdated(const std::vector<MediaRoute>& routes) {}
 
   MediaRouter* router() const { return router_; }
-  const MediaSource::Id source_id() const { return source_id_; }
 
  private:
   const raw_ptr<MediaRouter> router_;
-  const MediaSource::Id source_id_;
 };
 
 }  // namespace media_router
diff --git a/components/media_router/browser/test/test_helper.cc b/components/media_router/browser/test/test_helper.cc
index f8a8094..12243d8 100644
--- a/components/media_router/browser/test/test_helper.cc
+++ b/components/media_router/browser/test/test_helper.cc
@@ -21,10 +21,8 @@
     : MediaSinksObserver(router, source, origin) {}
 MockMediaSinksObserver::~MockMediaSinksObserver() = default;
 
-MockMediaRoutesObserver::MockMediaRoutesObserver(
-    MediaRouter* router,
-    const MediaSource::Id source_id)
-    : MediaRoutesObserver(router, source_id) {}
+MockMediaRoutesObserver::MockMediaRoutesObserver(MediaRouter* router)
+    : MediaRoutesObserver(router) {}
 MockMediaRoutesObserver::~MockMediaRoutesObserver() = default;
 
 MockPresentationConnectionProxy::MockPresentationConnectionProxy() = default;
diff --git a/components/media_router/browser/test/test_helper.h b/components/media_router/browser/test/test_helper.h
index b325e37..519c0f34 100644
--- a/components/media_router/browser/test/test_helper.h
+++ b/components/media_router/browser/test/test_helper.h
@@ -51,14 +51,10 @@
 
 class MockMediaRoutesObserver : public MediaRoutesObserver {
  public:
-  explicit MockMediaRoutesObserver(
-      MediaRouter* router,
-      const MediaSource::Id source_id = std::string());
+  explicit MockMediaRoutesObserver(MediaRouter* router);
   ~MockMediaRoutesObserver() override;
 
-  MOCK_METHOD2(OnRoutesUpdated,
-               void(const std::vector<MediaRoute>& routes,
-                    const std::vector<MediaRoute::Id>& joinable_route_ids));
+  MOCK_METHOD1(OnRoutesUpdated, void(const std::vector<MediaRoute>& routes));
 };
 
 class MockPresentationConnectionProxy
diff --git a/components/media_router/common/mojom/media_router.mojom b/components/media_router/common/mojom/media_router.mojom
index 075e3eb1..704e1c5 100644
--- a/components/media_router/common/mojom/media_router.mojom
+++ b/components/media_router/common/mojom/media_router.mojom
@@ -337,30 +337,8 @@
   // Stops querying sinks for |media_source|.
   StopObservingMediaSinks(string media_source);
 
-  // Starts reporting the state of active media routes via
-  // OnRoutesUpdated() in the context of the |media_source|.  The
-  // |media_source| represents the application interested in the media
-  // routes (usually the web page from which the content originates).
-  // If no |media_source| is given, this should be considered an
-  // observer that is not associated with a media source, and thus
-  // cannot connect to a remote route without showing a source.  The
-  // |media_source| should be considered when returning joinable routes in the
-  // OnRoutesUpdated() call. If an empty |media_source| is given, there is no
-  // context in which a joinable route makes sense and therefore, there should
-  // not be any joinable routes returned in OnRoutesUpdated().
-  // Querying will continue until StopObservingMediaRoutes() is called with
-  // the same |media_source| (even if it's an empty string).
-  StartObservingMediaRoutes(string media_source);
-
-  // Stops querying the state of all media routes in the context of
-  // the |media_source|.  StartObservingMediaRoutes() has
-  // to be called with the same |media_source| for this to have any effect even
-  // if it's empty.  Thus, StartObservingMediaRoutes(media_source) must be
-  // matched with StopObservingMediaRoutes(media_source).
-  // Calling StopObservingMediaRoutes() without a media_source will stop
-  // any media routes queries associated with emtpy strings (queries
-  // that being with StartObservingMediaRoutes()).
-  StopObservingMediaRoutes(string media_source);
+  // Starts reporting the state of active media routes.
+  StartObservingMediaRoutes();
 
   // Starts listening for messages from the media sink for the route given by
   // |route_id|.
@@ -435,13 +413,9 @@
   // Called when issues are reported for media routes.
   OnIssue(Issue issue);
 
-  // Called when list of routes for a MediaRouteProvider has been updated in the
-  // context of the calling |media_source|.  The array |joinable_route_ids|
-  // should contain route IDs of joinable routes found in the |routes| array.
+  // Called when list of routes for a MediaRouteProvider has been updated.
   OnRoutesUpdated(MediaRouteProviderId provider_id,
-                  array<MediaRoute> routes,
-                  string media_source,
-                  array<string> joinable_route_ids);
+                  array<MediaRoute> routes);
 
   // Called when the state of presentation connected to route |route_id| has
   // changed to |state|.
diff --git a/components/nacl/loader/nacl_helper_linux.cc b/components/nacl/loader/nacl_helper_linux.cc
index 3c8ec93e..f9f69e71 100644
--- a/components/nacl/loader/nacl_helper_linux.cc
+++ b/components/nacl/loader/nacl_helper_linux.cc
@@ -57,15 +57,12 @@
 //   if (!child) {
 void BecomeNaClLoader(base::ScopedFD browser_fd,
                       const NaClLoaderSystemInfo& system_info,
-                      bool uses_nonsfi_mode,
                       nacl::NaClSandbox* nacl_sandbox) {
   DCHECK(nacl_sandbox);
   VLOG(1) << "NaCl loader: setting up IPC descriptor";
   // Close or shutdown IPC channels that we don't need anymore.
   PCHECK(0 == IGNORE_EINTR(close(kNaClZygoteDescriptor)));
 
-  CHECK(!uses_nonsfi_mode);
-
   // Always ignore SIGPIPE, for consistency with other Chrome processes and
   // because some IPC code, such as sync_socket_posix.cc, requires this.
   // We do this before seccomp-bpf is initialized.
@@ -73,7 +70,7 @@
 
   // Finish layer-1 sandbox initialization and initialize the layer-2 sandbox.
   CHECK(!nacl_sandbox->HasOpenDirectory());
-  nacl_sandbox->InitializeLayerTwoSandbox(uses_nonsfi_mode);
+  nacl_sandbox->InitializeLayerTwoSandbox();
   nacl_sandbox->SealLayerOneSandbox();
   nacl_sandbox->CheckSandboxingStateWithPolicy();
 
@@ -84,7 +81,6 @@
   mojo::core::Init();
 
   base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO);
-  CHECK(!uses_nonsfi_mode);
   NaClListener listener;
   listener.set_prereserved_sandbox_size(system_info.prereserved_sandbox_size);
   listener.set_number_of_cores(system_info.number_of_cores);
@@ -96,7 +92,6 @@
 // Start the NaCl loader in a child created by the NaCl loader Zygote.
 void ChildNaClLoaderInit(std::vector<base::ScopedFD> child_fds,
                          const NaClLoaderSystemInfo& system_info,
-                         bool uses_nonsfi_mode,
                          nacl::NaClSandbox* nacl_sandbox,
                          const std::string& channel_id) {
   DCHECK(child_fds.size() >
@@ -112,8 +107,7 @@
       std::move(child_fds[content::ZygoteForkDelegate::kBrowserFDIndex]));
   child_fds.clear();
 
-  BecomeNaClLoader(std::move(browser_fd), system_info, uses_nonsfi_mode,
-                   nacl_sandbox);
+  BecomeNaClLoader(std::move(browser_fd), system_info, nacl_sandbox);
   _exit(1);
 }
 
@@ -125,12 +119,6 @@
                        nacl::NaClSandbox* nacl_sandbox,
                        base::PickleIterator* input_iter,
                        base::Pickle* output_pickle) {
-  bool uses_nonsfi_mode;
-  if (!input_iter->ReadBool(&uses_nonsfi_mode)) {
-    LOG(ERROR) << "Could not read uses_nonsfi_mode status";
-    return false;
-  }
-
   std::string channel_id;
   if (!input_iter->ReadString(&channel_id)) {
     LOG(ERROR) << "Could not read channel_id string";
@@ -157,8 +145,8 @@
   }
 
   if (child_pid == 0) {
-    ChildNaClLoaderInit(std::move(child_fds), system_info, uses_nonsfi_mode,
-                        nacl_sandbox, channel_id);
+    ChildNaClLoaderInit(std::move(child_fds), system_info, nacl_sandbox,
+                        channel_id);
     NOTREACHED();
   }
 
diff --git a/components/nacl/loader/sandbox_linux/nacl_sandbox_linux.cc b/components/nacl/loader/sandbox_linux/nacl_sandbox_linux.cc
index 0f20fa6..d020f107 100644
--- a/components/nacl/loader/sandbox_linux/nacl_sandbox_linux.cc
+++ b/components/nacl/loader/sandbox_linux/nacl_sandbox_linux.cc
@@ -102,7 +102,6 @@
     : layer_one_enabled_(false),
       layer_one_sealed_(false),
       layer_two_enabled_(false),
-      layer_two_is_nonsfi_(false),
       proc_fd_(-1),
       setuid_sandbox_client_(sandbox::SetuidSandboxClient::Create()) {
   proc_fd_.reset(
@@ -180,7 +179,7 @@
   CHECK_EQ(expected_num_fds, sandbox::ProcUtil::CountOpenFds(proc_fd_.get()));
 }
 
-void NaClSandbox::InitializeLayerTwoSandbox(bool uses_nonsfi_mode) {
+void NaClSandbox::InitializeLayerTwoSandbox() {
   // seccomp-bpf only applies to the current thread, so it's critical to only
   // have a single thread running here.
   DCHECK(!layer_one_sealed_);
@@ -192,7 +191,6 @@
   // Pass proc_fd_ ownership to the BPF sandbox, which guarantees it will
   // be closed. There is no point in keeping it around since the BPF policy
   // will prevent its usage.
-  CHECK(!uses_nonsfi_mode);
   layer_two_enabled_ = nacl::InitializeBPFSandbox(std::move(proc_fd_));
 }
 
@@ -207,29 +205,10 @@
 }
 
 void NaClSandbox::CheckSandboxingStateWithPolicy() {
-  static const char kItIsDangerousMsg[] = " this is dangerous.";
-  static const char kItIsNotAllowedMsg[] =
-      " this is not allowed in this configuration.";
-
-  const bool can_be_no_sandbox = !layer_two_is_nonsfi_;
-
-  if (!layer_one_enabled_ || !layer_one_sealed_) {
-    static const char kNoSuidMsg[] =
-        "The SUID sandbox is not engaged for NaCl:";
-    if (can_be_no_sandbox)
-      LOG(ERROR) << kNoSuidMsg << kItIsDangerousMsg;
-    else
-      LOG(FATAL) << kNoSuidMsg << kItIsNotAllowedMsg;
-  }
-
-  if (!layer_two_enabled_) {
-    static const char kNoBpfMsg[] =
-        "The seccomp-bpf sandbox is not engaged for NaCl:";
-    if (can_be_no_sandbox)
-      LOG(ERROR) << kNoBpfMsg << kItIsDangerousMsg;
-    else
-      LOG(FATAL) << kNoBpfMsg << kItIsNotAllowedMsg;
-  }
+  LOG_IF(ERROR, !layer_one_enabled_ || !layer_one_sealed_)
+      << "The SUID sandbox is not engaged for NaCl: this is dangerous.";
+  LOG_IF(ERROR, !layer_two_enabled_)
+      << "The seccomp-bpf sandbox is not engaged for NaCl: this is dangerous.";
 }
 
 }  // namespace nacl
diff --git a/components/nacl/loader/sandbox_linux/nacl_sandbox_linux.h b/components/nacl/loader/sandbox_linux/nacl_sandbox_linux.h
index e11c923..aec8305 100644
--- a/components/nacl/loader/sandbox_linux/nacl_sandbox_linux.h
+++ b/components/nacl/loader/sandbox_linux/nacl_sandbox_linux.h
@@ -57,11 +57,10 @@
   // setuid sandbox or was started by the namespace sandbox.
   void InitializeLayerOneSandbox();
   // Will attempt to initialize the layer-2 sandbox, depending on flags and the
-  // environment. |uses_nonsfi_mode| describes which seccomp-bpf policy is
-  // appropriate.
+  // environment.
   // This layer will also add a limit to how much of the address space can be
   // used.
-  void InitializeLayerTwoSandbox(bool uses_nonsfi_mode);
+  void InitializeLayerTwoSandbox();
   // Seal the layer-1 sandbox, making it enforcing.
   void SealLayerOneSandbox();
   // Check that the current sandboxing state matches the level of sandboxing
@@ -77,7 +76,6 @@
   bool layer_one_enabled_;
   bool layer_one_sealed_;
   bool layer_two_enabled_;
-  bool layer_two_is_nonsfi_;
   // |proc_fd_| must be released before the layer-1 sandbox is considered
   // enforcing.
   base::ScopedFD proc_fd_;
diff --git a/components/nacl/zygote/nacl_fork_delegate_linux.cc b/components/nacl/zygote/nacl_fork_delegate_linux.cc
index 10f72938..5a71fd90 100644
--- a/components/nacl/zygote/nacl_fork_delegate_linux.cc
+++ b/components/nacl/zygote/nacl_fork_delegate_linux.cc
@@ -137,26 +137,15 @@
     return;
   }
 
-  delegates->push_back(
-      std::make_unique<NaClForkDelegate>(false /* nonsfi_mode */));
-  delegates->push_back(
-      std::make_unique<NaClForkDelegate>(true /* nonsfi_mode */));
+  delegates->push_back(std::make_unique<NaClForkDelegate>());
 }
 
-NaClForkDelegate::NaClForkDelegate(bool nonsfi_mode)
-    : nonsfi_mode_(nonsfi_mode), status_(kNaClHelperUnused), fd_(-1) {
-}
+NaClForkDelegate::NaClForkDelegate() : status_(kNaClHelperUnused), fd_(-1) {}
 
 void NaClForkDelegate::Init(const int sandboxdesc,
                             const bool enable_layer1_sandbox) {
   VLOG(1) << "NaClForkDelegate::Init()";
 
-  // non-SFI mode is no longer supported.
-  // TODO(b/200965779): Clean up the code.
-  if (nonsfi_mode_) {
-    return;
-  }
-
   // TODO(rickyz): Make IsSuidSandboxChild a static function.
   std::unique_ptr<sandbox::SetuidSandboxClient> setuid_sandbox_client(
       sandbox::SetuidSandboxClient::Create());
@@ -182,35 +171,28 @@
   int fds[2];
   PCHECK(0 == socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds));
 
-  bool use_nacl_bootstrap = false;
-  // For non-SFI mode, we do not use fixed address space.
-  if (!nonsfi_mode_) {
-    // Using nacl_helper_bootstrap is not necessary on x86-64 because
-    // NaCl's x86-64 sandbox is not zero-address-based.  Starting
-    // nacl_helper through nacl_helper_bootstrap works on x86-64, but it
-    // leaves nacl_helper_bootstrap mapped at a fixed address at the
-    // bottom of the address space, which is undesirable because it
-    // effectively defeats ASLR.
+  bool use_nacl_bootstrap = true;
 #if defined(ARCH_CPU_X86_64)
-    use_nacl_bootstrap = false;
+  // Using nacl_helper_bootstrap is not necessary on x86-64 because
+  // NaCl's x86-64 sandbox is not zero-address-based.  Starting
+  // nacl_helper through nacl_helper_bootstrap works on x86-64, but it
+  // leaves nacl_helper_bootstrap mapped at a fixed address at the
+  // bottom of the address space, which is undesirable because it
+  // effectively defeats ASLR.
+  use_nacl_bootstrap = false;
 #elif defined(ARCH_CPU_X86)
-    // Performance vs. security trade-off: We prefer using a
-    // non-zero-address-based sandbox on x86-32 because it provides some
-    // ASLR and so is more secure.  However, on Atom CPUs, using a
-    // non-zero segment base is very slow, so we use a zero-based
-    // sandbox on those.
-    use_nacl_bootstrap = NonZeroSegmentBaseIsSlow();
-#else
-    use_nacl_bootstrap = true;
+  // Performance vs. security trade-off: We prefer using a
+  // non-zero-address-based sandbox on x86-32 because it provides some
+  // ASLR and so is more secure.  However, on Atom CPUs, using a
+  // non-zero segment base is very slow, so we use a zero-based
+  // sandbox on those.
+  use_nacl_bootstrap = NonZeroSegmentBaseIsSlow();
 #endif
-  }
 
   status_ = kNaClHelperUnused;
   base::FilePath helper_exe;
   base::FilePath helper_bootstrap_exe;
-  if (!base::PathService::Get(
-          nonsfi_mode_ ? nacl::FILE_NACL_HELPER_NONSFI : nacl::FILE_NACL_HELPER,
-          &helper_exe)) {
+  if (!base::PathService::Get(nacl::FILE_NACL_HELPER, &helper_exe)) {
     status_ = kNaClHelperMissing;
   } else if (use_nacl_bootstrap &&
              !base::PathService::Get(nacl::FILE_NACL_HELPER_BOOTSTRAP,
@@ -231,7 +213,6 @@
           sandbox::policy::switches::kDisableSeccompFilterSandbox,
           sandbox::policy::switches::kNoSandbox,
           switches::kEnableNaClDebug,
-          switches::kNaClDangerousNoSandboxNonSfi,
       };
       const base::CommandLine& current_cmd_line =
           *base::CommandLine::ForCurrentProcess();
@@ -281,8 +262,7 @@
     max_these_limits.push_back(RLIMIT_AS);
     options.maximize_rlimits = &max_these_limits;
 
-    // To avoid information leaks in Non-SFI mode, clear the environment for
-    // the NaCl Helper process.
+    // Clear the environment for the NaCl Helper process.
     options.clear_environment = true;
     AddPassthroughEnvToOptions(&options);
 
@@ -338,8 +318,7 @@
 void NaClForkDelegate::InitialUMA(std::string* uma_name,
                                   int* uma_sample,
                                   int* uma_boundary_value) {
-  *uma_name = nonsfi_mode_ ? "NaCl.Client.HelperNonSFI.InitState"
-                           : "NaCl.Client.Helper.InitState";
+  *uma_name = "NaCl.Client.Helper.InitState";
   *uma_sample = status_;
   *uma_boundary_value = kNaClHelperStatusBoundary;
 }
@@ -356,14 +335,9 @@
                                std::string* uma_name,
                                int* uma_sample,
                                int* uma_boundary_value) {
-  // We can only help with a specific process type depending on nonsfi_mode_.
-  const char* helpable_process_type = nonsfi_mode_
-                                          ? switches::kNaClLoaderNonSfiProcess
-                                          : switches::kNaClLoaderProcess;
-  if (process_type != helpable_process_type)
+  if (process_type != switches::kNaClLoaderProcess)
     return false;
-  *uma_name = nonsfi_mode_ ? "NaCl.Client.HelperNonSFI.StateOnFork"
-                           : "NaCl.Client.Helper.StateOnFork";
+  *uma_name = "NaCl.Client.Helper.StateOnFork";
   *uma_sample = status_;
   *uma_boundary_value = kNaClHelperStatusBoundary;
   return true;
@@ -384,9 +358,6 @@
   // First, send a remote fork request.
   base::Pickle write_pickle;
   write_pickle.WriteInt(nacl::kNaClForkRequest);
-  // TODO(hamaji): When we split the helper binary for non-SFI mode
-  // from nacl_helper, stop sending this information.
-  write_pickle.WriteBool(nonsfi_mode_);
   write_pickle.WriteString(channel_id);
 
   char reply_buf[kNaClMaxIPCMessageLength];
diff --git a/components/nacl/zygote/nacl_fork_delegate_linux.h b/components/nacl/zygote/nacl_fork_delegate_linux.h
index 99a0130..a59e18c3 100644
--- a/components/nacl/zygote/nacl_fork_delegate_linux.h
+++ b/components/nacl/zygote/nacl_fork_delegate_linux.h
@@ -30,11 +30,9 @@
 // ZygoteMain().
 class NaClForkDelegate : public content::ZygoteForkDelegate {
  public:
-  explicit NaClForkDelegate(bool nonsfi_mode);
-
+  NaClForkDelegate();
   NaClForkDelegate(const NaClForkDelegate&) = delete;
   NaClForkDelegate& operator=(const NaClForkDelegate&) = delete;
-
   ~NaClForkDelegate() override;
 
   void Init(int sandboxdesc, bool enable_layer1_sandbox) override;
@@ -69,7 +67,6 @@
     kNaClHelperStatusBoundary  // Must be one greater than highest value used.
   };
 
-  const bool nonsfi_mode_;
   NaClHelperStatus status_;
   int fd_;
 
diff --git a/components/optimization_guide/content/browser/page_content_annotations_service.cc b/components/optimization_guide/content/browser/page_content_annotations_service.cc
index 8d14212..5a61f45 100644
--- a/components/optimization_guide/content/browser/page_content_annotations_service.cc
+++ b/components/optimization_guide/content/browser/page_content_annotations_service.cc
@@ -8,6 +8,8 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/history/core/browser/history_service.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
+#include "components/optimization_guide/core/local_page_entities_metadata_provider.h"
 #include "components/optimization_guide/core/noisy_metrics_recorder.h"
 #include "components/optimization_guide/core/optimization_guide_enums.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
@@ -76,7 +78,10 @@
 PageContentAnnotationsService::PageContentAnnotationsService(
     const std::string& application_locale,
     OptimizationGuideModelProvider* optimization_guide_model_provider,
-    history::HistoryService* history_service)
+    history::HistoryService* history_service,
+    leveldb_proto::ProtoDatabaseProvider* database_provider,
+    const base::FilePath& database_dir,
+    scoped_refptr<base::SequencedTaskRunner> background_task_runner)
     : last_annotated_history_visits_(
           features::MaxContentAnnotationRequestsCached()) {
   DCHECK(optimization_guide_model_provider);
@@ -87,6 +92,13 @@
       application_locale, optimization_guide_model_provider);
   annotator_ = model_manager_.get();
 #endif
+
+  if (features::UseLocalPageEntitiesMetadataProvider()) {
+    local_page_entities_metadata_provider_ =
+        std::make_unique<LocalPageEntitiesMetadataProvider>();
+    local_page_entities_metadata_provider_->Initialize(
+        database_provider, database_dir, background_task_runner);
+  }
 }
 
 PageContentAnnotationsService::~PageContentAnnotationsService() = default;
@@ -258,6 +270,13 @@
 void PageContentAnnotationsService::GetMetadataForEntityId(
     const std::string& entity_id,
     EntityMetadataRetrievedCallback callback) {
+  if (features::UseLocalPageEntitiesMetadataProvider()) {
+    DCHECK(local_page_entities_metadata_provider_);
+    local_page_entities_metadata_provider_->GetMetadataForEntityId(
+        entity_id, std::move(callback));
+    return;
+  }
+
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
   model_manager_->GetMetadataForEntityId(entity_id, std::move(callback));
 #else
diff --git a/components/optimization_guide/content/browser/page_content_annotations_service.h b/components/optimization_guide/content/browser/page_content_annotations_service.h
index 4e361811..6fe6e21 100644
--- a/components/optimization_guide/content/browser/page_content_annotations_service.h
+++ b/components/optimization_guide/content/browser/page_content_annotations_service.h
@@ -9,12 +9,14 @@
 
 #include "base/callback_forward.h"
 #include "base/containers/lru_cache.h"
+#include "base/files/file_path.h"
 #include "base/hash/hash.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task/cancelable_task_tracker.h"
+#include "base/task/sequenced_task_runner.h"
 #include "components/continuous_search/browser/search_result_extractor_client.h"
 #include "components/continuous_search/browser/search_result_extractor_client_status.h"
 #include "components/continuous_search/common/public/mojom/continuous_search.mojom.h"
@@ -36,8 +38,13 @@
 class HistoryService;
 }  // namespace history
 
+namespace leveldb_proto {
+class ProtoDatabaseProvider;
+}  // namespace leveldb_proto
+
 namespace optimization_guide {
 
+class LocalPageEntitiesMetadataProvider;
 class OptimizationGuideModelProvider;
 class PageContentAnnotationsModelManager;
 class PageContentAnnotationsServiceBrowserTest;
@@ -65,7 +72,10 @@
   PageContentAnnotationsService(
       const std::string& application_locale,
       OptimizationGuideModelProvider* optimization_guide_model_provider,
-      history::HistoryService* history_service);
+      history::HistoryService* history_service,
+      leveldb_proto::ProtoDatabaseProvider* database_provider,
+      const base::FilePath& database_dir,
+      scoped_refptr<base::SequencedTaskRunner> background_task_runner);
   ~PageContentAnnotationsService() override;
   PageContentAnnotationsService(const PageContentAnnotationsService&) = delete;
   PageContentAnnotationsService& operator=(
@@ -165,6 +175,13 @@
                     PersistAnnotationsCallback callback,
                     history::QueryURLResult url_result);
 
+  // A metadata-only provider for page entities (as opposed to |model_manager_|
+  // which does both entity model execution and metadata providing) that uses a
+  // local database to provide the metadata for a given entity id. This is only
+  // non-null and initialized when its feature flag is enabled.
+  std::unique_ptr<LocalPageEntitiesMetadataProvider>
+      local_page_entities_metadata_provider_;
+
   // The history service to write content annotations to. Not owned. Guaranteed
   // to outlive |this|.
   raw_ptr<history::HistoryService> history_service_;
diff --git a/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer_unittest.cc b/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer_unittest.cc
index c08c20f..b9ff5dc 100644
--- a/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer_unittest.cc
+++ b/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer_unittest.cc
@@ -62,7 +62,10 @@
       history::HistoryService* history_service)
       : PageContentAnnotationsService("en-US",
                                       optimization_guide_model_provider,
-                                      history_service) {}
+                                      history_service,
+                                      nullptr,
+                                      base::FilePath(),
+                                      nullptr) {}
   ~FakePageContentAnnotationsService() override = default;
 
   void Annotate(const HistoryVisit& visit, const std::string& text) override {
diff --git a/components/optimization_guide/core/BUILD.gn b/components/optimization_guide/core/BUILD.gn
index e66d20f..0f858834 100644
--- a/components/optimization_guide/core/BUILD.gn
+++ b/components/optimization_guide/core/BUILD.gn
@@ -54,6 +54,8 @@
     "hints_processing_util.cc",
     "hints_processing_util.h",
     "insertion_ordered_set.h",
+    "local_page_entities_metadata_provider.cc",
+    "local_page_entities_metadata_provider.h",
     "memory_hint.cc",
     "memory_hint.h",
     "model_executor.h",
@@ -251,6 +253,7 @@
     "hints_manager_unittest.cc",
     "hints_processing_util_unittest.cc",
     "insertion_ordered_set_unittest.cc",
+    "local_page_entities_metadata_provider_unittest.cc",
     "model_handler_unittest.cc",
     "noisy_metrics_recorder_unittest.cc",
     "optimization_filter_unittest.cc",
diff --git a/components/optimization_guide/core/entity_metadata_provider.h b/components/optimization_guide/core/entity_metadata_provider.h
index 54ec81d..14f7def 100644
--- a/components/optimization_guide/core/entity_metadata_provider.h
+++ b/components/optimization_guide/core/entity_metadata_provider.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_OPTIMIZATION_GUIDE_CORE_ENTITY_METADATA_PROVIDER_H_
 
 #include "components/optimization_guide/core/entity_metadata.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace optimization_guide {
 
diff --git a/components/optimization_guide/core/local_page_entities_metadata_provider.cc b/components/optimization_guide/core/local_page_entities_metadata_provider.cc
new file mode 100644
index 0000000..18a32f9
--- /dev/null
+++ b/components/optimization_guide/core/local_page_entities_metadata_provider.cc
@@ -0,0 +1,93 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/optimization_guide/core/local_page_entities_metadata_provider.h"
+
+#include "components/optimization_guide/core/entity_metadata.h"
+
+namespace optimization_guide {
+
+namespace {
+
+// The amount of data to build up in memory before converting to a sorted on-
+// disk file.
+constexpr size_t kDatabaseWriteBufferSizeBytes = 128 * 1024;
+
+}  // namespace
+
+LocalPageEntitiesMetadataProvider::LocalPageEntitiesMetadataProvider() =
+    default;
+LocalPageEntitiesMetadataProvider::~LocalPageEntitiesMetadataProvider() =
+    default;
+
+void LocalPageEntitiesMetadataProvider::Initialize(
+    leveldb_proto::ProtoDatabaseProvider* database_provider,
+    const base::FilePath& database_dir,
+    scoped_refptr<base::SequencedTaskRunner> background_task_runner) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  background_task_runner_ = std::move(background_task_runner);
+  database_ = database_provider->GetDB<proto::EntityMetadataStorage>(
+      leveldb_proto::ProtoDbType::PAGE_ENTITY_METADATA_STORE, database_dir,
+      background_task_runner_);
+
+  leveldb_env::Options options = leveldb_proto::CreateSimpleOptions();
+  options.write_buffer_size = kDatabaseWriteBufferSizeBytes;
+  database_->Init(
+      options,
+      base::BindOnce(&LocalPageEntitiesMetadataProvider::OnDatabaseInitialized,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void LocalPageEntitiesMetadataProvider::InitializeForTesting(
+    std::unique_ptr<leveldb_proto::ProtoDatabase<proto::EntityMetadataStorage>>
+        database,
+    scoped_refptr<base::SequencedTaskRunner> background_task_runner) {
+  database_ = std::move(database);
+  background_task_runner_ = std::move(background_task_runner);
+}
+
+void LocalPageEntitiesMetadataProvider::OnDatabaseInitialized(
+    leveldb_proto::Enums::InitStatus status) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (status != leveldb_proto::Enums::InitStatus::kOK) {
+    database_.reset();
+    return;
+  }
+}
+
+void LocalPageEntitiesMetadataProvider::GetMetadataForEntityId(
+    const std::string& entity_id,
+    EntityMetadataRetrievedCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!database_) {
+    std::move(callback).Run(absl::nullopt);
+    return;
+  }
+
+  database_->GetEntry(
+      entity_id, base::BindOnce(&LocalPageEntitiesMetadataProvider::OnGotEntry,
+                                weak_ptr_factory_.GetWeakPtr(), entity_id,
+                                std::move(callback)));
+}
+
+void LocalPageEntitiesMetadataProvider::OnGotEntry(
+    const std::string& entity_id,
+    EntityMetadataRetrievedCallback callback,
+    bool success,
+    std::unique_ptr<proto::EntityMetadataStorage> entry) {
+  if (!success || !entry) {
+    std::move(callback).Run(absl::nullopt);
+    return;
+  }
+
+  EntityMetadata md;
+  md.entity_id = entity_id;
+  md.human_readable_name = entry->entity_name();
+
+  std::move(callback).Run(md);
+}
+
+}  // namespace optimization_guide
\ No newline at end of file
diff --git a/components/optimization_guide/core/local_page_entities_metadata_provider.h b/components/optimization_guide/core/local_page_entities_metadata_provider.h
new file mode 100644
index 0000000..11070c89
--- /dev/null
+++ b/components/optimization_guide/core/local_page_entities_metadata_provider.h
@@ -0,0 +1,67 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OPTIMIZATION_GUIDE_CORE_LOCAL_PAGE_ENTITIES_METADATA_PROVIDER_H_
+#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_LOCAL_PAGE_ENTITIES_METADATA_PROVIDER_H_
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "components/leveldb_proto/public/proto_database.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
+#include "components/optimization_guide/core/entity_metadata_provider.h"
+#include "components/optimization_guide/proto/page_entities_metadata.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace optimization_guide {
+
+// Provides EntityMetadata given an entity id by looking up entries in a local
+// database on-disk.
+class LocalPageEntitiesMetadataProvider : public EntityMetadataProvider {
+ public:
+  LocalPageEntitiesMetadataProvider();
+  ~LocalPageEntitiesMetadataProvider() override;
+  LocalPageEntitiesMetadataProvider(const LocalPageEntitiesMetadataProvider&) =
+      delete;
+  LocalPageEntitiesMetadataProvider& operator=(
+      const LocalPageEntitiesMetadataProvider&) = delete;
+
+  // Initializes this class, setting |database_| and |background_task_runner_|.
+  void Initialize(
+      leveldb_proto::ProtoDatabaseProvider* database_provider,
+      const base::FilePath& database_dir,
+      scoped_refptr<base::SequencedTaskRunner> background_task_runner);
+
+  // Directly sets |database_| and |background_task_runner_| for tests.
+  void InitializeForTesting(
+      std::unique_ptr<
+          leveldb_proto::ProtoDatabase<proto::EntityMetadataStorage>> database,
+      scoped_refptr<base::SequencedTaskRunner> background_task_runner);
+
+  // EntityMetadataProvider:
+  void GetMetadataForEntityId(
+      const std::string& entity_id,
+      EntityMetadataRetrievedCallback callback) override;
+
+ private:
+  void OnDatabaseInitialized(leveldb_proto::Enums::InitStatus status);
+  void OnGotEntry(const std::string& entity_id,
+                  EntityMetadataRetrievedCallback callback,
+                  bool success,
+                  std::unique_ptr<proto::EntityMetadataStorage> entry);
+
+  std::unique_ptr<leveldb_proto::ProtoDatabase<proto::EntityMetadataStorage>>
+      database_;
+
+  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<LocalPageEntitiesMetadataProvider> weak_ptr_factory_{
+      this};
+};
+
+}  // namespace optimization_guide
+
+#endif  // COMPONENTS_OPTIMIZATION_GUIDE_CORE_LOCAL_PAGE_ENTITIES_METADATA_PROVIDER_H_
\ No newline at end of file
diff --git a/components/optimization_guide/core/local_page_entities_metadata_provider_unittest.cc b/components/optimization_guide/core/local_page_entities_metadata_provider_unittest.cc
new file mode 100644
index 0000000..7a24cbe
--- /dev/null
+++ b/components/optimization_guide/core/local_page_entities_metadata_provider_unittest.cc
@@ -0,0 +1,134 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/optimization_guide/core/local_page_entities_metadata_provider.h"
+
+#include "base/test/task_environment.h"
+#include "components/leveldb_proto/testing/fake_db.h"
+#include "components/optimization_guide/core/optimization_guide_features.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace optimization_guide {
+
+class LocalPageEntitiesMetadataProviderTest : public testing::Test {
+ public:
+  LocalPageEntitiesMetadataProviderTest() = default;
+  ~LocalPageEntitiesMetadataProviderTest() override = default;
+
+  void SetUp() override {
+    auto db = std::make_unique<
+        leveldb_proto::test::FakeDB<proto::EntityMetadataStorage>>(&db_store_);
+    db_ = db.get();
+
+    provider_ = std::make_unique<LocalPageEntitiesMetadataProvider>();
+    provider_->InitializeForTesting(
+        std::move(db), task_environment_.GetMainThreadTaskRunner());
+  }
+
+  LocalPageEntitiesMetadataProvider* provider() { return provider_.get(); }
+
+  leveldb_proto::test::FakeDB<proto::EntityMetadataStorage>* db() {
+    return db_;
+  }
+
+  std::map<std::string, proto::EntityMetadataStorage>* store() {
+    return &db_store_;
+  }
+
+ private:
+  base::test::TaskEnvironment task_environment_;
+  std::unique_ptr<LocalPageEntitiesMetadataProvider> provider_;
+  leveldb_proto::test::FakeDB<proto::EntityMetadataStorage>* db_;
+  std::map<std::string, proto::EntityMetadataStorage> db_store_;
+};
+
+TEST_F(LocalPageEntitiesMetadataProviderTest, NonInitReturnsNullOpt) {
+  LocalPageEntitiesMetadataProvider provider;
+
+  absl::optional<EntityMetadata> md;
+  bool callback_ran = false;
+  provider.GetMetadataForEntityId(
+      "entity_id",
+      base::BindOnce(
+          [](bool* callback_ran_flag, absl::optional<EntityMetadata>* md_out,
+             const absl::optional<EntityMetadata>& md_in) {
+            *callback_ran_flag = true;
+            *md_out = md_in;
+          },
+          &callback_ran, &md));
+
+  ASSERT_TRUE(callback_ran);
+  EXPECT_EQ(absl::nullopt, md);
+}
+
+TEST_F(LocalPageEntitiesMetadataProviderTest, EmptyStoreReturnsNullOpt) {
+  absl::optional<EntityMetadata> md;
+  bool callback_ran = false;
+  provider()->GetMetadataForEntityId(
+      "entity_id",
+      base::BindOnce(
+          [](bool* callback_ran_flag, absl::optional<EntityMetadata>* md_out,
+             const absl::optional<EntityMetadata>& md_in) {
+            *callback_ran_flag = true;
+            *md_out = md_in;
+          },
+          &callback_ran, &md));
+
+  db()->GetCallback(/*success=*/true);
+
+  ASSERT_TRUE(callback_ran);
+  EXPECT_EQ(absl::nullopt, md);
+}
+
+TEST_F(LocalPageEntitiesMetadataProviderTest, PopulatedSuccess) {
+  proto::EntityMetadataStorage stored_proto;
+  stored_proto.set_entity_name("chip");
+  store()->emplace("chocolate", stored_proto);
+
+  EntityMetadata want_md;
+  want_md.entity_id = "chocolate";
+  want_md.human_readable_name = "chip";
+
+  absl::optional<EntityMetadata> md;
+  bool callback_ran = false;
+  provider()->GetMetadataForEntityId(
+      "chocolate",
+      base::BindOnce(
+          [](bool* callback_ran_flag, absl::optional<EntityMetadata>* md_out,
+             const absl::optional<EntityMetadata>& md_in) {
+            *callback_ran_flag = true;
+            *md_out = md_in;
+          },
+          &callback_ran, &md));
+
+  db()->GetCallback(/*success=*/true);
+
+  ASSERT_TRUE(callback_ran);
+  EXPECT_EQ(absl::make_optional(want_md), md);
+}
+
+TEST_F(LocalPageEntitiesMetadataProviderTest, PopulatedFailure) {
+  proto::EntityMetadataStorage stored_proto;
+  stored_proto.set_entity_name("chip");
+  store()->emplace("chocolate", stored_proto);
+
+  absl::optional<EntityMetadata> md;
+  bool callback_ran = false;
+  provider()->GetMetadataForEntityId(
+      "chocolate",
+      base::BindOnce(
+          [](bool* callback_ran_flag, absl::optional<EntityMetadata>* md_out,
+             const absl::optional<EntityMetadata>& md_in) {
+            *callback_ran_flag = true;
+            *md_out = md_in;
+          },
+          &callback_ran, &md));
+
+  db()->GetCallback(/*success=*/false);
+
+  ASSERT_TRUE(callback_ran);
+  EXPECT_EQ(absl::nullopt, md);
+}
+
+}  // namespace optimization_guide
\ No newline at end of file
diff --git a/components/optimization_guide/core/optimization_guide_constants.cc b/components/optimization_guide/core/optimization_guide_constants.cc
index a8102e2..c6b1318 100644
--- a/components/optimization_guide/core/optimization_guide_constants.cc
+++ b/components/optimization_guide/core/optimization_guide_constants.cc
@@ -27,4 +27,7 @@
     kOptimizationGuidePredictionModelAndFeaturesStore[] =
         FILE_PATH_LITERAL("optimization_guide_model_and_features_store");
 
+const base::FilePath::CharType kPageEntitiesMetadataStore[] =
+    FILE_PATH_LITERAL("page_content_annotations_page_entities_metadata_store");
+
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/optimization_guide_constants.h b/components/optimization_guide/core/optimization_guide_constants.h
index 8b94c027..d095761b8 100644
--- a/components/optimization_guide/core/optimization_guide_constants.h
+++ b/components/optimization_guide/core/optimization_guide_constants.h
@@ -33,6 +33,9 @@
 extern const base::FilePath::CharType
     kOptimizationGuidePredictionModelAndFeaturesStore[];
 
+// The folder where the page entities metadata store will be stored on disk.
+extern const base::FilePath::CharType kPageEntitiesMetadataStore[];
+
 }  // namespace optimization_guide
 
 #endif  // COMPONENTS_OPTIMIZATION_GUIDE_CORE_OPTIMIZATION_GUIDE_CONSTANTS_H_
diff --git a/components/optimization_guide/core/optimization_guide_features.cc b/components/optimization_guide/core/optimization_guide_features.cc
index 5d849cff..66f80905 100644
--- a/components/optimization_guide/core/optimization_guide_features.cc
+++ b/components/optimization_guide/core/optimization_guide_features.cc
@@ -96,6 +96,9 @@
 const base::Feature kPageVisibilityBatchAnnotations{
     "PageVisibilityBatchAnnotations", base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kUseLocalPageEntitiesMetadataProvider{
+    "UseLocalPageEntitiesMetadataProvider", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // The default value here is a bit of a guess.
 // TODO(crbug/1163244): This should be tuned once metrics are available.
 base::TimeDelta PageTextExtractionOutstandingRequestsGracePeriod() {
@@ -508,5 +511,9 @@
   return base::FeatureList::IsEnabled(kPageVisibilityBatchAnnotations);
 }
 
+bool UseLocalPageEntitiesMetadataProvider() {
+  return base::FeatureList::IsEnabled(kUseLocalPageEntitiesMetadataProvider);
+}
+
 }  // namespace features
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/optimization_guide_features.h b/components/optimization_guide/core/optimization_guide_features.h
index 37bfa5f..80010d3 100644
--- a/components/optimization_guide/core/optimization_guide_features.h
+++ b/components/optimization_guide/core/optimization_guide_features.h
@@ -34,6 +34,7 @@
 extern const base::Feature kOptimizationGuideMetadataValidation;
 extern const base::Feature kPageTopicsBatchAnnotations;
 extern const base::Feature kPageVisibilityBatchAnnotations;
+extern const base::Feature kUseLocalPageEntitiesMetadataProvider;
 
 // The grace period duration for how long to give outstanding page text dump
 // requests to respond after DidFinishLoad.
@@ -245,6 +246,9 @@
 // Returns if Page Visibility Batch Annotations are enabled.
 bool PageVisibilityBatchAnnotationsEnabled();
 
+// Whether to use the leveldb-based page entities metadata provider.
+bool UseLocalPageEntitiesMetadataProvider();
+
 }  // namespace features
 }  // namespace optimization_guide
 
diff --git a/components/optimization_guide/proto/page_entities_metadata.proto b/components/optimization_guide/proto/page_entities_metadata.proto
index 96a8479f1..1b5fa33 100644
--- a/components/optimization_guide/proto/page_entities_metadata.proto
+++ b/components/optimization_guide/proto/page_entities_metadata.proto
@@ -24,7 +24,18 @@
 // entities on the page.
 //
 // It is only populated for the PAGE_ENTITIES optimization type.
+//
+// Note that the meaning of metadata here is in relation to a page load.
 message PageEntitiesMetadata {
   // A set of entities that are expected to be present on the page.
   repeated Entity entities = 1;
 }
+
+// The metadata associated with an |Entity|.
+//
+// Each |Entity| has some attached metadata about it which may be stored on
+// device for later lookup. Notably, this includes it's human-readable name as
+// opposed to the opaque entity_id.
+message EntityMetadataStorage {
+  optional string entity_name = 1;
+}
\ No newline at end of file
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index c23d8b7..1b74223 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -48,6 +48,10 @@
 #endif
 };
 
+const base::Feature kUseMultipleOverlays{"UseMultipleOverlays",
+                                         base::FEATURE_DISABLED_BY_DEFAULT};
+const char kMaxOverlaysParam[] = "max_overlays";
+
 const base::Feature kDelegatedCompositing{"DelegatedCompositing",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -358,4 +362,17 @@
   return result;
 }
 
+int MaxOverlaysConsidered() {
+  if (!IsOverlayPrioritizationEnabled()) {
+    return 1;
+  }
+
+  if (!base::FeatureList::IsEnabled(kUseMultipleOverlays)) {
+    return 1;
+  }
+
+  return base::GetFieldTrialParamByFeatureAsInt(kUseMultipleOverlays,
+                                                kMaxOverlaysParam, 2);
+}
+
 }  // namespace features
diff --git a/components/viz/common/features.h b/components/viz/common/features.h
index ccee1a7..6d65cbd 100644
--- a/components/viz/common/features.h
+++ b/components/viz/common/features.h
@@ -25,6 +25,8 @@
 VIZ_COMMON_EXPORT extern const base::Feature kUseSkiaRenderer;
 VIZ_COMMON_EXPORT extern const base::Feature kRecordSkPicture;
 VIZ_COMMON_EXPORT extern const base::Feature kDisableDeJelly;
+VIZ_COMMON_EXPORT extern const base::Feature kUseMultipleOverlays;
+VIZ_COMMON_EXPORT extern const char kMaxOverlaysParam[];
 #if defined(OS_ANDROID)
 VIZ_COMMON_EXPORT extern const base::Feature kDynamicColorGamut;
 #endif
@@ -93,6 +95,7 @@
 VIZ_COMMON_EXPORT bool IsSurfaceSyncThrottling();
 VIZ_COMMON_EXPORT absl::optional<double> IsDynamicSchedulerEnabledForDraw();
 VIZ_COMMON_EXPORT absl::optional<double> IsDynamicSchedulerEnabledForClients();
+VIZ_COMMON_EXPORT int MaxOverlaysConsidered();
 
 }  // namespace features
 
diff --git a/components/viz/service/display/overlay_processor_using_strategy.cc b/components/viz/service/display/overlay_processor_using_strategy.cc
index 69d6f6b..3904039 100644
--- a/components/viz/service/display/overlay_processor_using_strategy.cc
+++ b/components/viz/service/display/overlay_processor_using_strategy.cc
@@ -5,6 +5,7 @@
 #include "components/viz/service/display/overlay_processor_using_strategy.h"
 
 #include <algorithm>
+#include <set>
 #include <utility>
 #include <vector>
 
@@ -15,6 +16,8 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "components/viz/common/features.h"
+#include "components/viz/common/quads/aggregated_render_pass.h"
+#include "components/viz/common/quads/quad_list.h"
 #include "components/viz/common/quads/solid_color_draw_quad.h"
 #include "components/viz/service/debugger/viz_debugger.h"
 #include "components/viz/service/display/display_resource_provider.h"
@@ -22,6 +25,7 @@
 #include "components/viz/service/display/overlay_candidate.h"
 #include "components/viz/service/display/overlay_strategy_single_on_top.h"
 #include "components/viz/service/display/overlay_strategy_underlay.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/geometry/transform.h"
@@ -102,7 +106,7 @@
 }
 
 OverlayProcessorUsingStrategy::OverlayProcessorUsingStrategy()
-    : OverlayProcessorInterface() {}
+    : max_overlays_considered_(features::MaxOverlaysConsidered()) {}
 
 OverlayProcessorUsingStrategy::~OverlayProcessorUsingStrategy() = default;
 
@@ -484,6 +488,12 @@
 
   SortProposedOverlayCandidatesPrioritized(&proposed_candidates);
 
+  if (ShouldAttemptMultipleOverlays(proposed_candidates)) {
+    auto* render_pass = render_pass_list->back().get();
+    return AttemptMultipleOverlays(proposed_candidates, primary_plane,
+                                   render_pass, *candidates);
+  }
+
   for (auto&& candidate : proposed_candidates) {
     // Underlays change the material so we save it here to record proper UMA.
     DrawQuad::Material quad_material =
@@ -563,6 +573,160 @@
   return false;
 }
 
+bool OverlayProcessorUsingStrategy::ShouldAttemptMultipleOverlays(
+    const Strategy::OverlayProposedCandidateList& sorted_candidates) {
+  if (max_overlays_considered_ <= 1) {
+    return false;
+  }
+
+  for (auto& proposed : sorted_candidates) {
+    // When candidates that require overlays fail, they get retried with
+    // different scale factors. This becomes complicated when using multiple
+    // overlays at once so we won't attempt multiple in that case.
+    if (proposed.candidate.requires_overlay) {
+      return false;
+    }
+    // Using multiple overlays only makes sense with SingleOnTop and Underlay
+    // strategies.
+    OverlayStrategy type = proposed.strategy->GetUMAEnum();
+    if (type != OverlayStrategy::kSingleOnTop &&
+        type != OverlayStrategy::kUnderlay) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool OverlayProcessorUsingStrategy::AttemptMultipleOverlays(
+    const Strategy::OverlayProposedCandidateList& sorted_candidates,
+    OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane,
+    AggregatedRenderPass* render_pass,
+    OverlayCandidateList& candidates) {
+  int max_overlays_possible = std::min(
+      max_overlays_considered_, static_cast<int>(sorted_candidates.size()));
+
+  Strategy::OverlayProposedCandidateList test_candidates;
+  // Reserve max possible overlays so iterators remain stable while we insert
+  // candidates.
+  test_candidates.reserve(max_overlays_possible);
+  bool testing_underlay = false;
+  // We'll keep track of the underlays that we're testing so we can assign their
+  // `plane_z_order`s based on their order in the QuadList.
+  std::vector<Strategy::OverlayProposedCandidateList::iterator> underlay_iters;
+  // Used to prevent testing multiple candidates representing the same DrawQuad.
+  std::set<size_t> used_quad_indices;
+
+  for (auto& cand : sorted_candidates) {
+    // Skip candidates whose quads have already been added to the test list. A
+    // quad could have an on top and an underlay candidate.
+    bool inserted = used_quad_indices.insert(cand.quad_iter.index()).second;
+    if (!inserted) {
+      continue;
+    }
+    test_candidates.push_back(cand);
+
+    switch (cand.strategy->GetUMAEnum()) {
+      case OverlayStrategy::kSingleOnTop:
+        // Ordering of on top candidates doesn't matter (they can't overlap), so
+        // they can all have z = 1.
+        test_candidates.back().candidate.plane_z_order = 1;
+        break;
+      case OverlayStrategy::kUnderlay:
+        testing_underlay = true;
+        underlay_iters.push_back(test_candidates.end() - 1);
+        break;
+      default:
+        // Unsupported strategy type.
+        NOTREACHED();
+    }
+    if (test_candidates.size() == static_cast<size_t>(max_overlays_possible)) {
+      break;
+    }
+  }
+
+  // We don't sort the actual items in `test_candidates` here in order to
+  // maintain the power-gain sorted order.
+  AssignUnderlayZOrders(underlay_iters);
+
+  candidates.reserve(test_candidates.size());
+  for (auto& proposed_candidate : test_candidates) {
+    candidates.push_back(proposed_candidate.candidate);
+  }
+
+  if (!testing_underlay || !primary_plane) {
+    CheckOverlaySupport(primary_plane, &candidates);
+  } else {
+    Strategy::PrimaryPlane new_plane_candidate(*primary_plane);
+    new_plane_candidate.enable_blending = true;
+    // Check for support.
+    CheckOverlaySupport(&new_plane_candidate, &candidates);
+  }
+
+  bool underlay_used = false;
+  auto cand_it = candidates.begin();
+  auto test_it = test_candidates.begin();
+  while (cand_it != candidates.end()) {
+    // Update the test candidates so we can use EraseIf below.
+    test_it->candidate.overlay_handled = cand_it->overlay_handled;
+    if (cand_it->overlay_handled && cand_it->plane_z_order < 0) {
+      underlay_used = true;
+    }
+    cand_it++;
+    test_it++;
+  }
+  // Remove failed candidates
+  base::EraseIf(candidates, [](auto& cand) { return !cand.overlay_handled; });
+  base::EraseIf(test_candidates, [](auto& proposed) -> bool {
+    return !proposed.candidate.overlay_handled;
+  });
+
+  if (candidates.empty()) {
+    return false;
+  }
+
+  if (underlay_used && primary_plane) {
+    // Using underlays means the primary plane needs blending enabled.
+    primary_plane->enable_blending = true;
+  }
+
+  // Sort test candidates in reverse order so we can commit them from back to
+  // front. This makes sure none of the quad iterators are invalidated when some
+  // are removed from the QuadList as they're committed.
+  //
+  // TODO(khaslett): Remove this hacky workaround. Instead of erasing quads we
+  // could probably replace them with solid colour quads or make them invisible
+  // instead.
+  std::sort(test_candidates.begin(), test_candidates.end(),
+            [](const Strategy::OverlayProposedCandidate& c1,
+               const Strategy::OverlayProposedCandidate& c2) -> bool {
+              return c1.quad_iter.index() > c2.quad_iter.index();
+            });
+  // Commit successful candidates.
+  for (auto& test_candidate : test_candidates) {
+    test_candidate.strategy->CommitCandidate(test_candidate, render_pass);
+  }
+
+  return true;
+}
+
+void OverlayProcessorUsingStrategy::AssignUnderlayZOrders(
+    std::vector<Strategy::OverlayProposedCandidateList::iterator>&
+        underlay_iters) {
+  // Sort the underlay iterators by DrawQuad order, frontmost first.
+  std::sort(
+      underlay_iters.begin(), underlay_iters.end(),
+      [](const Strategy::OverlayProposedCandidateList::iterator& c1,
+         const Strategy::OverlayProposedCandidateList::iterator& c2) -> bool {
+        return c1->quad_iter.index() < c2->quad_iter.index();
+      });
+  // Assign underlay candidate plane_z_orders based on DrawQuad order.
+  int underlay_z_order = -1;
+  for (auto& it : underlay_iters) {
+    it->candidate.plane_z_order = underlay_z_order--;
+  }
+}
+
 gfx::Rect OverlayProcessorUsingStrategy::GetOverlayDamageRectForOutputSurface(
     const OverlayCandidate& overlay) const {
   return ToEnclosedRect(overlay.display_rect);
diff --git a/components/viz/service/display/overlay_processor_using_strategy.h b/components/viz/service/display/overlay_processor_using_strategy.h
index 51bf6a94..feefed8 100644
--- a/components/viz/service/display/overlay_processor_using_strategy.h
+++ b/components/viz/service/display/overlay_processor_using_strategy.h
@@ -47,6 +47,8 @@
 
       // A iterator in the vector of quads.
       QuadList::Iterator quad_iter;
+      // This is needed to sort candidates based on DrawQuad order.
+      size_t quad_index;
       OverlayCandidate candidate;
       raw_ptr<Strategy> strategy = nullptr;
 
@@ -251,6 +253,33 @@
       std::vector<gfx::Rect>* content_bounds,
       gfx::Rect* incoming_damage);
 
+  // Determines if we should attempt multiple overlays. This is based on
+  // `max_overlays_considered_`, the strategies proposed, and if any of the
+  // candidates require an overlay.
+  bool ShouldAttemptMultipleOverlays(
+      const Strategy::OverlayProposedCandidateList& sorted_candidates);
+
+  // Attempts to promote multiple candidates to overlays. Returns a boolean
+  // indicating if any of the attempted candidates were successfully promoted to
+  // overlays.
+  //
+  // TODO(khaslett): Write unit tests for this function before launching
+  // UseMultipleOverlays feature.
+  bool AttemptMultipleOverlays(
+      const Strategy::OverlayProposedCandidateList& sorted_candidates,
+      OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane,
+      AggregatedRenderPass* render_pass,
+      OverlayCandidateList& candidates);
+
+  // Assigns `plane_z_order`s to the proposed underlay candidates based on their
+  // DrawQuad orderings.
+  //
+  // TODO(khaslett): Write unit tests for this function before launching
+  // UseMultipleOverlays feature.
+  void AssignUnderlayZOrders(
+      std::vector<Strategy::OverlayProposedCandidateList::iterator>&
+          underlay_iters);
+
   // This function reorders and removes |proposed_candidates| based on a
   // heuristic designed to maximize the effectiveness of the limited number
   // of Hardware overlays. Effectiveness here is primarily about power and
@@ -288,6 +317,8 @@
   static ProposedCandidateKey ToProposeKey(
       const Strategy::OverlayProposedCandidate& proposed);
 
+  const int max_overlays_considered_;
+
   std::unordered_map<ProposedCandidateKey,
                      OverlayCandidateTemporalTracker,
                      ProposedCandidateKeyHasher>
diff --git a/components/viz/service/display/overlay_strategy_single_on_top.cc b/components/viz/service/display/overlay_strategy_single_on_top.cc
index ec16948..71b7dfa 100644
--- a/components/viz/service/display/overlay_strategy_single_on_top.cc
+++ b/components/viz/service/display/overlay_strategy_single_on_top.cc
@@ -92,8 +92,6 @@
   auto* render_pass = render_pass_list->back().get();
   QuadList* quad_list = &render_pass->quad_list;
   // Build a list of candidates with the associated quad.
-  OverlayCandidate best_candidate;
-  auto best_quad_it = quad_list->end();
   for (auto it = quad_list->begin(); it != quad_list->end(); ++it) {
     OverlayCandidate candidate;
     if (OverlayCandidate::FromDrawQuad(
diff --git a/components/viz/service/surfaces/referenced_surface_tracker.cc b/components/viz/service/surfaces/referenced_surface_tracker.cc
index e9498de..b451cca 100644
--- a/components/viz/service/surfaces/referenced_surface_tracker.cc
+++ b/components/viz/service/surfaces/referenced_surface_tracker.cc
@@ -16,21 +16,29 @@
     std::vector<SurfaceReference>* references_to_remove) {
   DCHECK(parent_surface_id.is_valid());
 
-  // Find SurfaceIds in |old_referenced_surfaces| that aren't referenced
-  // anymore.
-  for (const SurfaceId& surface_id : old_referenced_surfaces) {
-    if (new_referenced_surfaces.count(surface_id) == 0) {
-      references_to_remove->push_back(
-          SurfaceReference(parent_surface_id, surface_id));
-    }
-  }
+  auto old_it = old_referenced_surfaces.begin();
+  auto old_end = old_referenced_surfaces.end();
+  auto new_it = new_referenced_surfaces.begin();
+  auto new_end = new_referenced_surfaces.end();
 
-  // Find SurfaceIds in |new_referenced_surfaces| that aren't already
-  // referenced.
-  for (const SurfaceId& surface_id : new_referenced_surfaces) {
-    if (old_referenced_surfaces.count(surface_id) == 0) {
+  // Do a linear walk through both old and new references to compute added and
+  // removed entries.
+  while (old_it != old_end || new_it != new_end) {
+    if (old_it == old_end) {
       references_to_add->push_back(
-          SurfaceReference(parent_surface_id, surface_id));
+          SurfaceReference(parent_surface_id, *new_it++));
+    } else if (new_it == new_end) {
+      references_to_remove->push_back(
+          SurfaceReference(parent_surface_id, *old_it++));
+    } else if (*old_it < *new_it) {
+      references_to_remove->push_back(
+          SurfaceReference(parent_surface_id, *old_it++));
+    } else if (*new_it < *old_it) {
+      references_to_add->push_back(
+          SurfaceReference(parent_surface_id, *new_it++));
+    } else {
+      ++new_it;
+      ++old_it;
     }
   }
 }
diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc
index 96437239..547e500 100644
--- a/components/viz/service/surfaces/surface.cc
+++ b/components/viz/service/surfaces/surface.cc
@@ -151,60 +151,14 @@
 void Surface::OnChildActivatedForActiveFrame(const SurfaceId& activated_id) {
   DCHECK(HasActiveFrame());
 
-  for (size_t i = 0;
-       i < active_frame_data_->frame.metadata.referenced_surfaces.size(); i++) {
-    const SurfaceRange& surface_range =
-        active_frame_data_->frame.metadata.referenced_surfaces[i];
-    if (!surface_range.IsInRangeInclusive(activated_id))
-      continue;
-
-    const SurfaceId& last_id = last_surface_id_for_range_[i];
-    // If we already have a reference to a surface in the primary's allocation
-    // group, we should already be unregistered from the allocation group of the
-    // fallback so we shouldn't receive SurfaceIds from that group.
-    // TODO(crbug.com/1264657): This DCHECK is failing frequently on Chrome OS
-    // for valid use cases where there are multiple references in
-    // |referenced_surfaces| that contain |activated_id|. Temporary disable to
-    // avoid flake while investigating solutions.
-    // DCHECK(!surface_range.HasDifferentEmbedTokens() || !last_id.is_valid() ||
-    //       !last_id.HasSameEmbedTokenAs(surface_range.end()) ||
-    //       activated_id.HasSameEmbedTokenAs(last_id));
-
-    // Remove the old reference.
-    if (last_id.is_valid()) {
-      auto old_it = active_referenced_surfaces_.find(last_id);
-      if (old_it != active_referenced_surfaces_.end())
-        active_referenced_surfaces_.erase(old_it);
-      surface_manager_->RemoveSurfaceReferences(
-          {SurfaceReference(surface_info_.id(), last_id)});
+  for (auto& surface_range : GetActiveFrame().metadata.referenced_surfaces) {
+    if (surface_range.IsInRangeInclusive(activated_id)) {
+      // If |activated_id| is included in any of the surface reference then
+      // recompute the active surface references. This must handle the case
+      // where a SurfaceId is included in multiple surface ranges.
+      RecomputeActiveReferencedSurfaces();
+      return;
     }
-
-    // Add a new reference.
-    active_referenced_surfaces_.insert(activated_id);
-    surface_manager_->AddSurfaceReferences(
-        {SurfaceReference(surface_info_.id(), activated_id)});
-
-    // If we were referencing a surface in the allocation group of the
-    // fallback, but now there is a surface available in the allocation group
-    // of the primary, unregister this surface from the allocation group of
-    // the fallback.
-    if (activated_id.HasSameEmbedTokenAs(surface_range.end()) &&
-        surface_range.HasDifferentEmbedTokens() &&
-        (!last_id.is_valid() || !last_id.HasSameEmbedTokenAs(activated_id))) {
-      DCHECK(surface_range.start());
-      DCHECK(!last_id.is_valid() ||
-             last_id.HasSameEmbedTokenAs(*surface_range.start()));
-      SurfaceAllocationGroup* group =
-          surface_manager_->GetAllocationGroupForSurfaceId(
-              *surface_range.start());
-      if (group && referenced_allocation_groups_.count(group)) {
-        group->UnregisterActiveEmbedder(this);
-        referenced_allocation_groups_.erase(group);
-      }
-    }
-
-    // Update the referenced surface for this range.
-    last_surface_id_for_range_[i] = activated_id;
   }
 }
 
diff --git a/components/viz/service/surfaces/surface_unittest.cc b/components/viz/service/surfaces/surface_unittest.cc
index 91a237f..09acc0fe 100644
--- a/components/viz/service/surfaces/surface_unittest.cc
+++ b/components/viz/service/surfaces/surface_unittest.cc
@@ -19,6 +19,8 @@
 #include "components/viz/test/compositor_frame_helpers.h"
 #include "components/viz/test/fake_external_begin_frame_source.h"
 #include "components/viz/test/mock_compositor_frame_sink_client.h"
+#include "components/viz/test/test_surface_id_allocator.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -28,17 +30,25 @@
 constexpr FrameSinkId kArbitraryFrameSinkId(1, 1);
 constexpr bool kIsRoot = true;
 
-TEST(SurfaceTest, PresentationCallback) {
+class SurfaceTest : public testing::Test {
+ public:
+  SurfaceTest()
+      : frame_sink_manager_(
+            FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_)) {}
+
+ protected:
+  ServerSharedBitmapManager shared_bitmap_manager_;
+  FrameSinkManagerImpl frame_sink_manager_;
+};
+
+TEST_F(SurfaceTest, PresentationCallback) {
   constexpr gfx::Size kSurfaceSize(300, 300);
   constexpr gfx::Rect kDamageRect(0, 0);
   const LocalSurfaceId local_surface_id(6, base::UnguessableToken::Create());
 
-  ServerSharedBitmapManager shared_bitmap_manager;
-  FrameSinkManagerImpl frame_sink_manager{
-      FrameSinkManagerImpl::InitParams(&shared_bitmap_manager)};
   MockCompositorFrameSinkClient client;
   auto support = std::make_unique<CompositorFrameSinkSupport>(
-      &client, &frame_sink_manager, kArbitraryFrameSinkId, kIsRoot);
+      &client, &frame_sink_manager_, kArbitraryFrameSinkId, kIsRoot);
   uint32_t frame_token = 0;
   {
     CompositorFrame frame =
@@ -67,7 +77,7 @@
   }
 }
 
-TEST(SurfaceTest, SurfaceIds) {
+TEST_F(SurfaceTest, SurfaceIds) {
   for (size_t i = 0; i < 3; ++i) {
     ParentLocalSurfaceIdAllocator allocator;
     allocator.GenerateId();
@@ -87,13 +97,10 @@
 
 // Test that CopyOutputRequests can outlive the current frame and be
 // aggregated on the next frame.
-TEST(SurfaceTest, CopyRequestLifetime) {
-  ServerSharedBitmapManager shared_bitmap_manager;
-  FrameSinkManagerImpl frame_sink_manager{
-      FrameSinkManagerImpl::InitParams(&shared_bitmap_manager)};
-  SurfaceManager* surface_manager = frame_sink_manager.surface_manager();
+TEST_F(SurfaceTest, CopyRequestLifetime) {
+  SurfaceManager* surface_manager = frame_sink_manager_.surface_manager();
   auto support = std::make_unique<CompositorFrameSinkSupport>(
-      nullptr, &frame_sink_manager, kArbitraryFrameSinkId, kIsRoot);
+      nullptr, &frame_sink_manager_, kArbitraryFrameSinkId, kIsRoot);
 
   LocalSurfaceId local_surface_id(6, base::UnguessableToken::Create());
   SurfaceId surface_id(kArbitraryFrameSinkId, local_surface_id);
@@ -150,5 +157,103 @@
   EXPECT_TRUE(copy_called);
 }
 
+// Verify activate referenced surfaces is correct when there are two surface
+// references to overlapping surface ranges. In particular the two surface
+// ranges are (S1, S1) and (S1, S2). When both S1 and S2 activate active
+// referenced surfaces should include both S1 and S2. See
+// https://crbug.com/1275605 for more context.
+TEST_F(SurfaceTest, ActiveSurfaceReferencesWithOverlappingReferences) {
+  constexpr gfx::Rect output_rect(100, 100);
+  SurfaceManager* surface_manager = frame_sink_manager_.surface_manager();
+
+  auto root_support = std::make_unique<CompositorFrameSinkSupport>(
+      nullptr, &frame_sink_manager_, kArbitraryFrameSinkId,
+      /*is_root=*/true);
+  TestSurfaceIdAllocator root_surface_id(kArbitraryFrameSinkId);
+
+  auto child_support1 = std::make_unique<CompositorFrameSinkSupport>(
+      nullptr, &frame_sink_manager_, FrameSinkId(2, 1), /*is_root=*/false);
+  TestSurfaceIdAllocator child_surface_id1(child_support1->frame_sink_id());
+
+  auto child_support2 = std::make_unique<CompositorFrameSinkSupport>(
+      nullptr, &frame_sink_manager_, FrameSinkId(3, 1), /*is_root=*/false);
+  TestSurfaceIdAllocator child_surface_id2(child_support2->frame_sink_id());
+
+  // Submit a root frame with two SurfaceDrawQuads. The first SurfaceDrawQuad
+  // embeds |child_support1|. The second SurfaceDrawQuad embeds |child_support2|
+  // but has |child_support1| as a fallback which mimics a navigating renderer.
+  // Note that |old_surface_range| and |navigation_surface_range| overlap.
+  SurfaceRange old_surface_range(child_surface_id1, child_surface_id1);
+  SurfaceRange navigation_surface_range(child_surface_id1, child_surface_id2);
+  auto root_render_pass =
+      RenderPassBuilder(CompositorRenderPassId{1}, output_rect)
+          .AddSurfaceQuad(output_rect, old_surface_range)
+          .AddSurfaceQuad(output_rect, navigation_surface_range)
+          .Build();
+
+  {
+    CompositorFrame frame = MakeCompositorFrame(root_render_pass->DeepCopy());
+    EXPECT_THAT(
+        frame.metadata.referenced_surfaces,
+        testing::ElementsAre(old_surface_range, navigation_surface_range));
+
+    root_support->SubmitCompositorFrame(root_surface_id.local_surface_id(),
+                                        std::move(frame));
+  }
+
+  Surface* root_surface = surface_manager->GetSurfaceForId(root_surface_id);
+  ASSERT_TRUE(root_surface);
+
+  // No active references yet because no child surfaces have been submitted yet.
+  EXPECT_THAT(root_surface->active_referenced_surfaces(), testing::IsEmpty());
+
+  // Submit something to the second child surface and verify it's now included
+  // in active referenced surfaces.
+  child_support2->SubmitCompositorFrame(child_surface_id2.local_surface_id(),
+                                        MakeDefaultCompositorFrame());
+  EXPECT_TRUE(surface_manager->GetSurfaceForId(child_surface_id2));
+  EXPECT_THAT(root_surface->active_referenced_surfaces(),
+              testing::ElementsAre(child_surface_id2));
+
+  // Submit something to the first child surface and verify both are in active
+  // surface references. Note this order of activation is "backwards" as
+  // normally the |child_surface_id1| would have activated first if the browser
+  // is navigating away from it but if the first renderer is slow to produce
+  // content the order can be reversed.
+  child_support1->SubmitCompositorFrame(child_surface_id1.local_surface_id(),
+                                        MakeDefaultCompositorFrame());
+  EXPECT_TRUE(surface_manager->GetSurfaceForId(child_surface_id1));
+  EXPECT_THAT(root_surface->active_referenced_surfaces(),
+              testing::ElementsAre(child_surface_id1, child_surface_id2));
+
+  // Resubmit root frame with the same SurfaceDrawQuads and verify active
+  // surface references are unchanged.
+  root_support->SubmitCompositorFrame(
+      root_surface_id.local_surface_id(),
+      MakeCompositorFrame(std::move(root_render_pass)));
+  EXPECT_THAT(root_surface->active_referenced_surfaces(),
+              testing::ElementsAre(child_surface_id1, child_surface_id2));
+
+  // Submit a new root frame without the reference to the first child surface
+  // and verify |child_surface_id1| is no longer part of active referenced
+  // surfaces.
+  {
+    SurfaceRange post_navigation_surface_range(child_surface_id2,
+                                               child_surface_id2);
+    CompositorFrame frame =
+        CompositorFrameBuilder()
+            .AddRenderPass(
+                RenderPassBuilder(CompositorRenderPassId{1}, output_rect)
+                    .AddSurfaceQuad(output_rect, post_navigation_surface_range))
+            .Build();
+
+    root_support->SubmitCompositorFrame(root_surface_id.local_surface_id(),
+                                        std::move(frame));
+  }
+
+  EXPECT_THAT(root_surface->active_referenced_surfaces(),
+              testing::ElementsAre(child_surface_id2));
+}
+
 }  // namespace
 }  // namespace viz
diff --git a/content/browser/locks/lock_manager.cc b/content/browser/locks/lock_manager.cc
index 4dfe4ce1..7c544f0 100644
--- a/content/browser/locks/lock_manager.cc
+++ b/content/browser/locks/lock_manager.cc
@@ -40,25 +40,26 @@
  public:
   static mojo::SelfOwnedAssociatedReceiverRef<blink::mojom::LockHandle> Create(
       base::WeakPtr<LockManager> context,
-      const url::Origin& origin,
+      storage::BucketId bucket_id,
       int64_t lock_id,
       mojo::PendingAssociatedRemote<blink::mojom::LockHandle>* remote) {
     return mojo::MakeSelfOwnedAssociatedReceiver(
-        std::make_unique<LockHandleImpl>(std::move(context), origin, lock_id),
+        std::make_unique<LockHandleImpl>(std::move(context), bucket_id,
+                                         lock_id),
         remote->InitWithNewEndpointAndPassReceiver());
   }
 
   LockHandleImpl(base::WeakPtr<LockManager> context,
-                 const url::Origin& origin,
+                 storage::BucketId bucket_id,
                  int64_t lock_id)
-      : context_(context), origin_(origin), lock_id_(lock_id) {}
+      : context_(context), bucket_id_(bucket_id), lock_id_(lock_id) {}
 
   LockHandleImpl(const LockHandleImpl&) = delete;
   LockHandleImpl& operator=(const LockHandleImpl&) = delete;
 
   ~LockHandleImpl() override {
     if (context_)
-      context_->ReleaseLock(origin_, lock_id_);
+      context_->ReleaseLock(bucket_id_, lock_id_);
   }
 
   // Called when the handle will be released from this end of the pipe. It
@@ -67,7 +68,7 @@
 
  private:
   base::WeakPtr<LockManager> context_;
-  const url::Origin origin_;
+  const storage::BucketId bucket_id_;
   const int64_t lock_id_;
 };
 
@@ -91,7 +92,7 @@
 
   // Grant a lock request. This mints a LockHandle and returns it over the
   // request pipe.
-  void Grant(LockManager* lock_manager, const url::Origin& origin) {
+  void Grant(LockManager* lock_manager, storage::BucketId bucket_id) {
     DCHECK(lock_manager);
     DCHECK(!lock_manager_);
     DCHECK(request_);
@@ -100,7 +101,8 @@
     lock_manager_ = lock_manager->weak_ptr_factory_.GetWeakPtr();
 
     mojo::PendingAssociatedRemote<blink::mojom::LockHandle> remote;
-    handle_ = LockHandleImpl::Create(lock_manager_, origin, lock_id_, &remote);
+    handle_ =
+        LockHandleImpl::Create(lock_manager_, bucket_id, lock_id_, &remote);
     request_->Granted(std::move(remote));
     request_.reset();
   }
@@ -148,13 +150,13 @@
 
 LockManager::~LockManager() = default;
 
-// The OriginState class manages and exposes the state of lock requests
-// for a given origin.
-class LockManager::OriginState {
+// The BucketState class manages and exposes the state of lock requests
+// for a given bucket.
+class LockManager::BucketState {
  public:
-  explicit OriginState(LockManager* lock_manager)
+  explicit BucketState(LockManager* lock_manager)
       : lock_manager_(lock_manager) {}
-  ~OriginState() = default;
+  ~BucketState() = default;
 
   // Helper function for breaking the lock at the front of a given request
   // queue.
@@ -183,7 +185,7 @@
                                 std::move(request));
     auto it = request_queue.begin();
     lock_id_to_iterator_.emplace(it->lock_id(), it);
-    it->Grant(lock_manager_, receiver_state.origin);
+    it->Grant(lock_manager_, receiver_state.bucket_id);
   }
 
   void AddRequest(int64_t lock_id,
@@ -209,11 +211,11 @@
     auto it = --(request_queue.end());
     lock_id_to_iterator_.emplace(it->lock_id(), it);
     if (can_grant) {
-      it->Grant(lock_manager_, receiver_state.origin);
+      it->Grant(lock_manager_, receiver_state.bucket_id);
     }
   }
 
-  void EraseLock(int64_t lock_id, const url::Origin& origin) {
+  void EraseLock(int64_t lock_id, storage::BucketId bucket_id) {
     // Note - the two lookups here could be replaced with one if the
     // lock_id_to_iterator_ map also stored a reference to the request queue.
     auto iterator_it = lock_id_to_iterator_.find(lock_id);
@@ -254,7 +256,7 @@
       return;
 
     if (request_queue.front().mode() == LockMode::EXCLUSIVE) {
-      request_queue.front().Grant(lock_manager_, origin);
+      request_queue.front().Grant(lock_manager_, bucket_id);
     } else {
       DCHECK(request_queue.front().mode() == LockMode::SHARED);
       for (auto grantee = request_queue.begin();
@@ -262,7 +264,7 @@
            grantee->mode() == LockMode::SHARED;
            ++grantee) {
         DCHECK(!grantee->is_granted());
-        grantee->Grant(lock_manager_, origin);
+        grantee->Grant(lock_manager_, bucket_id);
       }
     }
   }
@@ -289,8 +291,8 @@
   }
 
  private:
-  // OriginState::resource_names_to_requests_ maps a resource name to
-  // that resource's associated request queue for a given origin.
+  // BucketState::resource_names_to_requests_ maps a resource name to
+  // that resource's associated request queue for a given bucket.
   //
   // A resource's request queue is a list of Lock objects representing lock
   // requests against that resource. All the granted locks for a resource reside
@@ -298,7 +300,7 @@
   // request queue.
   std::unordered_map<std::string, std::list<Lock>> resource_names_to_requests_;
 
-  // OriginState::lock_id_to_iterator_ maps a lock's id to the
+  // BucketState::lock_id_to_iterator_ maps a lock's id to the
   // iterator pointing to its location in its associated request queue.
   std::unordered_map<int64_t, std::list<Lock>::iterator> lock_id_to_iterator_;
 
@@ -307,10 +309,17 @@
   const raw_ptr<LockManager> lock_manager_;
 };
 
+LockManager::ReceiverState::ReceiverState(std::string client_id,
+                                          storage::BucketId bucket_id)
+    : client_id(std::move(client_id)), bucket_id(bucket_id) {}
+LockManager::ReceiverState::ReceiverState() = default;
+LockManager::ReceiverState::ReceiverState(const ReceiverState& other) = default;
+LockManager::ReceiverState::~ReceiverState() = default;
+
 void LockManager::BindReceiver(
     int render_process_id,
     int render_frame_id,
-    const url::Origin& origin,
+    storage::BucketId bucket_id,
     mojo::PendingReceiver<blink::mojom::LockManager> receiver) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -318,7 +327,7 @@
   // and be the same opaque string seen in Service Worker client ids.
   const std::string client_id = base::GenerateGUID();
 
-  receivers_.Add(this, std::move(receiver), {client_id, origin});
+  receivers_.Add(this, std::move(receiver), {client_id, bucket_id});
 }
 
 void LockManager::RequestLock(
@@ -338,52 +347,56 @@
     return;
   }
 
-  const auto& context = receivers_.current_context();
-
-  if (!base::Contains(origins_, context.origin))
-    origins_.emplace(context.origin, this);
-
-  int64_t lock_id = NextLockId();
-
   mojo::AssociatedRemote<blink::mojom::LockRequest> request(
       std::move(request_remote));
+  const auto& context = receivers_.current_context();
+  if (context.bucket_id.is_null()) {
+    request->Failed();
+    return;
+  }
+
+  if (!base::Contains(buckets_, context.bucket_id))
+    buckets_.emplace(context.bucket_id, this);
+
+  int64_t lock_id = NextLockId();
   request.set_disconnect_handler(base::BindOnce(&LockManager::ReleaseLock,
                                                 base::Unretained(this),
-                                                context.origin, lock_id));
+                                                context.bucket_id, lock_id));
 
-  OriginState& origin_state = origins_.find(context.origin)->second;
+  BucketState& bucket_state = buckets_.find(context.bucket_id)->second;
   if (wait == WaitMode::PREEMPT) {
-    origin_state.PreemptLock(lock_id, name, mode, std::move(request), context);
+    bucket_state.PreemptLock(lock_id, name, mode, std::move(request), context);
   } else
-    origin_state.AddRequest(lock_id, name, mode, std::move(request), wait,
+    bucket_state.AddRequest(lock_id, name, mode, std::move(request), wait,
                             context);
 }
 
-void LockManager::ReleaseLock(const url::Origin& origin, int64_t lock_id) {
+void LockManager::ReleaseLock(storage::BucketId bucket_id, int64_t lock_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  auto origin_it = origins_.find(origin);
-  if (origin_it == origins_.end())
+  auto bucket_id_it = buckets_.find(bucket_id);
+  if (bucket_id_it == buckets_.end())
     return;
 
-  OriginState& state = origin_it->second;
-  state.EraseLock(lock_id, origin);
+  BucketState& state = bucket_id_it->second;
+  state.EraseLock(lock_id, bucket_id);
   if (state.IsEmpty())
-    origins_.erase(origin);
+    buckets_.erase(bucket_id);
 }
 
 void LockManager::QueryState(QueryStateCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  const url::Origin& origin = receivers_.current_context().origin;
-  auto origin_it = origins_.find(origin);
-  if (origin_it == origins_.end()) {
+  storage::BucketId bucket_id = receivers_.current_context().bucket_id;
+
+  auto bucket_id_it = buckets_.find(bucket_id);
+  if (bucket_id_it == buckets_.end()) {
     std::move(callback).Run(std::vector<blink::mojom::LockInfoPtr>(),
                             std::vector<blink::mojom::LockInfoPtr>());
     return;
   }
-
-  OriginState& state = origin_it->second;
+  DCHECK(!bucket_id.is_null());
+  BucketState& state = bucket_id_it->second;
   auto requested_held_pair = state.Snapshot();
   std::move(callback).Run(std::move(requested_held_pair.first),
                           std::move(requested_held_pair.second));
diff --git a/content/browser/locks/lock_manager.h b/content/browser/locks/lock_manager.h
index 36f2d28..20b857e 100644
--- a/content/browser/locks/lock_manager.h
+++ b/content/browser/locks/lock_manager.h
@@ -13,6 +13,9 @@
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
+#include "components/services/storage/public/cpp/buckets/bucket_id.h"
+#include "components/services/storage/public/cpp/buckets/constants.h"
+#include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "third_party/blink/public/mojom/locks/lock_manager.mojom.h"
 #include "url/origin.h"
@@ -22,7 +25,7 @@
 // One instance of this exists per StoragePartition, and services multiple
 // child processes/origins. An instance must only be used on the sequence
 // it was created on.
-class LockManager : public blink::mojom::LockManager {
+class CONTENT_EXPORT LockManager : public blink::mojom::LockManager {
  public:
   LockManager();
   ~LockManager() override;
@@ -31,11 +34,11 @@
   LockManager& operator=(const LockManager&) = delete;
 
   // Binds |receiver| to this LockManager. |receiver| belongs to a frame or
-  // worker at |origin| hosted by |render_process_id|. If it belongs to a frame,
-  // |render_frame_id| identifies it, otherwise it is MSG_ROUTING_NONE.
+  // worker at |bucket_id| hosted by |render_process_id|. If it belongs to a
+  // frame, |render_frame_id| identifies it, otherwise it is MSG_ROUTING_NONE.
   void BindReceiver(int render_process_id,
                     int render_frame_id,
-                    const url::Origin& origin,
+                    storage::BucketId bucket_id,
                     mojo::PendingReceiver<blink::mojom::LockManager> receiver);
 
   // Request a lock. When the lock is acquired, |callback| will be invoked with
@@ -47,24 +50,29 @@
                        request) override;
 
   // Called by a LockHandle's implementation when destructed.
-  void ReleaseLock(const url::Origin& origin, int64_t lock_id);
+  void ReleaseLock(storage::BucketId bucket_id, int64_t lock_id);
 
-  // Called to request a snapshot of the current lock state for an origin.
+  // Called to request a snapshot of the current lock state for a bucket.
   void QueryState(QueryStateCallback callback) override;
 
  private:
   // Internal representation of a lock request or held lock.
   class Lock;
 
-  // State for a particular origin.
-  class OriginState;
+  // State for a particular bucket.
+  class BucketState;
 
   // State for each client held in |receivers_|.
   struct ReceiverState {
+    ReceiverState(std::string client_id, storage::BucketId bucket_id);
+    ReceiverState();
+    ReceiverState(const ReceiverState& other);
+    ~ReceiverState();
+
     std::string client_id;
 
-    // Origin of the frame or worker owning this receiver.
-    url::Origin origin;
+    // BucketId of the frame or worker owning this receiver.
+    storage::BucketId bucket_id;
   };
 
   // Mints a monotonically increasing identifier. Used both for lock requests
@@ -74,7 +82,7 @@
   mojo::ReceiverSet<blink::mojom::LockManager, ReceiverState> receivers_;
 
   int64_t next_lock_id_ = 0;
-  std::map<url::Origin, OriginState> origins_;
+  std::map<storage::BucketId, BucketState> buckets_;
 
   SEQUENCE_CHECKER(sequence_checker_);
   base::WeakPtrFactory<LockManager> weak_ptr_factory_{this};
diff --git a/content/browser/locks/lock_manager_unittest.cc b/content/browser/locks/lock_manager_unittest.cc
new file mode 100644
index 0000000..1492566
--- /dev/null
+++ b/content/browser/locks/lock_manager_unittest.cc
@@ -0,0 +1,113 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/locks/lock_manager.h"
+
+#include <vector>
+
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+class LockManagerInvalidBucketTest : public testing::Test {
+ public:
+  void SetUp() override {
+    pending_receiver_ = pending_remote_.InitWithNewPipeAndPassReceiver();
+
+    int render_process_id = 2;
+    int render_frame_id = 5;
+    storage::BucketId bucket_id = storage::BucketId();  // Invalid BucketId.
+
+    lock_manager_.BindReceiver(render_process_id, render_frame_id, bucket_id,
+                               std::move(pending_receiver_));
+
+    remote_.Bind(std::move(pending_remote_));
+  }
+
+  mojo::Remote<blink::mojom::LockManager>& GetRemote() { return remote_; }
+
+ private:
+  base::test::SingleThreadTaskEnvironment task_environment_;
+  LockManager lock_manager_;
+  mojo::PendingRemote<blink::mojom::LockManager> pending_remote_;
+  mojo::PendingReceiver<blink::mojom::LockManager> pending_receiver_;
+  mojo::Remote<blink::mojom::LockManager> remote_;
+};
+
+class TestLockRequest : public blink::mojom::LockRequest {
+ public:
+  explicit TestLockRequest(
+      mojo::PendingAssociatedReceiver<blink::mojom::LockRequest>
+          pending_receiver)
+      : receiver_(this, std::move(pending_receiver)) {}
+
+  void Granted(mojo::PendingAssociatedRemote<blink::mojom::LockHandle>
+                   lock_handle) override {
+    remote_ = &lock_handle;
+    granted_ = true;
+    run_loop_.Quit();
+  }
+
+  void Failed() override {
+    failed_ = true;
+    run_loop_.Quit();
+  }
+
+  void Abort(const std::string& reason) override {
+    aborted_ = true;
+    run_loop_.Quit();
+    return;
+  }
+
+  void WaitForCallback() { run_loop_.Run(); }
+
+  bool FailureCalled() const { return failed_; }
+  bool GrantedCalled() const { return granted_; }
+  bool AbortCalled() const { return aborted_; }
+
+ private:
+  mojo::PendingAssociatedRemote<blink::mojom::LockHandle>* remote_;
+  mojo::AssociatedReceiver<blink::mojom::LockRequest> receiver_;
+  base::RunLoop run_loop_;
+  bool failed_ = false;
+  bool granted_ = false;
+  bool aborted_ = false;
+};
+
+TEST_F(LockManagerInvalidBucketTest, RequestLock) {
+  mojo::PendingAssociatedRemote<blink::mojom::LockRequest> pending_remote;
+  mojo::PendingAssociatedReceiver<blink::mojom::LockRequest> pending_receiver =
+      pending_remote.InitWithNewEndpointAndPassReceiver();
+  TestLockRequest request(std::move(pending_receiver));
+
+  GetRemote()->RequestLock("lock", blink::mojom::LockMode::EXCLUSIVE,
+                           LockManager::WaitMode::WAIT,
+                           std::move(pending_remote));
+
+  request.WaitForCallback();
+  EXPECT_TRUE(request.FailureCalled());
+  EXPECT_FALSE(request.GrantedCalled());
+  EXPECT_FALSE(request.AbortCalled());
+}
+
+TEST_F(LockManagerInvalidBucketTest, QueryState) {
+  base::RunLoop run_loop;
+
+  GetRemote()->QueryState(
+      base::BindOnce([](std::vector<blink::mojom::LockInfoPtr> first,
+                        std::vector<blink::mojom::LockInfoPtr> second) {
+        EXPECT_TRUE(first.empty());
+        EXPECT_TRUE(second.empty());
+      }).Then(run_loop.QuitClosure()));
+
+  run_loop.Run();
+}
+
+}  // namespace content
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index 35386deb..911998cb 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -328,6 +328,21 @@
   client_->OnFrameTokenChanged(frame_token, activation_time);
 }
 
+// CommitPending without a target for TakeFallbackContentFrom. Since we cannot
+// guarantee that Navigation will complete, evict our surfaces which are from
+// a previous Navigation.
+void DelegatedFrameHost::ClearFallbackSurfaceForCommitPending() {
+  const viz::SurfaceId* fallback_surface_id =
+      client_->DelegatedFrameHostGetLayer()->GetOldestAcceptableFallback();
+
+  // CommitPending failed, and Navigation never completed. Evict our surfaces.
+  if (fallback_surface_id && fallback_surface_id->is_valid()) {
+    EvictDelegatedFrame();
+    client_->DelegatedFrameHostGetLayer()->SetOldestAcceptableFallback(
+        viz::SurfaceId());
+  }
+}
+
 void DelegatedFrameHost::ResetFallbackToFirstNavigationSurface() {
   const viz::SurfaceId* fallback_surface_id =
       client_->DelegatedFrameHostGetLayer()->GetOldestAcceptableFallback();
diff --git a/content/browser/renderer_host/delegated_frame_host.h b/content/browser/renderer_host/delegated_frame_host.h
index ce90e29..3b5c749 100644
--- a/content/browser/renderer_host/delegated_frame_host.h
+++ b/content/browser/renderer_host/delegated_frame_host.h
@@ -96,6 +96,7 @@
   // ui::CompositorObserver implementation.
   void OnCompositingShuttingDown(ui::Compositor* compositor) override;
 
+  void ClearFallbackSurfaceForCommitPending();
   void ResetFallbackToFirstNavigationSurface();
 
   // viz::HostFrameSinkClient implementation.
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 9111a8a..5db6c33 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -1639,6 +1639,22 @@
     }
   }
 #endif
+
+  static constexpr base::Feature kNavigationRequestPreconnect{
+      "NavigationRequestPreconnect", base::FEATURE_DISABLED_BY_DEFAULT};
+  if (base::FeatureList::IsEnabled(kNavigationRequestPreconnect) &&
+      NeedsUrlLoader() && common_params_->url.SchemeIsHTTPOrHTTPS()) {
+    BrowserContext* browser_context =
+        frame_tree_node_->navigator().controller().GetBrowserContext();
+    if (GetContentClient()->browser()->ShouldPreconnectNavigation(
+            browser_context)) {
+      auto* storage_partition =
+          frame_tree_node_->current_frame_host()->GetStoragePartition();
+      storage_partition->GetNetworkContext()->PreconnectSockets(
+          1, common_params_->url, true,
+          GetIsolationInfo().network_isolation_key());
+    }
+  }
 }
 
 NavigationRequest::~NavigationRequest() {
diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc
index b8f0983..4864b53 100644
--- a/content/browser/renderer_host/render_frame_host_manager.cc
+++ b/content/browser/renderer_host/render_frame_host_manager.cc
@@ -3459,6 +3459,19 @@
     }
   }
 
+  // Notify that we have no `old_view` from which to TakeFallbackContentFrom.
+  // This will clear the current Fallback Surface, which would be from a
+  // previous Navigation. This way we do not display old content if this new
+  // PendingCommit does not lead to a successful Navigation. This must be called
+  // before NotifySwappedFromRenderManager, which will allocate a new
+  // viz::LocalSurfaceId, which will allow the Renderer to submit new content.
+  // TODO(crbug.com/1072817): Remove this once CommitPending has more explicit
+  // shutdown, both for successful and failed navigations.
+  if (!old_view) {
+    delegate_->NotifySwappedFromRenderManagerWithoutFallbackContent(
+        render_frame_host_.get());
+  }
+
   // Notify that we've swapped RenderFrameHosts. We do this before shutting down
   // the RFH so that we can clean up RendererResources related to the RFH first.
   delegate_->NotifySwappedFromRenderManager(old_render_frame_host.get(),
diff --git a/content/browser/renderer_host/render_frame_host_manager.h b/content/browser/renderer_host/render_frame_host_manager.h
index 23d9e05..e116184 100644
--- a/content/browser/renderer_host/render_frame_host_manager.h
+++ b/content/browser/renderer_host/render_frame_host_manager.h
@@ -142,6 +142,12 @@
     virtual void NotifySwappedFromRenderManager(
         RenderFrameHostImpl* old_frame,
         RenderFrameHostImpl* new_frame) = 0;
+    // Notifies that we are swapping to a `new_frame` when there is no
+    // `old_frame` available from which to take fallback content.
+    // TODO(crbug.com/1072817): Remove this once CommitPending has more explicit
+    // shutdown, both for successful and failed navigations.
+    virtual void NotifySwappedFromRenderManagerWithoutFallbackContent(
+        RenderFrameHostImpl* new_frame) = 0;
     // TODO(nasko): This should be removed once extensions no longer use
     // NotificationService. See https://crbug.com/462682.
     virtual void NotifyMainFrameSwappedFromRenderManager(
diff --git a/content/browser/renderer_host/render_frame_host_manager_unittest.cc b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
index 4bbf9e2..83623ec3 100644
--- a/content/browser/renderer_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
@@ -2056,6 +2056,92 @@
                          contents2->GetSiteInstance()->group()));
 }
 
+// Tests two WebContents from the same origin, where one is first navigated to
+// a different origin. This different origin experiences a Renderer crash.
+// However we then navigate that WebContents back to the old origin, which still
+// has an active Renderer.
+//
+// This test confirms that for this return navigation that we identified that
+// there is no FallbackSurface for the RenderWidgetHostView to display during
+// the navigation. (https://crbug.com/1258363)
+TEST_P(RenderFrameHostManagerTestWithSiteIsolation,
+       TwoTabsOneNavigatesAndCrashesThenNavigatesBack) {
+  const GURL kUrl1("http://www.google.com/");
+  const GURL kUrl2("http://webkit.org/");
+
+  // `contents1` and `contents2` navigate to the same page.
+  TestWebContents* contents1 = contents();
+  std::unique_ptr<TestWebContents> contents2(
+      TestWebContents::Create(browser_context(), contents1->GetSiteInstance()));
+  contents1->NavigateAndCommit(kUrl1);
+  contents2->NavigateAndCommit(kUrl1);
+  MockRenderProcessHost* rph = contents1->GetMainFrame()->GetProcess();
+  EXPECT_EQ(rph, contents2->GetMainFrame()->GetProcess());
+  EXPECT_TRUE(contents1->GetMainFrame()->GetView());
+  EXPECT_TRUE(contents2->GetMainFrame()->GetView());
+  EXPECT_TRUE(contents1->GetMainFrame()->IsRenderFrameLive());
+  EXPECT_TRUE(contents2->GetMainFrame()->IsRenderFrameLive());
+  EXPECT_EQ(contents1->GetSiteInstance(), contents2->GetSiteInstance());
+  TestRenderWidgetHostView* initial_view =
+      static_cast<TestRenderWidgetHostView*>(
+          contents2->GetMainFrame()->GetView());
+
+  // Navigate `content2` to a different page. This navigation should have a
+  // valid FallbackSurface for the RenderWidgetHostView to display.
+  contents2->NavigateAndCommit(kUrl2);
+  TestRenderWidgetHostView* post_nav_view =
+      static_cast<TestRenderWidgetHostView*>(
+          contents2->GetMainFrame()->GetView());
+  // Since this is a different origin we should also be using a different
+  // RenderWidgetHostView.
+  EXPECT_NE(initial_view, post_nav_view);
+  EXPECT_FALSE(
+      post_nav_view->clear_fallback_surface_for_commit_pending_called());
+  EXPECT_TRUE(post_nav_view->take_fallback_content_from_called());
+  post_nav_view->ClearFallbackSurfaceCalled();
+  EXPECT_TRUE(contents1->GetMainFrame()->IsRenderFrameLive());
+  EXPECT_TRUE(contents2->GetMainFrame()->IsRenderFrameLive());
+  EXPECT_NE(contents1->GetSiteInstance(), contents2->GetSiteInstance());
+  EXPECT_TRUE(contents1->GetMainFrame()->GetView());
+  EXPECT_TRUE(contents2->GetMainFrame()->GetView());
+
+  // Crash the Renderer of the tab that navigated.
+  MockRenderProcessHost* rph2 = contents2->GetMainFrame()->GetProcess();
+  EXPECT_NE(rph, rph2);
+  rph2->SimulateCrash();
+  EXPECT_TRUE(contents1->GetMainFrame()->IsRenderFrameLive());
+  EXPECT_FALSE(contents2->GetMainFrame()->IsRenderFrameLive());
+  EXPECT_NE(contents1->GetSiteInstance(), contents2->GetSiteInstance());
+  EXPECT_TRUE(contents1->GetMainFrame()->GetView());
+  EXPECT_FALSE(contents2->GetMainFrame()->GetView());
+
+  // Navigate `contents2` back to previous host, which still has an active
+  // Renderer. This should notify the RenderWidgetHostView that there is no
+  // new FallbackSurface to take, and that it should update it's currently
+  // cached one.
+  contents2->NavigateAndCommit(kUrl1);
+  TestRenderWidgetHostView* return_nav_view =
+      static_cast<TestRenderWidgetHostView*>(
+          contents2->GetMainFrame()->GetView());
+  // We should be reusing the original RenderWidgetHostView that `contents2`
+  // used before the navigation to `kUrl2`
+  EXPECT_EQ(initial_view, return_nav_view);
+  EXPECT_TRUE(
+      return_nav_view->clear_fallback_surface_for_commit_pending_called());
+  EXPECT_FALSE(return_nav_view->take_fallback_content_from_called());
+  return_nav_view->ClearFallbackSurfaceCalled();
+
+  EXPECT_TRUE(contents1->GetMainFrame()->IsRenderFrameLive());
+  EXPECT_TRUE(contents2->GetMainFrame()->IsRenderFrameLive());
+  EXPECT_EQ(contents1->GetSiteInstance(), contents2->GetSiteInstance());
+  EXPECT_TRUE(contents1->GetMainFrame()->GetView());
+  EXPECT_TRUE(contents2->GetMainFrame()->GetView());
+  // We should also be back to sharing the same RenderProcessHost.
+  MockRenderProcessHost* rph3 = contents2->GetMainFrame()->GetProcess();
+  EXPECT_NE(rph2, rph3);
+  EXPECT_EQ(rph, rph3);
+}
+
 // Ensure that we don't grant WebUI bindings to a pending RenderViewHost when
 // creating proxies for a non-WebUI subframe navigation.  This was possible due
 // to the InitRenderView call from CreateRenderFrameProxy.
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index e69d715..ac1cbdad 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -66,6 +66,10 @@
 #include "components/discardable_memory/public/mojom/discardable_shared_memory_manager.mojom.h"
 #include "components/discardable_memory/service/discardable_shared_memory_manager.h"
 #include "components/metrics/single_sample_metrics.h"
+#include "components/services/storage/public/cpp/buckets/bucket_id.h"
+#include "components/services/storage/public/cpp/buckets/bucket_info.h"
+#include "components/services/storage/public/cpp/buckets/constants.h"
+#include "components/services/storage/public/cpp/quota_error_or.h"
 #include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
 #include "components/services/storage/public/mojom/indexed_db_control.mojom.h"
 #include "components/tracing/common/tracing_switches.h"
@@ -108,6 +112,7 @@
 #include "content/browser/renderer_host/recently_destroyed_hosts.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/renderer_host/render_message_filter.h"
+#include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/renderer_host/render_widget_helper.h"
 #include "content/browser/renderer_host/renderer_sandboxed_process_launcher_delegate.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
@@ -127,6 +132,7 @@
 #include "content/common/pseudonymization_salt.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_or_resource_context.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/device_service.h"
 #include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/network_service_instance.h"
@@ -175,6 +181,7 @@
 #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "storage/browser/database/database_tracker.h"
+#include "storage/browser/quota/quota_manager.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/page/launching_process_state.h"
@@ -2073,8 +2080,22 @@
     const url::Origin& origin,
     mojo::PendingReceiver<blink::mojom::LockManager> receiver) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  storage_partition_impl_->GetQuotaManager()->proxy()->GetOrCreateBucket(
+      blink::StorageKey(origin), storage::kDefaultBucketName,
+      GetUIThreadTaskRunner({}),
+      base::BindOnce(&RenderProcessHostImpl::CreateLockManagerWithBucketInfo,
+                     weak_factory_.GetWeakPtr(), render_frame_id,
+                     std::move(receiver)));
+}
+
+void RenderProcessHostImpl::CreateLockManagerWithBucketInfo(
+    int render_frame_id,
+    mojo::PendingReceiver<blink::mojom::LockManager> receiver,
+    storage::QuotaErrorOr<storage::BucketInfo> bucket) {
   storage_partition_impl_->GetLockManager()->BindReceiver(
-      GetID(), render_frame_id, origin, std::move(receiver));
+      GetID(), render_frame_id, bucket.ok() ? bucket->id : storage::BucketId(),
+      std::move(receiver));
 }
 
 void RenderProcessHostImpl::CreatePermissionService(
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 6a0d49c..88d1379 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -23,6 +23,9 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "components/services/storage/public/cpp/buckets/bucket_id.h"
+#include "components/services/storage/public/cpp/buckets/bucket_info.h"
+#include "components/services/storage/public/cpp/quota_error_or.h"
 #include "content/browser/child_process_launcher.h"
 #include "content/browser/renderer_host/code_cache_host_impl.h"
 #include "content/browser/renderer_host/media/aec_dump_manager_impl.h"
@@ -859,6 +862,13 @@
   // the RenderProcessHost is needed but the renderer process is not.
   bool HasOnlyNonLiveRenderFrameHosts();
 
+  // Helper method for CreateLockManager() which facilitates use of |bucket|
+  // instead of |origin| for binding |receiver|
+  void CreateLockManagerWithBucketInfo(
+      int render_frame_id,
+      mojo::PendingReceiver<blink::mojom::LockManager> receiver,
+      storage::QuotaErrorOr<storage::BucketInfo> bucket);
+
   // Get an existing RenderProcessHost associated with the given browser
   // context, if possible.  The renderer process is chosen randomly from
   // suitable renderers that share the same context and type (determined by the
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index b6829c3..3580332 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -1279,6 +1279,11 @@
   }
 }
 
+void RenderWidgetHostViewAndroid::ClearFallbackSurfaceForCommitPending() {
+  delegated_frame_host_->ClearFallbackSurfaceForCommitPending();
+  local_surface_id_allocator_.Invalidate();
+}
+
 void RenderWidgetHostViewAndroid::ResetFallbackToFirstNavigationSurface() {
   if (delegated_frame_host_)
     delegated_frame_host_->ResetFallbackToFirstNavigationSurface();
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h
index 4b6b423..85e96b8 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.h
+++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -167,6 +167,7 @@
   blink::mojom::PointerLockResult ChangeMouseLock(
       bool request_unadjusted_movement) override;
   void UnlockMouse() override;
+  void ClearFallbackSurfaceForCommitPending() override;
   void ResetFallbackToFirstNavigationSurface() override;
   bool RequestRepaintForTesting() override;
   void SetIsInVR(bool is_in_vr) override;
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index d383517..d295a11 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -930,6 +930,11 @@
   return nullptr;
 }
 
+void RenderWidgetHostViewAura::ClearFallbackSurfaceForCommitPending() {
+  delegated_frame_host_->ClearFallbackSurfaceForCommitPending();
+  window_->InvalidateLocalSurfaceId();
+}
+
 void RenderWidgetHostViewAura::ResetFallbackToFirstNavigationSurface() {
   DCHECK(delegated_frame_host_) << "Cannot be invoked during destruction.";
   delegated_frame_host_->ResetFallbackToFirstNavigationSurface();
@@ -2616,7 +2621,7 @@
 
   // Invalidate the surface so that we don't attempt to evict it multiple times.
   window_->InvalidateLocalSurfaceId();
-    delegated_frame_host_->OnNavigateToNewPage();
+  delegated_frame_host_->OnNavigateToNewPage();
   CancelActiveTouches();
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h
index f13fd54f6..c560d1c 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -172,6 +172,7 @@
   void UnlockKeyboard() override;
   bool IsKeyboardLocked() override;
   base::flat_map<std::string, std::string> GetKeyboardLayoutMap() override;
+  void ClearFallbackSurfaceForCommitPending() override;
   void ResetFallbackToFirstNavigationSurface() override;
   bool RequestRepaintForTesting() override;
   void DidStopFlinging() override;
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h
index 7759ce1..e15b08a 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.h
+++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -239,6 +239,9 @@
   virtual void FocusedNodeChanged(bool is_editable_node,
                                   const gfx::Rect& node_bounds_in_screen) {}
 
+  // This method will clear any cached fallback surface. For use in response to
+  // a CommitPending where there is no content for TakeFallbackContentFrom.
+  virtual void ClearFallbackSurfaceForCommitPending() {}
   // This method will reset the fallback to the first surface after navigation.
   virtual void ResetFallbackToFirstNavigationSurface() = 0;
 
@@ -660,6 +663,8 @@
                            MojoInterfaceReboundOnDisconnect);
   FRIEND_TEST_ALL_PREFIXES(NoCompositingRenderWidgetHostViewBrowserTest,
                            NoFallbackAfterHiddenNavigationFails);
+  FRIEND_TEST_ALL_PREFIXES(NoCompositingRenderWidgetHostViewBrowserTest,
+                           NoFallbackIfSwapFailedBeforeNavigation);
 
   void SynchronizeVisualProperties();
 
diff --git a/content/browser/renderer_host/render_widget_host_view_browsertest.cc b/content/browser/renderer_host/render_widget_host_view_browsertest.cc
index d8014d5..56ad3ab 100644
--- a/content/browser/renderer_host/render_widget_host_view_browsertest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_browsertest.cc
@@ -21,6 +21,7 @@
 #include "content/browser/renderer_host/dip_util.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_view_base.h"
+#include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/gpu_data_manager.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_view_host.h"
@@ -385,6 +386,46 @@
 
 #endif  // !defined(OS_MAC)
 
+// Tests that if a pending commit attempts to swap from a RenderFrameHost which
+// has no Fallback Surface, that we clear pre-existing ones in a
+// RenderWidgetHostViewBase that is being re-used. While still properly
+// allocating a new Surface if Navigation eventually succeeds.
+IN_PROC_BROWSER_TEST_F(NoCompositingRenderWidgetHostViewBrowserTest,
+                       NoFallbackIfSwapFailedBeforeNavigation) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  // Creates the initial RenderWidgetHostViewBase, and connects to a
+  // CompositorFrameSink.
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("/page_with_animation.html")));
+  RenderWidgetHostViewBase* rwhvb = GetRenderWidgetHostView();
+  ASSERT_TRUE(rwhvb);
+  viz::LocalSurfaceId initial_lsid = rwhvb->GetLocalSurfaceId();
+  EXPECT_TRUE(initial_lsid.is_valid());
+
+  // Actually set our Fallback Surface.
+  rwhvb->ResetFallbackToFirstNavigationSurface();
+  EXPECT_TRUE(rwhvb->HasFallbackSurface());
+
+  // Perform a navigation to the same content source. This will reuse the
+  // existing RenderWidgetHostViewBase.
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+  // Notify that this pending commit has no RenderFrameHost with which to get a
+  // Fallback Surface. This should evict the Fallback Surface.
+  web_contents->NotifySwappedFromRenderManagerWithoutFallbackContent(
+      web_contents->GetMainFrame());
+  EXPECT_FALSE(rwhvb->HasFallbackSurface());
+
+  // Actually complete a navigation once we've removed the Fallback Surface.
+  // This should lead to a new viz::LocalSurfaceId.
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("/page_with_animation.html")));
+  EXPECT_TRUE(rwhvb->GetLocalSurfaceId().is_valid());
+  viz::LocalSurfaceId post_nav_lsid = rwhvb->GetLocalSurfaceId();
+  EXPECT_NE(initial_lsid, post_nav_lsid);
+  EXPECT_TRUE(post_nav_lsid.IsNewerThan(initial_lsid));
+}
+
 namespace {
 
 std::unique_ptr<net::test_server::HttpResponse> HandleSlowStyleSheet(
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h
index d81637c..7d6b64bc7 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.h
+++ b/content/browser/renderer_host/render_widget_host_view_mac.h
@@ -148,6 +148,7 @@
   void EnsureSurfaceSynchronizedForWebTest() override;
   void FocusedNodeChanged(bool is_editable_node,
                           const gfx::Rect& node_bounds_in_screen) override;
+  void ClearFallbackSurfaceForCommitPending() override;
   void ResetFallbackToFirstNavigationSurface() override;
   bool RequestRepaintForTesting() override;
   gfx::NativeViewAccessible AccessibilityGetNativeViewAccessible() override;
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index 5eb3097..72099de3 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -1166,6 +1166,12 @@
   }
 }
 
+void RenderWidgetHostViewMac::ClearFallbackSurfaceForCommitPending() {
+  browser_compositor_->GetDelegatedFrameHost()
+      ->ClearFallbackSurfaceForCommitPending();
+  browser_compositor_->InvalidateLocalSurfaceIdOnEviction();
+}
+
 void RenderWidgetHostViewMac::ResetFallbackToFirstNavigationSurface() {
   browser_compositor_->GetDelegatedFrameHost()
       ->ResetFallbackToFirstNavigationSurface();
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 46ddbb2..c4351be 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -7887,6 +7887,13 @@
   NotifyFrameSwapped(old_frame, new_frame);
 }
 
+void WebContentsImpl::NotifySwappedFromRenderManagerWithoutFallbackContent(
+    RenderFrameHostImpl* new_frame) {
+  auto* rwhv = static_cast<RenderWidgetHostViewBase*>(new_frame->GetView());
+  if (rwhv)
+    rwhv->ClearFallbackSurfaceForCommitPending();
+}
+
 void WebContentsImpl::NotifyMainFrameSwappedFromRenderManager(
     RenderFrameHostImpl* old_frame,
     RenderFrameHostImpl* new_frame) {
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index d50ca217..1c40c72 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -986,6 +986,8 @@
   void CancelModalDialogsForRenderManager() override;
   void NotifySwappedFromRenderManager(RenderFrameHostImpl* old_frame,
                                       RenderFrameHostImpl* new_frame) override;
+  void NotifySwappedFromRenderManagerWithoutFallbackContent(
+      RenderFrameHostImpl* new_frame) override;
   void NotifyMainFrameSwappedFromRenderManager(
       RenderFrameHostImpl* old_frame,
       RenderFrameHostImpl* new_frame) override;
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index cc674e90..5bfc6d9a 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -110,6 +110,15 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
+void FederatedAuthRequestImpl::CancelTokenRequest() {
+  if (!auth_request_callback_)
+    return;
+
+  // Dialog will be hidden by the destructor for request_dialog_controller_,
+  // triggered by CompleteRequest.
+  CompleteRequest(RequestIdTokenStatus::kErrorCanceled, "");
+}
+
 void FederatedAuthRequestImpl::Revoke(
     const GURL& provider,
     const std::string& client_id,
diff --git a/content/browser/webid/federated_auth_request_impl.h b/content/browser/webid/federated_auth_request_impl.h
index 89ca82f..98f1f3ae 100644
--- a/content/browser/webid/federated_auth_request_impl.h
+++ b/content/browser/webid/federated_auth_request_impl.h
@@ -46,6 +46,7 @@
       blink::mojom::RequestMode mode,
       bool prefer_auto_sign_in,
       blink::mojom::FederatedAuthRequest::RequestIdTokenCallback);
+  void CancelTokenRequest();
   void Revoke(const GURL& provider,
               const std::string& client_id,
               const std::string& account_id,
diff --git a/content/browser/webid/federated_auth_request_service.cc b/content/browser/webid/federated_auth_request_service.cc
index efa45b6..2fd7f362 100644
--- a/content/browser/webid/federated_auth_request_service.cc
+++ b/content/browser/webid/federated_auth_request_service.cc
@@ -55,6 +55,10 @@
                         std::move(callback));
 }
 
+void FederatedAuthRequestService::CancelTokenRequest() {
+  impl_->CancelTokenRequest();
+}
+
 void FederatedAuthRequestService::Revoke(const GURL& provider,
                                          const std::string& client_id,
                                          const std::string& account_id,
diff --git a/content/browser/webid/federated_auth_request_service.h b/content/browser/webid/federated_auth_request_service.h
index b182d28..9240775 100644
--- a/content/browser/webid/federated_auth_request_service.h
+++ b/content/browser/webid/federated_auth_request_service.h
@@ -48,6 +48,7 @@
                       blink::mojom::RequestMode mode,
                       bool prefer_auto_sign_in,
                       RequestIdTokenCallback) override;
+  void CancelTokenRequest() override;
   void Revoke(const GURL& provider,
               const std::string& client_id,
               const std::string& account_id,
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index eacca78..f26ff21 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -1300,4 +1300,10 @@
     BrowserContext* browser_context) {
   return false;
 }
+
+bool ContentBrowserClient::ShouldPreconnectNavigation(
+    BrowserContext* browser_context) {
+  return false;
+}
+
 }  // namespace content
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 3a57b640..d50c7e0 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -2184,6 +2184,9 @@
   // blink::features::kOriginAgentClusterDefaultEnabled instead.
   virtual bool ShouldDisableOriginAgentClusterDefault(
       BrowserContext* browser_context);
+
+  // Whether a navigation in |browser_context| should preconnect early.
+  virtual bool ShouldPreconnectNavigation(BrowserContext* browser_context);
 };
 
 }  // namespace content
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index be410d78..1909581 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2051,6 +2051,7 @@
     "../browser/loader/navigation_early_hints_manager_unittest.cc",
     "../browser/loader/navigation_url_loader_impl_unittest.cc",
     "../browser/loader/navigation_url_loader_unittest.cc",
+    "../browser/locks/lock_manager_unittest.cc",
     "../browser/manifest/manifest_icon_downloader_unittest.cc",
     "../browser/media/audible_metrics_unittest.cc",
     "../browser/media/audio_input_stream_broker_unittest.cc",
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 22e3455..7c8036a 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -495,6 +495,7 @@
 # Pixel_WebGPUImportVideoFrame hangs on Windows FYI bots
 crbug.com/1274796 [ win nvidia-0x2184 ] Pixel_WebGPUImportVideoFrame [ Failure ]
 crbug.com/1274796 [ win intel-0x5912 ] Pixel_WebGPUImportVideoFrame [ Failure ]
+crbug.com/1274796 [ win intel-0x3e92 ] Pixel_WebGPUImportVideoFrame [ RetryOnFailure ]
 crbug.com/1274796 [ win amd-0x7340 ] Pixel_WebGPUImportVideoFrame [ Failure ]
 
 # Pixel_Video_Media_Stream_Incompatible_Stride flakes with SkiaRenderer GL
diff --git a/content/test/test_render_view_host.cc b/content/test/test_render_view_host.cc
index 0b12d1ff..6a097ce 100644
--- a/content/test/test_render_view_host.cc
+++ b/content/test/test_render_view_host.cc
@@ -189,8 +189,13 @@
   return gfx::Rect();
 }
 
+void TestRenderWidgetHostView::ClearFallbackSurfaceForCommitPending() {
+  clear_fallback_surface_for_commit_pending_called_ = true;
+}
+
 void TestRenderWidgetHostView::TakeFallbackContentFrom(
     RenderWidgetHostView* view) {
+  take_fallback_content_from_called_ = true;
   CopyBackgroundColorIfPresentFrom(*view);
 }
 
@@ -230,6 +235,11 @@
   OnFrameTokenChangedForView(frame_token, activation_time);
 }
 
+void TestRenderWidgetHostView::ClearFallbackSurfaceCalled() {
+  clear_fallback_surface_for_commit_pending_called_ = false;
+  take_fallback_content_from_called_ = false;
+}
+
 std::unique_ptr<SyntheticGestureTarget>
 TestRenderWidgetHostView::CreateSyntheticGestureTarget() {
   NOTIMPLEMENTED();
diff --git a/content/test/test_render_view_host.h b/content/test/test_render_view_host.h
index a35ab16..1807374 100644
--- a/content/test/test_render_view_host.h
+++ b/content/test/test_render_view_host.h
@@ -91,6 +91,9 @@
       blink::mojom::ShareService::ShareCallback callback) override;
 #endif  // defined(OS_MAC)
 
+  // Notified in response to a CommitPending where there is no content for
+  // TakeFallbackContentFrom to use.
+  void ClearFallbackSurfaceForCommitPending() override;
   // Advances the fallback surface to the first surface after navigation. This
   // ensures that stale surfaces are not presented to the user for an indefinite
   // period of time.
@@ -137,6 +140,17 @@
 
   void SetCompositor(ui::Compositor* compositor) { compositor_ = compositor; }
 
+  // Clears `clear_fallback_surface_for_commit_pending_called_` and
+  // `take_fallback_content_from_called_`.
+  void ClearFallbackSurfaceCalled();
+  bool clear_fallback_surface_for_commit_pending_called() const {
+    return clear_fallback_surface_for_commit_pending_called_;
+  }
+
+  bool take_fallback_content_from_called() const {
+    return take_fallback_content_from_called_;
+  }
+
  protected:
   // RenderWidgetHostViewBase:
   void UpdateBackgroundColor() override;
@@ -163,6 +177,9 @@
   // EnsureSurfaceSynchronizedForWebTest().
   uint32_t latest_capture_sequence_number_ = 0u;
 
+  bool clear_fallback_surface_for_commit_pending_called_ = false;
+  bool take_fallback_content_from_called_ = false;
+
 #if defined(USE_AURA)
   std::unique_ptr<aura::Window> window_;
 #endif
diff --git a/device/bluetooth/bluetooth_socket_mac.mm b/device/bluetooth/bluetooth_socket_mac.mm
index f6705be..b8c08f6 100644
--- a/device/bluetooth/bluetooth_socket_mac.mm
+++ b/device/bluetooth/bluetooth_socket_mac.mm
@@ -76,6 +76,15 @@
   return self;
 }
 
+- (void)dealloc {
+  if (_error_callback) {
+    // The delegate's sdpQueryComplete was not called. This may happen if no
+    // target is specified.
+    std::move(_error_callback).Run("No target");
+  }
+  [super dealloc];
+}
+
 - (void)sdpQueryComplete:(IOBluetoothDevice*)device status:(IOReturn)status {
   DCHECK_EQ(device, _device);
   _socket->OnSDPQueryComplete(status, device, std::move(_success_callback),
diff --git a/device/bluetooth/chromeos/bluetooth_utils.cc b/device/bluetooth/chromeos/bluetooth_utils.cc
index c9e9007..b0ab4d9a 100644
--- a/device/bluetooth/chromeos/bluetooth_utils.cc
+++ b/device/bluetooth/chromeos/bluetooth_utils.cc
@@ -393,4 +393,9 @@
       base::StrCat({base_histogram_name, ".", transport_name}), duration);
 }
 
+void RecordSetDeviceNickName(SetNicknameResult set_nickname_result) {
+  base::UmaHistogramEnumeration("Bluetooth.ChromeOS.SetNickname.Result",
+                                set_nickname_result);
+}
+
 }  // namespace device
diff --git a/device/bluetooth/chromeos/bluetooth_utils.h b/device/bluetooth/chromeos/bluetooth_utils.h
index 4ae192a..b78a3e4 100644
--- a/device/bluetooth/chromeos/bluetooth_utils.h
+++ b/device/bluetooth/chromeos/bluetooth_utils.h
@@ -88,6 +88,16 @@
   kMaxValue = kSuccess
 };
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class SetNicknameResult {
+  kInvalidNicknameFormat = 0,
+  kDeviceNotFound = 1,
+  kPrefsUnavailable = 2,
+  kSuccess = 3,
+  kMaxValue = kSuccess,
+};
+
 // Return filtered devices based on the filter type and max number of devices.
 DEVICE_BLUETOOTH_EXPORT device::BluetoothAdapter::DeviceList
 FilterBluetoothDeviceList(const BluetoothAdapter::DeviceList& devices,
@@ -145,6 +155,9 @@
     BluetoothTransport transport,
     base::TimeDelta duration);
 
+// Record each time a Bluetooth device nickname change is attempted.
+DEVICE_BLUETOOTH_EXPORT void RecordSetDeviceNickName(SetNicknameResult success);
+
 }  // namespace device
 
 #endif  // DEVICE_BLUETOOTH_CHROMEOS_BLUETOOTH_UTILS_H_
diff --git a/device/bluetooth/public/cpp/bluetooth_uuid.cc b/device/bluetooth/public/cpp/bluetooth_uuid.cc
index 019e843..92ea80af 100644
--- a/device/bluetooth/public/cpp/bluetooth_uuid.cc
+++ b/device/bluetooth/public/cpp/bluetooth_uuid.cc
@@ -150,8 +150,8 @@
   return canonical_value_ != uuid.canonical_value_;
 }
 
-void PrintTo(const BluetoothUUID& uuid, std::ostream* out) {
-  *out << uuid.canonical_value();
+std::ostream& operator<<(std::ostream& os, BluetoothUUID uuid) {
+  return os << uuid.canonical_value();
 }
 
 }  // namespace device
diff --git a/device/bluetooth/public/cpp/bluetooth_uuid.h b/device/bluetooth/public/cpp/bluetooth_uuid.h
index 0c27ae7..cfa61ce9 100644
--- a/device/bluetooth/public/cpp/bluetooth_uuid.h
+++ b/device/bluetooth/public/cpp/bluetooth_uuid.h
@@ -5,6 +5,7 @@
 #ifndef DEVICE_BLUETOOTH_PUBLIC_CPP_BLUETOOTH_UUID_H_
 #define DEVICE_BLUETOOTH_PUBLIC_CPP_BLUETOOTH_UUID_H_
 
+#include <ostream>
 #include <string>
 #include <vector>
 
@@ -108,8 +109,8 @@
   std::string canonical_value_;
 };
 
-// This is required by gtest to print a readable output on test failures.
-void PrintTo(const BluetoothUUID& uuid, std::ostream* out);
+// Output the 128-bit canonical string representation of `uuid` to `os`.
+std::ostream& operator<<(std::ostream& os, BluetoothUUID uuid);
 
 struct BluetoothUUIDHash {
   size_t operator()(const device::BluetoothUUID& uuid) const {
diff --git a/device/bluetooth/public/cpp/bluetooth_uuid_unittest.cc b/device/bluetooth/public/cpp/bluetooth_uuid_unittest.cc
index 40bc572..2afc706 100644
--- a/device/bluetooth/public/cpp/bluetooth_uuid_unittest.cc
+++ b/device/bluetooth/public/cpp/bluetooth_uuid_unittest.cc
@@ -6,6 +6,8 @@
 
 #include <stddef.h>
 
+#include <sstream>
+
 #include "base/cxx17_backports.h"
 #include "base/strings/string_piece.h"
 #include "build/build_config.h"
@@ -170,4 +172,15 @@
   }
 }
 
+TEST(BluetoothUUIDTest, BluetoothUUID_stream_insertion_operator) {
+  const std::string uuid_str("00001abc-0000-1000-8000-00805f9b34fb");
+
+  BluetoothUUID uuid(uuid_str);
+  std::ostringstream ss;
+  ss << uuid;
+  const std::string out = ss.str();
+
+  EXPECT_EQ(uuid_str, out);
+}
+
 }  // namespace device
diff --git a/extensions/browser/api/web_request/web_request_info.cc b/extensions/browser/api/web_request/web_request_info.cc
index 542e23cf..c96658b1 100644
--- a/extensions/browser/api/web_request/web_request_info.cc
+++ b/extensions/browser/api/web_request/web_request_info.cc
@@ -181,7 +181,7 @@
       ukm_source_id(ukm_source_id) {
   web_request_type = ToWebRequestResourceType(request, is_download);
 
-  DCHECK_EQ(is_navigation_request, navigation_id.has_value());
+  DCHECK_EQ(is_navigation_request, this->navigation_id.has_value());
 
   InitializeWebViewAndFrameData(navigation_ui_data.get());
 
diff --git a/gpu/config/gpu_finch_features.cc b/gpu/config/gpu_finch_features.cc
index 68b74d5e..5183c00e 100644
--- a/gpu/config/gpu_finch_features.cc
+++ b/gpu/config/gpu_finch_features.cc
@@ -213,6 +213,9 @@
 
 #if defined(OS_ANDROID)
 
+const base::FeatureParam<std::string> kVulkanBlockListByHardware{
+    &kVulkan, "BlockListByHardware", "mt*"};
+
 const base::FeatureParam<std::string> kVulkanBlockListByBrand{
     &kVulkan, "BlockListByBrand", "HONOR"};
 
@@ -281,6 +284,8 @@
 
   // Check block list against build info.
   const auto* build_info = base::android::BuildInfo::GetInstance();
+  if (IsDeviceBlocked(build_info->hardware(), kVulkanBlockListByHardware.Get()))
+    return false;
   if (IsDeviceBlocked(build_info->brand(), kVulkanBlockListByBrand.Get()))
     return false;
   if (IsDeviceBlocked(build_info->device(), kVulkanBlockListByDevice.Get()))
diff --git a/infra/config/dev/subprojects/chromium/ci.star b/infra/config/dev/subprojects/chromium/ci.star
index 26d65da..719c5e6 100644
--- a/infra/config/dev/subprojects/chromium/ci.star
+++ b/infra/config/dev/subprojects/chromium/ci.star
@@ -96,7 +96,13 @@
 
 ci_builder(
     name = "win-rel-swarming",
-    os = os.WINDOWS_DEFAULT,
+    os = os.WINDOWS_10,
+    goma_enable_ats = True,
+)
+
+ci_builder(
+    name = "win11-rel-swarming",
+    os = os.WINDOWS_11,
     goma_enable_ats = True,
 )
 
diff --git a/infra/config/dev/subprojects/chromium/consoles/chromium.swarm.star b/infra/config/dev/subprojects/chromium/consoles/chromium.swarm.star
index 8774a33..d0c0f064 100644
--- a/infra/config/dev/subprojects/chromium/consoles/chromium.swarm.star
+++ b/infra/config/dev/subprojects/chromium/consoles/chromium.swarm.star
@@ -13,6 +13,7 @@
         luci.console_view_entry(builder = "ci/mac-rel-swarming"),
         luci.console_view_entry(builder = "ci/mac-arm-rel-swarming"),
         luci.console_view_entry(builder = "ci/win-rel-swarming"),
+        luci.console_view_entry(builder = "ci/win11-rel-swarming"),
     ],
 )
 
diff --git a/infra/config/generated/builders/ci/mac-arm64-on-arm64-rel/properties.textpb b/infra/config/generated/builders/ci/mac-arm64-on-arm64-rel/properties.textpb
index 8e72d66..b0e563e 100644
--- a/infra/config/generated/builders/ci/mac-arm64-on-arm64-rel/properties.textpb
+++ b/infra/config/generated/builders/ci/mac-arm64-on-arm64-rel/properties.textpb
@@ -11,6 +11,9 @@
       "v.test_suite"
     ]
   },
-  "builder_group": "chromium.fyi",
-  "recipe": "chromium"
+  "builder_group": "chromium.mac",
+  "recipe": "chromium",
+  "sheriff_rotations": [
+    "chromium"
+  ]
 }
\ No newline at end of file
diff --git a/infra/config/generated/luci/cr-buildbucket-dev.cfg b/infra/config/generated/luci/cr-buildbucket-dev.cfg
index acb7cbc0..54a4b24 100644
--- a/infra/config/generated/luci/cr-buildbucket-dev.cfg
+++ b/infra/config/generated/luci/cr-buildbucket-dev.cfg
@@ -487,5 +487,63 @@
         }
       }
     }
+    builders {
+      name: "win11-rel-swarming"
+      swarming_host: "chromium-swarm-dev.appspot.com"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Windows-11"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/main"
+        cmd: "luciexe"
+      }
+      properties:
+        '{'
+        '  "$build/goma": {'
+        '    "enable_ats": true,'
+        '    "rpc_extra_params": "?prod",'
+        '    "server_host": "goma.chromium.org",'
+        '    "use_luci_auth": true'
+        '  },'
+        '  "$recipe_engine/resultdb/test_presentation": {'
+        '    "column_keys": [],'
+        '    "grouping_keys": ['
+        '      "status",'
+        '      "v.test_suite"'
+        '    ]'
+        '  },'
+        '  "builder_group": "chromium.dev",'
+        '  "recipe": "swarming/staging"'
+        '}'
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-builder-dev@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      experiments {
+        key: "luci.use_realms"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium_staging"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium_staging"
+          table: "ci_text_artifacts"
+          text_artifacts {}
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
   }
 }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 1efbc58c..e3bdb2c 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -38006,11 +38006,14 @@
         '      }'
         '    }'
         '  },'
-        '  "builder_group": "chromium.fyi",'
+        '  "builder_group": "chromium.mac",'
         '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium"'
+        '  "recipe": "chromium",'
+        '  "sheriff_rotations": ['
+        '    "chromium"'
+        '  ]'
         '}'
-      execution_timeout_secs: 36000
+      execution_timeout_secs: 10800
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
diff --git a/infra/config/generated/luci/luci-milo-dev.cfg b/infra/config/generated/luci/luci-milo-dev.cfg
index 59d1d2e..58204c8 100644
--- a/infra/config/generated/luci/luci-milo-dev.cfg
+++ b/infra/config/generated/luci/luci-milo-dev.cfg
@@ -28,6 +28,9 @@
   builders {
     name: "buildbucket/luci.chromium.ci/win-rel-swarming"
   }
+  builders {
+    name: "buildbucket/luci.chromium.ci/win11-rel-swarming"
+  }
   header {
     oncalls {
       name: "Chromium"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index bf9270c..ac2f92d8 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -216,6 +216,11 @@
     short_name: "11"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/mac-arm64-on-arm64-rel"
+    category: "chromium.mac|release|arm64"
+    short_name: "a64"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/mac-arm64-rel"
     category: "chromium.mac|release|arm64"
     short_name: "bld"
@@ -6211,11 +6216,6 @@
     short_name: "herm"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/mac-arm64-on-arm64-rel"
-    category: "mac"
-    short_name: "a64"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/mac-osxbeta-rel"
     category: "mac"
     short_name: "beta"
@@ -9045,6 +9045,11 @@
     short_name: "11"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/mac-arm64-on-arm64-rel"
+    category: "release|arm64"
+    short_name: "a64"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/mac-arm64-rel"
     category: "release|arm64"
     short_name: "bld"
diff --git a/infra/config/generated/luci/luci-notify.cfg b/infra/config/generated/luci/luci-notify.cfg
index 95dc509..1014407b 100644
--- a/infra/config/generated/luci/luci-notify.cfg
+++ b/infra/config/generated/luci/luci-notify.cfg
@@ -3506,6 +3506,25 @@
   }
   builders {
     bucket: "ci"
+    name: "mac-arm64-on-arm64-rel"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "\\b(bot_update|compile|gclient runhooks|runhooks|update|\\w*nocompile_test)\\b"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "\\b(bot_update|compile|gclient runhooks|runhooks|update|\\w*nocompile_test)\\b"
+    email {
+      rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff"
+    }
+    template: "tree_closure_email_template"
+  }
+  builders {
+    bucket: "ci"
     name: "mac-arm64-rel"
     repository: "https://chromium.googlesource.com/chromium/src"
   }
diff --git a/infra/config/generated/luci/luci-scheduler-dev.cfg b/infra/config/generated/luci/luci-scheduler-dev.cfg
index 1bd30cd..a499a88 100644
--- a/infra/config/generated/luci/luci-scheduler-dev.cfg
+++ b/infra/config/generated/luci/luci-scheduler-dev.cfg
@@ -84,6 +84,16 @@
     builder: "win-rel-swarming-staging"
   }
 }
+job {
+  id: "win11-rel-swarming"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket-dev.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "win11-rel-swarming"
+  }
+}
 trigger {
   id: "chromium-gitiles-trigger"
   realm: "ci"
@@ -96,6 +106,7 @@
   triggers: "mac-rel-swarming"
   triggers: "win-rel-swarming"
   triggers: "win-rel-swarming-staging"
+  triggers: "win11-rel-swarming"
   gitiles {
     repo: "https://chromium.googlesource.com/chromium/src"
     refs: "regexp:refs/heads/main"
diff --git a/infra/config/lib/builders.star b/infra/config/lib/builders.star
index 3eeda99a..1d68306 100644
--- a/infra/config/lib/builders.star
+++ b/infra/config/lib/builders.star
@@ -103,8 +103,10 @@
     WINDOWS_8_1 = os_enum("Windows-8.1", os_category.WINDOWS),
     WINDOWS_10 = os_enum("Windows-10", os_category.WINDOWS),
     WINDOWS_10_1703 = os_enum("Windows-10-15063", os_category.WINDOWS),
-    WINDOWS_10_20h2 = os_enum("Windows-10-19042", os_category.WINDOWS),
     WINDOWS_10_1909 = os_enum("Windows-10-18363", os_category.WINDOWS),
+    WINDOWS_10_20h2 = os_enum("Windows-10-19042", os_category.WINDOWS),
+    WINDOWS_11 = os_enum("Windows-11", os_category.WINDOWS),
+    WINDOWS_11_21h2 = os_enum("Windows-11-22000", os_category.WINDOWS),
     WINDOWS_DEFAULT = os_enum("Windows-10", os_category.WINDOWS),
     WINDOWS_ANY = os_enum("Windows", os_category.WINDOWS),
 )
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index 9f4ba0d..578b5e7 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -4692,17 +4692,6 @@
 )
 
 ci.fyi_mac_builder(
-    name = "mac-arm64-on-arm64-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "mac",
-        short_name = "a64",
-    ),
-    cores = None,
-    cpu = cpu.ARM64,
-    os = os.MAC_11,
-)
-
-ci.fyi_mac_builder(
     name = "mac-hermetic-upgrade-rel",
     console_view_entry = consoles.console_view_entry(
         category = "mac",
@@ -5949,6 +5938,22 @@
 )
 
 ci.mac_builder(
+    name = "mac-arm64-on-arm64-rel",
+
+    # TODO(crbug.com/1186823): Expand to more branches when all M1 bots are
+    # rosettaless.
+    branch_selector = branches.MAIN,
+    console_view_entry = consoles.console_view_entry(
+        category = "release|arm64",
+        short_name = "a64",
+    ),
+    main_console_view = "main",
+    cores = None,
+    cpu = cpu.ARM64,
+    os = os.MAC_11,
+)
+
+ci.mac_builder(
     name = "mac-arm64-rel",
     branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
     console_view_entry = consoles.console_view_entry(
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index a334e35..88d79fd 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1187,7 +1187,7 @@
         Manage Your Google Account
       </message>
       <message name="IDS_IOS_REMOVE_GOOGLE_ACCOUNT_TITLE" desc="Title for the button that removes a Google account from the device">
-      Remove Account from this Device
+        Remove from this Device
       </message>
       <message name="IDS_IOS_MANAGE_SYNC_SETTINGS_TITLE" desc="Title for the view in the Settings for enabling/disabling Sync. [iOS only]">
         Manage Sync
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_REMOVE_GOOGLE_ACCOUNT_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_REMOVE_GOOGLE_ACCOUNT_TITLE.png.sha1
new file mode 100644
index 0000000..4284f391
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_REMOVE_GOOGLE_ACCOUNT_TITLE.png.sha1
@@ -0,0 +1 @@
+608c0a7963e0fc13a0b0d5acd4096e4b61a84bf8
\ No newline at end of file
diff --git a/ios/chrome/browser/policy/reporting/report_generator_ios_unittest.mm b/ios/chrome/browser/policy/reporting/report_generator_ios_unittest.mm
index d4a813f6..d33a467 100644
--- a/ios/chrome/browser/policy/reporting/report_generator_ios_unittest.mm
+++ b/ios/chrome/browser/policy/reporting/report_generator_ios_unittest.mm
@@ -12,6 +12,7 @@
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "components/enterprise/browser/reporting/report_request.h"
 #include "components/policy/core/common/cloud/cloud_policy_util.h"
 #include "components/policy/core/common/mock_policy_service.h"
 #include "components/policy/core/common/policy_map.h"
@@ -39,8 +40,6 @@
 
 class ReportGeneratorIOSTest : public PlatformTest {
  public:
-  using ReportRequest = definition::ReportRequest;
-
   ReportGeneratorIOSTest() : generator_(&delegate_factory_) {
     TestChromeBrowserState::Builder builder;
     builder.SetPath(kProfilePath);
@@ -88,16 +87,15 @@
     histogram_tester_ = std::make_unique<base::HistogramTester>();
     base::RunLoop run_loop;
     std::vector<std::unique_ptr<ReportRequest>> reqs;
-    generator_.Generate(
-        ReportType::kFull,
-        base::BindLambdaForTesting(
-            [&run_loop, &reqs](ReportGenerator::ReportRequests requests) {
-              while (!requests.empty()) {
-                reqs.push_back(std::move(requests.front()));
-                requests.pop();
-              }
-              run_loop.Quit();
-            }));
+    generator_.Generate(ReportType::kFull,
+                        base::BindLambdaForTesting(
+                            [&run_loop, &reqs](ReportRequestQueue requests) {
+                              while (!requests.empty()) {
+                                reqs.push_back(std::move(requests.front()));
+                                requests.pop();
+                              }
+                              run_loop.Quit();
+                            }));
     run_loop.Run();
     VerifyMetrics(reqs);
     return reqs;
@@ -134,27 +132,34 @@
   // Verify the basic request
   auto* basic_request = requests[0].get();
 
-  EXPECT_NE(std::string(), basic_request->computer_name());
-  EXPECT_EQ(std::string(), basic_request->serial_number());
-  EXPECT_EQ(
-      policy::GetBrowserDeviceIdentifier()->SerializePartialAsString(),
-      basic_request->browser_device_identifier().SerializePartialAsString());
-  EXPECT_NE(std::string(), basic_request->device_model());
-  EXPECT_NE(std::string(), basic_request->brand_name());
+  EXPECT_NE(std::string(),
+            basic_request->GetDeviceReportRequest().computer_name());
+  EXPECT_EQ(std::string(),
+            basic_request->GetDeviceReportRequest().serial_number());
+  EXPECT_EQ(policy::GetBrowserDeviceIdentifier()->SerializePartialAsString(),
+            basic_request->GetDeviceReportRequest()
+                .browser_device_identifier()
+                .SerializePartialAsString());
+  EXPECT_NE(std::string(),
+            basic_request->GetDeviceReportRequest().device_model());
+  EXPECT_NE(std::string(),
+            basic_request->GetDeviceReportRequest().brand_name());
 
   // Verify the OS report
-  EXPECT_TRUE(basic_request->has_os_report());
-  auto& os_report = basic_request->os_report();
+  EXPECT_TRUE(basic_request->GetDeviceReportRequest().has_os_report());
+  auto& os_report = basic_request->GetDeviceReportRequest().os_report();
   EXPECT_NE(std::string(), os_report.name());
   EXPECT_NE(std::string(), os_report.arch());
   EXPECT_NE(std::string(), os_report.version());
 
   // Ensure there are no partial reports
-  EXPECT_EQ(0, basic_request->partial_report_types_size());
+  EXPECT_EQ(
+      0, basic_request->GetDeviceReportRequest().partial_report_types_size());
 
   // Verify the browser report
-  EXPECT_TRUE(basic_request->has_browser_report());
-  auto& browser_report = basic_request->browser_report();
+  EXPECT_TRUE(basic_request->GetDeviceReportRequest().has_browser_report());
+  auto& browser_report =
+      basic_request->GetDeviceReportRequest().browser_report();
   EXPECT_NE(std::string(), browser_report.browser_version());
   EXPECT_TRUE(browser_report.has_channel());
   EXPECT_NE(std::string(), browser_report.executable_path());
diff --git a/ios/chrome/browser/policy/reporting/report_scheduler_ios_unittest.mm b/ios/chrome/browser/policy/reporting/report_scheduler_ios_unittest.mm
index a085053..636d5d4 100644
--- a/ios/chrome/browser/policy/reporting/report_scheduler_ios_unittest.mm
+++ b/ios/chrome/browser/policy/reporting/report_scheduler_ios_unittest.mm
@@ -16,6 +16,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/enterprise/browser/controller/fake_browser_dm_token_storage.h"
 #include "components/enterprise/browser/reporting/common_pref_names.h"
+#include "components/enterprise/browser/reporting/report_request.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
 #include "ios/chrome/browser/policy/reporting/reporting_delegate_factory_ios.h"
 #include "ios/chrome/test/ios_chrome_scoped_testing_local_state.h"
@@ -42,9 +43,9 @@
 }  // namespace
 
 ACTION_P(ScheduleGeneratorCallback, request_number) {
-  ReportGenerator::ReportRequests requests;
+  ReportRequestQueue requests;
   for (int i = 0; i < request_number; i++)
-    requests.push(std::make_unique<ReportGenerator::ReportRequest>());
+    requests.push(std::make_unique<ReportRequest>(ReportType::kFull));
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(arg0), std::move(requests)));
 }
@@ -61,7 +62,7 @@
   }
   MOCK_METHOD2(OnGenerate,
                void(ReportType report_type, ReportCallback& callback));
-  MOCK_METHOD0(GenerateBasic, ReportRequests());
+  MOCK_METHOD0(GenerateBasic, ReportRequestQueue());
 };
 
 class MockReportUploader : public ReportUploader {
@@ -71,7 +72,7 @@
   MockReportUploader& operator=(const MockReportUploader&) = delete;
   ~MockReportUploader() override = default;
 
-  MOCK_METHOD2(SetRequestAndUpload, void(ReportRequests, ReportCallback));
+  MOCK_METHOD2(SetRequestAndUpload, void(ReportRequestQueue, ReportCallback));
 };
 
 class ReportSchedulerIOSTest : public PlatformTest {
@@ -134,10 +135,10 @@
     }
   }
 
-  ReportGenerator::ReportRequests CreateRequests(int number) {
-    ReportGenerator::ReportRequests requests;
+  ReportRequestQueue CreateRequests(int number) {
+    ReportRequestQueue requests;
     for (int i = 0; i < number; i++)
-      requests.push(std::make_unique<ReportGenerator::ReportRequest>());
+      requests.push(std::make_unique<ReportRequest>(ReportType::kFull));
     return requests;
   }
 
diff --git a/ios/chrome/browser/ui/authentication/cells/BUILD.gn b/ios/chrome/browser/ui/authentication/cells/BUILD.gn
index 4178acb..bf23160 100644
--- a/ios/chrome/browser/ui/authentication/cells/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/cells/BUILD.gn
@@ -27,6 +27,8 @@
     "//build:branding_buildflags",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser",
+    "//ios/chrome/browser/signin:constants",
+    "//ios/chrome/browser/signin:signin_util",
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/authentication/views",
     "//ios/chrome/browser/ui/collection_view/cells",
diff --git a/ios/chrome/browser/ui/authentication/cells/signin_promo_view_configurator.h b/ios/chrome/browser/ui/authentication/cells/signin_promo_view_configurator.h
index ce11c6c2..76cf3f9 100644
--- a/ios/chrome/browser/ui/authentication/cells/signin_promo_view_configurator.h
+++ b/ios/chrome/browser/ui/authentication/cells/signin_promo_view_configurator.h
@@ -16,8 +16,10 @@
 - (instancetype)init NS_UNAVAILABLE;
 
 // Initializes the instance.
-// |userEmail| is used as a proxy to determine if accounts have
-// been added to the device.
+// If |viewMode| is SigninPromoViewModeNoAccounts, then |userEmail|,
+// |userGivenName| and |userImage| have to be nil.
+// Otherwise |userEmail| and |userImage| can't be nil. |userImage| has to be to
+// the size of IdentityAvatarSize::SmallSize.
 - (instancetype)initWithSigninPromoViewMode:(SigninPromoViewMode)viewMode
                                   userEmail:(NSString*)userEmail
                               userGivenName:(NSString*)userGivenName
diff --git a/ios/chrome/browser/ui/authentication/cells/signin_promo_view_configurator.mm b/ios/chrome/browser/ui/authentication/cells/signin_promo_view_configurator.mm
index 97fba6d5..4e624a6 100644
--- a/ios/chrome/browser/ui/authentication/cells/signin_promo_view_configurator.mm
+++ b/ios/chrome/browser/ui/authentication/cells/signin_promo_view_configurator.mm
@@ -5,8 +5,11 @@
 #import "ios/chrome/browser/ui/authentication/cells/signin_promo_view_configurator.h"
 
 #include "base/strings/sys_string_conversions.h"
+#import "ios/chrome/browser/signin/constants.h"
+#import "ios/chrome/browser/signin/signin_util.h"
 #import "ios/chrome/browser/ui/authentication/cells/signin_promo_view.h"
 #import "ios/chrome/browser/ui/authentication/cells/signin_promo_view_constants.h"
+#import "ios/chrome/common/ui/util/image_util.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/public/provider/chrome/browser/signin/signin_resources_api.h"
@@ -68,6 +71,8 @@
   std::u16string name16 = SysNSStringToUTF16(name);
   switch (self.signinPromoViewMode) {
     case SigninPromoViewModeNoAccounts: {
+      DCHECK(!name);
+      DCHECK(!self.userImage);
       NSString* signInString = GetNSString(IDS_IOS_SYNC_PROMO_TURN_ON_SYNC);
       signinPromoView.accessibilityLabel = signInString;
       [signinPromoView.primaryButton setTitle:signInString
@@ -94,11 +99,14 @@
       break;
     }
   }
-
+  DCHECK(name);
   DCHECK_NE(self.signinPromoViewMode, SigninPromoViewModeNoAccounts);
   UIImage* image = self.userImage;
-  if (!image)
-    image = ios::provider::GetSigninDefaultAvatar();
+  DCHECK(image);
+  CGSize avatarSize =
+      GetSizeForIdentityAvatarSize(IdentityAvatarSize::SmallSize);
+  DCHECK_EQ(avatarSize.width, image.size.width);
+  DCHECK_EQ(avatarSize.height, image.size.height);
   [signinPromoView setProfileImage:image];
 }
 
diff --git a/ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.h b/ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.h
index 85a9aa2..1ee1d334d 100644
--- a/ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.h
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.h
@@ -52,7 +52,7 @@
 // Opens MyGoogle UI from the account list settings.
 + (void)openMyGoogleDialogWithFakeIdentity:(FakeChromeIdentity*)fakeIdentity;
 
-// Taps "Remove account from this device" button and follow-up confirmation.
+// Taps "Remove from this device" button and follow-up confirmation.
 // Assumes the user is on the Settings screen.
 + (void)tapRemoveAccountFromDeviceWithFakeIdentity:
     (FakeChromeIdentity*)fakeIdentity;
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index 70c8fee..826cb0c 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -192,6 +192,7 @@
     "//ios/chrome/common/ui/colors",
     "//ios/chrome/common/ui/favicon",
     "//ios/chrome/common/ui/util",
+    "//ios/chrome/common/ui/util:image_util",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/mailto",
     "//ios/public/provider/chrome/browser/signin",
diff --git a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
index a8aacaac..3792632 100644
--- a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
@@ -5,6 +5,8 @@
 #import "ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.h"
 
 #include "base/mac/foundation_util.h"
+#import "ios/chrome/browser/signin/constants.h"
+#import "ios/chrome/browser/signin/signin_util.h"
 #import "ios/chrome/browser/ui/authentication/cells/signin_promo_view_configurator.h"
 #import "ios/chrome/browser/ui/authentication/cells/table_view_account_item.h"
 #import "ios/chrome/browser/ui/authentication/cells/table_view_signin_promo_item.h"
@@ -35,6 +37,7 @@
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
+#import "ios/chrome/common/ui/util/image_util.h"
 #import "ios/public/provider/chrome/browser/signin/signin_resources_api.h"
 #include "url/gurl.h"
 
@@ -495,17 +498,32 @@
       toSectionWithIdentifier:SectionIdentifierAutofill];
 
   // SectionIdentifierAccount.
+  UIImage* signinPromoAvatar = ios::provider::GetSigninDefaultAvatar();
+  CGSize avatarSize =
+      GetSizeForIdentityAvatarSize(IdentityAvatarSize::SmallSize);
+  signinPromoAvatar =
+      ResizeImage(signinPromoAvatar, avatarSize, ProjectionMode::kFill);
   TableViewSigninPromoItem* signinPromo =
       [[TableViewSigninPromoItem alloc] initWithType:ItemTypeAccount];
   signinPromo.configurator = [[SigninPromoViewConfigurator alloc]
       initWithSigninPromoViewMode:SigninPromoViewModeSigninWithAccount
                         userEmail:@"jonhdoe@example.com"
                     userGivenName:@"John Doe"
-                        userImage:nil
+                        userImage:signinPromoAvatar
                    hasCloseButton:NO];
   signinPromo.text = @"Signin promo text example";
   [model addItem:signinPromo toSectionWithIdentifier:SectionIdentifierAccount];
 
+  signinPromo = [[TableViewSigninPromoItem alloc] initWithType:ItemTypeAccount];
+  signinPromo.configurator = [[SigninPromoViewConfigurator alloc]
+      initWithSigninPromoViewMode:SigninPromoViewModeNoAccounts
+                        userEmail:nil
+                    userGivenName:nil
+                        userImage:nil
+                   hasCloseButton:YES];
+  signinPromo.text = @"Signin promo text example";
+  [model addItem:signinPromo toSectionWithIdentifier:SectionIdentifierAccount];
+
   UIImage* defaultAvatar = ios::provider::GetSigninDefaultAvatar();
 
   TableViewAccountItem* accountItemDetailWithError =
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 3dc06b9..ad30fc7 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-230498e1951004a66ec73c5102bd96c3963ad541
\ No newline at end of file
+67954db99e8f9b886cc85c29b9d93bee7682217e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index 20a11579..c2ef827 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-525b829fa0eb212a83831e8553b3fe820c2425a9
\ No newline at end of file
+d03b1be23d59a99b3b008217a5cc8e0a31d64fac
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 8ca7de3..4577932 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-bed82ffdc8ab7cf2377b832c9c138a61399baef0
\ No newline at end of file
+ebce3952c81d8fbb1e147e07facb13c9da30c1c5
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index d882d351..7ebea87 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-3b945e58f52a2305fd7180c18af6d7f04f96b887
\ No newline at end of file
+651f2db4616d149fc84e79485634f6a972d4775b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index 724c65e..a4573770 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-6f2519a2c1467c5e7ed7c1e7f72fecbe0cfa701c
\ No newline at end of file
+95818481490bce2f520af7f43ee118cb24182305
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index e76eecfe..f239f2e 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-300c9f59171763afd68e7a1f7ccfb49037a9a600
\ No newline at end of file
+8fcb3f7f24cb7f3476fa156502ba9ca15a66866a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 07433b0..11e6c8ca 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-069af65b116b1a93d73e8f67e2f18a91bb75661a
\ No newline at end of file
+321fd382a83f8f671eefe3c90952ef407c0f0284
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index d578d69..179c71b 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-f8fb430360e82952b5465991b7c1a6cb71e2ac46
\ No newline at end of file
+fae1b380608b45729060670e5b9a10d78658d2c2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 6744897..3b45b768 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-81598f1241bb5898b76498892fbb638128723031
\ No newline at end of file
+2ed371c80cd73faddccbf7094b711275443cdf4f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 4274e61..9b87666f 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-d71667f660eda347eb1e22633c43d6bfe45d7ef6
\ No newline at end of file
+5188c53e26e8021a566b1ba5eaafd50f07ab8165
\ No newline at end of file
diff --git a/media/mojo/clients/mojo_video_encode_accelerator.cc b/media/mojo/clients/mojo_video_encode_accelerator.cc
index 32301b33..3842c2ee 100644
--- a/media/mojo/clients/mojo_video_encode_accelerator.cc
+++ b/media/mojo/clients/mojo_video_encode_accelerator.cc
@@ -15,6 +15,8 @@
 #include "media/gpu/gpu_video_accelerator_util.h"
 #include "media/mojo/common/mojo_shared_buffer_video_frame.h"
 #include "media/mojo/mojom/video_encoder_info.mojom.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/system/platform_handle.h"
@@ -31,7 +33,8 @@
  public:
   VideoEncodeAcceleratorClient(
       VideoEncodeAccelerator::Client* client,
-      mojo::PendingReceiver<mojom::VideoEncodeAcceleratorClient> receiver);
+      mojo::PendingAssociatedReceiver<mojom::VideoEncodeAcceleratorClient>
+          receiver);
 
   VideoEncodeAcceleratorClient(const VideoEncodeAcceleratorClient&) = delete;
   VideoEncodeAcceleratorClient& operator=(const VideoEncodeAcceleratorClient&) =
@@ -51,12 +54,13 @@
 
  private:
   raw_ptr<VideoEncodeAccelerator::Client> client_;
-  mojo::Receiver<mojom::VideoEncodeAcceleratorClient> receiver_;
+  mojo::AssociatedReceiver<mojom::VideoEncodeAcceleratorClient> receiver_;
 };
 
 VideoEncodeAcceleratorClient::VideoEncodeAcceleratorClient(
     VideoEncodeAccelerator::Client* client,
-    mojo::PendingReceiver<mojom::VideoEncodeAcceleratorClient> receiver)
+    mojo::PendingAssociatedReceiver<mojom::VideoEncodeAcceleratorClient>
+        receiver)
     : client_(client), receiver_(this, std::move(receiver)) {
   DCHECK(client_);
 }
@@ -120,9 +124,10 @@
 
   // Get a mojom::VideoEncodeAcceleratorClient bound to a local implementation
   // (VideoEncodeAcceleratorClient) and send the remote.
-  mojo::PendingRemote<mojom::VideoEncodeAcceleratorClient> vea_client_remote;
+  mojo::PendingAssociatedRemote<mojom::VideoEncodeAcceleratorClient>
+      vea_client_remote;
   vea_client_ = std::make_unique<VideoEncodeAcceleratorClient>(
-      client, vea_client_remote.InitWithNewPipeAndPassReceiver());
+      client, vea_client_remote.InitWithNewEndpointAndPassReceiver());
 
   bool result = false;
   vea_->Initialize(config, std::move(vea_client_remote), &result);
diff --git a/media/mojo/clients/mojo_video_encode_accelerator_unittest.cc b/media/mojo/clients/mojo_video_encode_accelerator_unittest.cc
index b90711ba..cd413d8 100644
--- a/media/mojo/clients/mojo_video_encode_accelerator_unittest.cc
+++ b/media/mojo/clients/mojo_video_encode_accelerator_unittest.cc
@@ -13,6 +13,8 @@
 #include "media/mojo/clients/mojo_video_encode_accelerator.h"
 #include "media/mojo/mojom/video_encode_accelerator.mojom.h"
 #include "media/video/video_encode_accelerator.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
@@ -45,7 +47,7 @@
   // mojom::VideoEncodeAccelerator impl.
   void Initialize(
       const media::VideoEncodeAccelerator::Config& config,
-      mojo::PendingRemote<mojom::VideoEncodeAcceleratorClient> client,
+      mojo::PendingAssociatedRemote<mojom::VideoEncodeAcceleratorClient> client,
       InitializeCallback success_callback) override {
     if (initialization_success_) {
       ASSERT_TRUE(client);
@@ -62,13 +64,14 @@
     }
     std::move(success_callback).Run(initialization_success_);
   }
-  MOCK_METHOD6(DoInitialize,
-               void(media::VideoPixelFormat,
-                    const gfx::Size&,
-                    media::VideoCodecProfile,
-                    media::Bitrate,
-                    media::VideoEncodeAccelerator::Config::ContentType,
-                    mojo::Remote<mojom::VideoEncodeAcceleratorClient>*));
+  MOCK_METHOD6(
+      DoInitialize,
+      void(media::VideoPixelFormat,
+           const gfx::Size&,
+           media::VideoCodecProfile,
+           media::Bitrate,
+           media::VideoEncodeAccelerator::Config::ContentType,
+           mojo::AssociatedRemote<mojom::VideoEncodeAcceleratorClient>*));
 
   void Encode(const scoped_refptr<VideoFrame>& frame,
               bool keyframe,
@@ -120,7 +123,7 @@
   }
 
  private:
-  mojo::Remote<mojom::VideoEncodeAcceleratorClient> client_;
+  mojo::AssociatedRemote<mojom::VideoEncodeAcceleratorClient> client_;
   int32_t configured_bitstream_buffer_id_ = -1;
   bool initialization_success_ = true;
 };
diff --git a/media/mojo/mojom/video_encode_accelerator.mojom b/media/mojo/mojom/video_encode_accelerator.mojom
index 9da9e49..fcaa9232 100644
--- a/media/mojo/mojom/video_encode_accelerator.mojom
+++ b/media/mojo/mojom/video_encode_accelerator.mojom
@@ -140,7 +140,7 @@
   // Responded by VideoEncodeAcceleratorClient.RequireBitstreamBuffers().
   [Sync]
   Initialize(VideoEncodeAcceleratorConfig config,
-             pending_remote<VideoEncodeAcceleratorClient> client)
+             pending_associated_remote<VideoEncodeAcceleratorClient> client)
       => (bool result);
 
   // Encodes a |frame|, being completely done with it after its callback.
diff --git a/media/mojo/services/mojo_video_encode_accelerator_service.cc b/media/mojo/services/mojo_video_encode_accelerator_service.cc
index d61f920f..6d81cc88 100644
--- a/media/mojo/services/mojo_video_encode_accelerator_service.cc
+++ b/media/mojo/services/mojo_video_encode_accelerator_service.cc
@@ -47,7 +47,7 @@
 
 void MojoVideoEncodeAcceleratorService::Initialize(
     const media::VideoEncodeAccelerator::Config& config,
-    mojo::PendingRemote<mojom::VideoEncodeAcceleratorClient> client,
+    mojo::PendingAssociatedRemote<mojom::VideoEncodeAcceleratorClient> client,
     InitializeCallback success_callback) {
   DVLOG(1) << __func__ << " " << config.AsHumanReadableString();
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/media/mojo/services/mojo_video_encode_accelerator_service.h b/media/mojo/services/mojo_video_encode_accelerator_service.h
index 789b995..46512a28 100644
--- a/media/mojo/services/mojo_video_encode_accelerator_service.h
+++ b/media/mojo/services/mojo_video_encode_accelerator_service.h
@@ -18,8 +18,9 @@
 #include "media/mojo/mojom/video_encode_accelerator.mojom.h"
 #include "media/mojo/services/media_mojo_export.h"
 #include "media/video/video_encode_accelerator.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace gpu {
@@ -62,7 +63,7 @@
   // mojom::VideoEncodeAccelerator impl.
   void Initialize(
       const media::VideoEncodeAccelerator::Config& config,
-      mojo::PendingRemote<mojom::VideoEncodeAcceleratorClient> client,
+      mojo::PendingAssociatedRemote<mojom::VideoEncodeAcceleratorClient> client,
       InitializeCallback callback) override;
   void Encode(const scoped_refptr<VideoFrame>& frame,
               bool force_keyframe,
@@ -98,7 +99,7 @@
 
   // Owned pointer to the underlying VideoEncodeAccelerator.
   std::unique_ptr<::media::VideoEncodeAccelerator> encoder_;
-  mojo::Remote<mojom::VideoEncodeAcceleratorClient> vea_client_;
+  mojo::AssociatedRemote<mojom::VideoEncodeAcceleratorClient> vea_client_;
 
   // Cache of parameters for sanity verification.
   size_t output_buffer_size_;
diff --git a/media/mojo/services/mojo_video_encode_accelerator_service_unittest.cc b/media/mojo/services/mojo_video_encode_accelerator_service_unittest.cc
index 9b8a21b..016e092 100644
--- a/media/mojo/services/mojo_video_encode_accelerator_service_unittest.cc
+++ b/media/mojo/services/mojo_video_encode_accelerator_service_unittest.cc
@@ -15,8 +15,10 @@
 #include "media/mojo/services/mojo_video_encode_accelerator_service.h"
 #include "media/video/fake_video_encode_accelerator.h"
 #include "media/video/video_encode_accelerator.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -100,10 +102,15 @@
 
   void BindAndInitialize() {
     // Create an Mojo VEA Client remote and bind it to our Mock.
-    mojo::PendingRemote<mojom::VideoEncodeAcceleratorClient> mojo_vea_client;
-    mojo_vea_receiver_ = mojo::MakeSelfOwnedReceiver(
+    mojo::PendingAssociatedRemote<mojom::VideoEncodeAcceleratorClient>
+        pending_client_remote;
+    auto pending_client_receiver =
+        pending_client_remote.InitWithNewEndpointAndPassReceiver();
+    pending_client_receiver.EnableUnassociatedUsage();
+
+    mojo_vea_receiver_ = mojo::MakeSelfOwnedAssociatedReceiver(
         std::make_unique<MockMojoVideoEncodeAcceleratorClient>(),
-        mojo_vea_client.InitWithNewPipeAndPassReceiver());
+        std::move(pending_client_receiver));
 
     EXPECT_CALL(*mock_mojo_vea_client(),
                 RequireBitstreamBuffers(_, kInputVisibleSize, _));
@@ -113,7 +120,7 @@
     const media::VideoEncodeAccelerator::Config config(
         PIXEL_FORMAT_I420, kInputVisibleSize, H264PROFILE_MIN, kInitialBitrate);
     mojo_vea_service()->Initialize(
-        config, std::move(mojo_vea_client),
+        config, std::move(pending_client_remote),
         base::BindOnce([](bool success) { ASSERT_TRUE(success); }));
     base::RunLoop().RunUntilIdle();
   }
@@ -135,7 +142,7 @@
  private:
   base::test::SingleThreadTaskEnvironment task_environment_;
 
-  mojo::SelfOwnedReceiverRef<mojom::VideoEncodeAcceleratorClient>
+  mojo::SelfOwnedAssociatedReceiverRef<mojom::VideoEncodeAcceleratorClient>
       mojo_vea_receiver_;
 
   // The class under test.
@@ -233,7 +240,7 @@
        InitializeWithInvalidClientFails) {
   CreateMojoVideoEncodeAccelerator();
 
-  mojo::PendingRemote<mojom::VideoEncodeAcceleratorClient>
+  mojo::PendingAssociatedRemote<mojom::VideoEncodeAcceleratorClient>
       invalid_mojo_vea_client;
 
   constexpr media::Bitrate kInitialBitrate =
@@ -252,10 +259,11 @@
   CreateMojoVideoEncodeAccelerator(
       false /* will_fake_vea_initialization_succeed */);
 
-  mojo::PendingRemote<mojom::VideoEncodeAcceleratorClient> mojo_vea_client;
-  auto mojo_vea_receiver = mojo::MakeSelfOwnedReceiver(
+  mojo::PendingAssociatedRemote<mojom::VideoEncodeAcceleratorClient>
+      mojo_vea_client;
+  auto mojo_vea_receiver = mojo::MakeSelfOwnedAssociatedReceiver(
       std::make_unique<MockMojoVideoEncodeAcceleratorClient>(),
-      mojo_vea_client.InitWithNewPipeAndPassReceiver());
+      mojo_vea_client.InitWithNewEndpointAndPassReceiver());
 
   constexpr media::Bitrate kInitialBitrate =
       media::Bitrate::ConstantBitrate(100000u);
diff --git a/mojo/core/broker_host.cc b/mojo/core/broker_host.cc
index 517ef830..e629119 100644
--- a/mojo/core/broker_host.cc
+++ b/mojo/core/broker_host.cc
@@ -36,7 +36,12 @@
   base::CurrentThread::Get()->AddDestructionObserver(this);
 
   channel_ = Channel::Create(this, std::move(connection_params),
-                             client_process.IsValid()
+#if defined(OS_WIN)
+                             client_process_
+#else
+                             client_process
+#endif
+                                     .IsValid()
                                  ? Channel::HandlePolicy::kAcceptHandles
                                  : Channel::HandlePolicy::kRejectHandles,
                              base::ThreadTaskRunnerHandle::Get());
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index 3c28869..79727ee3 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -88,7 +88,6 @@
     "chromoting_param_traits.h",
     "desktop_and_cursor_conditional_composer.h",
     "desktop_environment.h",
-    "desktop_environment_options.h",
     "evaluate_capability.h",
     "input_injector.h",
     "sas_injector.h",
@@ -259,7 +258,6 @@
     "desktop_display_info.cc",
     "desktop_display_info.h",
     "desktop_display_info_loader.h",
-    "desktop_environment_options.cc",
     "desktop_process.cc",
     "desktop_process.h",
     "desktop_resizer.h",
diff --git a/remoting/host/base/BUILD.gn b/remoting/host/base/BUILD.gn
index ae9b7f8..e0d4f64 100644
--- a/remoting/host/base/BUILD.gn
+++ b/remoting/host/base/BUILD.gn
@@ -13,6 +13,7 @@
   ]
 
   public = [
+    "desktop_environment_options.h",
     "host_exit_codes.h",
     "screen_controls.h",
     "screen_resolution.h",
@@ -21,6 +22,7 @@
   ]
 
   sources = [
+    "desktop_environment_options.cc",
     "host_exit_codes.cc",
     "screen_resolution.cc",
     "switches.cc",
diff --git a/remoting/host/desktop_environment_options.cc b/remoting/host/base/desktop_environment_options.cc
similarity index 78%
rename from remoting/host/desktop_environment_options.cc
rename to remoting/host/base/desktop_environment_options.cc
index 97bf946..fe1647e 100644
--- a/remoting/host/desktop_environment_options.cc
+++ b/remoting/host/base/desktop_environment_options.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "remoting/host/desktop_environment_options.h"
+#include "remoting/host/base/desktop_environment_options.h"
 
 #include <string>
 #include <utility>
@@ -10,10 +10,6 @@
 #include "build/build_config.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-#if defined(OS_WIN)
-#include "remoting/host/win/evaluate_d3d.h"
-#endif
-
 namespace remoting {
 
 using DesktopCaptureOptions = webrtc::DesktopCaptureOptions;
@@ -35,22 +31,13 @@
 DesktopEnvironmentOptions::DesktopEnvironmentOptions(
     const DesktopEnvironmentOptions& other) = default;
 DesktopEnvironmentOptions::~DesktopEnvironmentOptions() = default;
-DesktopEnvironmentOptions&
-DesktopEnvironmentOptions::operator=(
+DesktopEnvironmentOptions& DesktopEnvironmentOptions::operator=(
     DesktopEnvironmentOptions&& other) = default;
-DesktopEnvironmentOptions&
-DesktopEnvironmentOptions::operator=(
+DesktopEnvironmentOptions& DesktopEnvironmentOptions::operator=(
     const DesktopEnvironmentOptions& other) = default;
 
 void DesktopEnvironmentOptions::Initialize() {
   desktop_capture_options_.set_detect_updated_region(true);
-#if defined (OS_WIN)
-  // Whether DirectX capturer can be enabled depends on various factors,
-  // including how many applications are using related APIs.
-  // WebRTC/DesktopCapturer will take care of those details. This check is used
-  // to ensure D3D APIs are safe to access in the current environment.
-  desktop_capture_options_.set_allow_directx_capturer(IsD3DAvailable());
-#endif
 }
 
 const DesktopCaptureOptions*
@@ -58,8 +45,7 @@
   return &desktop_capture_options_;
 }
 
-DesktopCaptureOptions*
-DesktopEnvironmentOptions::desktop_capture_options() {
+DesktopCaptureOptions* DesktopEnvironmentOptions::desktop_capture_options() {
   return &desktop_capture_options_;
 }
 
@@ -131,12 +117,6 @@
 
 void DesktopEnvironmentOptions::ApplySessionOptions(
     const SessionOptions& options) {
-#if defined(OS_WIN)
-  absl::optional<bool> directx_capturer = options.GetBool("DirectX-Capturer");
-  if (directx_capturer) {
-    desktop_capture_options_.set_allow_directx_capturer(*directx_capturer);
-  }
-#endif
   // This field is for test purpose. Usually it should not be set to false.
   absl::optional<bool> detect_updated_region =
       options.GetBool("Detect-Updated-Region");
diff --git a/remoting/host/desktop_environment_options.h b/remoting/host/base/desktop_environment_options.h
similarity index 92%
rename from remoting/host/desktop_environment_options.h
rename to remoting/host/base/desktop_environment_options.h
index fc4268c..fb78643 100644
--- a/remoting/host/desktop_environment_options.h
+++ b/remoting/host/base/desktop_environment_options.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef REMOTING_HOST_DESKTOP_ENVIRONMENT_OPTIONS_H_
-#define REMOTING_HOST_DESKTOP_ENVIRONMENT_OPTIONS_H_
+#ifndef REMOTING_HOST_BASE_DESKTOP_ENVIRONMENT_OPTIONS_H_
+#define REMOTING_HOST_BASE_DESKTOP_ENVIRONMENT_OPTIONS_H_
 
 #include "base/memory/weak_ptr.h"
 #include "remoting/base/session_options.h"
@@ -67,7 +67,7 @@
   // instances. Note, not all DesktopEnvironments support curtain mode.
   bool enable_curtaining_ = false;
 
-  // True if a user-interactive window is showing up in it2me scenario.
+  // True if user-interactive windows should be displayed on the desktop.
   bool enable_user_interface_ = true;
 
   // True if a notification should be shown when a remote user is connected.
@@ -98,4 +98,4 @@
 
 }  // namespace remoting
 
-#endif  // REMOTING_HOST_DESKTOP_ENVIRONMENT_OPTIONS_H_
+#endif  // REMOTING_HOST_BASE_DESKTOP_ENVIRONMENT_OPTIONS_H_
diff --git a/remoting/host/basic_desktop_environment.cc b/remoting/host/basic_desktop_environment.cc
index ee417c5..f6ca2f2 100644
--- a/remoting/host/basic_desktop_environment.cc
+++ b/remoting/host/basic_desktop_environment.cc
@@ -177,15 +177,8 @@
   desktop_capture_options().x_display()->IgnoreXServerGrabs();
   watchdog.Disarm();
 #elif defined(OS_WIN)
-  // The options passed to this instance are determined by a process running in
-  // Session 0.  Access to DirectX functions in Session 0 is limited so the
-  // results are not guaranteed to be accurate in the desktop context.  Due to
-  // this problem, we need to requery the following method to make sure we are
-  // still safe to use D3D APIs.  Only overwrite the value if it isn't safe to
-  // use D3D APIs as we don't want to re-enable this setting if it was disabled
-  // via an experiment or client flag.
-  if (!IsD3DAvailable())
-    options_.desktop_capture_options()->set_allow_directx_capturer(false);
+  options_.desktop_capture_options()->set_allow_directx_capturer(
+      IsD3DAvailable());
 #endif
 }
 
diff --git a/remoting/host/chromoting_host.h b/remoting/host/chromoting_host.h
index 963f69a..6987eab3 100644
--- a/remoting/host/chromoting_host.h
+++ b/remoting/host/chromoting_host.h
@@ -16,8 +16,8 @@
 #include "base/threading/thread.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "net/base/backoff_entry.h"
+#include "remoting/host/base/desktop_environment_options.h"
 #include "remoting/host/client_session.h"
-#include "remoting/host/desktop_environment_options.h"
 #include "remoting/host/host_extension.h"
 #include "remoting/host/host_status_monitor.h"
 #include "remoting/host/host_status_observer.h"
diff --git a/remoting/host/chromoting_messages.h b/remoting/host/chromoting_messages.h
index d304e3a..b635ef8c 100644
--- a/remoting/host/chromoting_messages.h
+++ b/remoting/host/chromoting_messages.h
@@ -13,9 +13,9 @@
 #include "ipc/ipc_channel_handle.h"
 #include "ipc/ipc_message_start.h"
 #include "ipc/ipc_platform_file.h"
+#include "remoting/host/base/desktop_environment_options.h"
 #include "remoting/host/base/screen_resolution.h"
 #include "remoting/host/chromoting_param_traits.h"
-#include "remoting/host/desktop_environment_options.h"
 #include "remoting/proto/action.pb.h"
 #include "remoting/proto/control.pb.h"
 #include "remoting/protocol/errors.h"
diff --git a/remoting/host/chromoting_param_traits.h b/remoting/host/chromoting_param_traits.h
index 87bc30be..c3240d7 100644
--- a/remoting/host/chromoting_param_traits.h
+++ b/remoting/host/chromoting_param_traits.h
@@ -10,8 +10,8 @@
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "remoting/base/result.h"
+#include "remoting/host/base/desktop_environment_options.h"
 #include "remoting/host/base/screen_resolution.h"
-#include "remoting/host/desktop_environment_options.h"
 #include "remoting/proto/action.pb.h"
 #include "remoting/proto/control.pb.h"
 #include "remoting/proto/file_transfer.pb.h"
diff --git a/remoting/host/client_session.h b/remoting/host/client_session.h
index 5c563cd..a9a4549 100644
--- a/remoting/host/client_session.h
+++ b/remoting/host/client_session.h
@@ -18,13 +18,13 @@
 #include "base/timer/timer.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
+#include "remoting/host/base/desktop_environment_options.h"
 #include "remoting/host/client_session_control.h"
 #include "remoting/host/client_session_details.h"
 #include "remoting/host/client_session_events.h"
 #include "remoting/host/desktop_and_cursor_composer_notifier.h"
 #include "remoting/host/desktop_and_cursor_conditional_composer.h"
 #include "remoting/host/desktop_display_info.h"
-#include "remoting/host/desktop_environment_options.h"
 #include "remoting/host/host_experiment_session_plugin.h"
 #include "remoting/host/host_extension_session_manager.h"
 #include "remoting/host/mojom/chromoting_host_services.mojom.h"
diff --git a/remoting/host/client_session_unittest.cc b/remoting/host/client_session_unittest.cc
index b330c43..8849222 100644
--- a/remoting/host/client_session_unittest.cc
+++ b/remoting/host/client_session_unittest.cc
@@ -809,34 +809,4 @@
                    ->detect_updated_region());
 }
 
-#if defined(OS_WIN)
-TEST_F(ClientSessionTest, ForwardDirectXHostSessionOptions1) {
-  auto session = std::make_unique<protocol::FakeSession>();
-  auto configuration = std::make_unique<jingle_xmpp::XmlElement>(
-      jingle_xmpp::QName(kChromotingXmlNamespace, "host-configuration"));
-  configuration->SetBodyText("DirectX-Capturer:true");
-  session->SetAttachment(0, std::move(configuration));
-  CreateClientSession(std::move(session));
-  ConnectClientSession();
-  ASSERT_TRUE(desktop_environment_factory_->last_desktop_environment()
-                  ->options()
-                  .desktop_capture_options()
-                  ->allow_directx_capturer());
-}
-
-TEST_F(ClientSessionTest, ForwardDirectXHostSessionOptions2) {
-  auto session = std::make_unique<protocol::FakeSession>();
-  auto configuration = std::make_unique<jingle_xmpp::XmlElement>(
-      jingle_xmpp::QName(kChromotingXmlNamespace, "host-configuration"));
-  configuration->SetBodyText("DirectX-Capturer:false");
-  session->SetAttachment(0, std::move(configuration));
-  CreateClientSession(std::move(session));
-  ConnectClientSession();
-  ASSERT_FALSE(desktop_environment_factory_->last_desktop_environment()
-                   ->options()
-                   .desktop_capture_options()
-                   ->allow_directx_capturer());
-}
-#endif
-
 }  // namespace remoting
diff --git a/remoting/host/desktop_environment.h b/remoting/host/desktop_environment.h
index 11b3043..439c796 100644
--- a/remoting/host/desktop_environment.h
+++ b/remoting/host/desktop_environment.h
@@ -12,8 +12,8 @@
 #include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "remoting/host/base/desktop_environment_options.h"
 #include "remoting/host/desktop_and_cursor_conditional_composer.h"
-#include "remoting/host/desktop_environment_options.h"
 
 namespace webrtc {
 class DesktopCapturer;
diff --git a/remoting/host/desktop_session_agent.h b/remoting/host/desktop_session_agent.h
index bbf7c353..fe585c5c 100644
--- a/remoting/host/desktop_session_agent.h
+++ b/remoting/host/desktop_session_agent.h
@@ -21,10 +21,10 @@
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
 #include "mojo/public/cpp/system/message_pipe.h"
+#include "remoting/host/base/desktop_environment_options.h"
 #include "remoting/host/client_session_control.h"
 #include "remoting/host/desktop_and_cursor_conditional_composer.h"
 #include "remoting/host/desktop_display_info.h"
-#include "remoting/host/desktop_environment_options.h"
 #include "remoting/host/file_transfer/session_file_operations_handler.h"
 #include "remoting/host/mojom/desktop_session.mojom.h"
 #include "remoting/host/mojom/remoting_mojom_traits.h"
diff --git a/remoting/host/desktop_session_agent_unittest.cc b/remoting/host/desktop_session_agent_unittest.cc
index d2da93f..bdc0a7d3 100644
--- a/remoting/host/desktop_session_agent_unittest.cc
+++ b/remoting/host/desktop_session_agent_unittest.cc
@@ -19,9 +19,9 @@
 #include "ipc/ipc_channel_proxy.h"
 #include "ipc/ipc_listener.h"
 #include "remoting/base/auto_thread_task_runner.h"
+#include "remoting/host/base/desktop_environment_options.h"
 #include "remoting/host/base/screen_resolution.h"
 #include "remoting/host/chromoting_messages.h"
-#include "remoting/host/desktop_environment_options.h"
 #include "remoting/host/fake_desktop_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/remoting/host/fake_desktop_environment.h b/remoting/host/fake_desktop_environment.h
index e8f59588..c2d46cd 100644
--- a/remoting/host/fake_desktop_environment.h
+++ b/remoting/host/fake_desktop_environment.h
@@ -13,9 +13,9 @@
 #include "base/memory/raw_ptr.h"
 #include "base/task/single_thread_task_runner.h"
 #include "remoting/host/action_executor.h"
+#include "remoting/host/base/desktop_environment_options.h"
 #include "remoting/host/base/screen_controls.h"
 #include "remoting/host/desktop_environment.h"
-#include "remoting/host/desktop_environment_options.h"
 #include "remoting/host/fake_mouse_cursor_monitor.h"
 #include "remoting/host/input_injector.h"
 #include "remoting/protocol/fake_desktop_capturer.h"
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index bfb7d12..0b1e23f1 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -52,6 +52,7 @@
 #include "remoting/base/rsa_key_pair.h"
 #include "remoting/base/service_urls.h"
 #include "remoting/base/util.h"
+#include "remoting/host/base/desktop_environment_options.h"
 #include "remoting/host/base/host_exit_codes.h"
 #include "remoting/host/base/switches.h"
 #include "remoting/host/base/username.h"
@@ -63,7 +64,6 @@
 #include "remoting/host/config_watcher.h"
 #include "remoting/host/crash_process.h"
 #include "remoting/host/desktop_environment.h"
-#include "remoting/host/desktop_environment_options.h"
 #include "remoting/host/desktop_session_connector.h"
 #include "remoting/host/ftl_echo_message_listener.h"
 #include "remoting/host/ftl_host_change_notification_listener.h"
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 3f2d2bf..2507a6f5 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -3752,7 +3752,8 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android31.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android31.textpb",
+          "--gtest_filter=-ScopedDirTest.CloseOutOfScope"
         ],
         "merge": {
           "args": [
diff --git a/testing/buildbot/chromium.dawn.json b/testing/buildbot/chromium.dawn.json
index 0f899a2..67d2e85 100644
--- a/testing/buildbot/chromium.dawn.json
+++ b/testing/buildbot/chromium.dawn.json
@@ -194,7 +194,6 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--no-xvfb",
           "--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan"
         ],
@@ -228,7 +227,6 @@
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu-with-backend-validation",
           "--time-out-ms=30000",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--no-xvfb",
           "--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan"
         ],
@@ -450,7 +448,6 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--no-xvfb",
           "--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan"
         ],
@@ -484,7 +481,6 @@
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu-with-backend-validation",
           "--time-out-ms=30000",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--no-xvfb",
           "--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan"
         ],
@@ -706,7 +702,6 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--no-xvfb",
           "--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan"
         ],
@@ -740,7 +735,6 @@
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu-with-backend-validation",
           "--time-out-ms=30000",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--no-xvfb",
           "--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan"
         ],
@@ -962,7 +956,6 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--no-xvfb",
           "--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan"
         ],
@@ -996,7 +989,6 @@
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu-with-backend-validation",
           "--time-out-ms=30000",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--no-xvfb",
           "--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan"
         ],
@@ -1235,7 +1227,6 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--platform=mac-mac11"
         ],
         "isolate_name": "webgpu_blink_web_tests",
@@ -1271,7 +1262,6 @@
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu-with-backend-validation",
           "--time-out-ms=30000",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--platform=mac-mac11"
         ],
         "isolate_name": "webgpu_blink_web_tests",
@@ -1494,7 +1484,6 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--platform=mac-mac11"
         ],
         "isolate_name": "webgpu_blink_web_tests",
@@ -1528,7 +1517,6 @@
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu-with-backend-validation",
           "--time-out-ms=30000",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--platform=mac-mac11"
         ],
         "isolate_name": "webgpu_blink_web_tests",
@@ -1839,7 +1827,6 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--platform=mac-mac11"
         ],
         "isolate_name": "webgpu_blink_web_tests",
@@ -1875,7 +1862,6 @@
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu-with-backend-validation",
           "--time-out-ms=30000",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--platform=mac-mac11"
         ],
         "isolate_name": "webgpu_blink_web_tests",
@@ -2098,7 +2084,6 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--platform=mac-mac11"
         ],
         "isolate_name": "webgpu_blink_web_tests",
@@ -2132,7 +2117,6 @@
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu-with-backend-validation",
           "--time-out-ms=30000",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--platform=mac-mac11"
         ],
         "isolate_name": "webgpu_blink_web_tests",
@@ -2352,7 +2336,6 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--target=Release_x64",
           "--time-out-ms=48000"
         ],
@@ -2575,7 +2558,6 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--target=Release_x64"
         ],
         "isolate_name": "webgpu_blink_web_tests",
@@ -2608,7 +2590,6 @@
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu-with-partial-backend-validation",
           "--time-out-ms=30000",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--target=Release_x64"
         ],
         "isolate_name": "webgpu_blink_web_tests",
@@ -2823,7 +2804,6 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--target=Release_x64"
         ],
         "isolate_name": "webgpu_blink_web_tests",
@@ -2856,7 +2836,6 @@
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu-with-partial-backend-validation",
           "--time-out-ms=30000",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--target=Release_x64"
         ],
         "isolate_name": "webgpu_blink_web_tests",
@@ -3071,7 +3050,6 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--target=Release_x64"
         ],
         "isolate_name": "webgpu_blink_web_tests",
@@ -3104,7 +3082,6 @@
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu-with-partial-backend-validation",
           "--time-out-ms=30000",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--target=Release_x64"
         ],
         "isolate_name": "webgpu_blink_web_tests",
@@ -3319,7 +3296,6 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--target=Release_x64"
         ],
         "isolate_name": "webgpu_blink_web_tests",
@@ -3352,7 +3328,6 @@
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu-with-partial-backend-validation",
           "--time-out-ms=30000",
-          "--additional-driver-flag=--disable-gpu-sandbox",
           "--target=Release_x64"
         ],
         "isolate_name": "webgpu_blink_web_tests",
@@ -3568,8 +3543,7 @@
       {
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
-          "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox"
+          "--flag-specific=webgpu"
         ],
         "isolate_name": "webgpu_blink_web_tests",
         "merge": {
@@ -3600,8 +3574,7 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu-with-partial-backend-validation",
-          "--time-out-ms=30000",
-          "--additional-driver-flag=--disable-gpu-sandbox"
+          "--time-out-ms=30000"
         ],
         "isolate_name": "webgpu_blink_web_tests",
         "merge": {
@@ -3814,8 +3787,7 @@
       {
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
-          "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox"
+          "--flag-specific=webgpu"
         ],
         "isolate_name": "webgpu_blink_web_tests",
         "merge": {
@@ -3846,8 +3818,7 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu-with-partial-backend-validation",
-          "--time-out-ms=30000",
-          "--additional-driver-flag=--disable-gpu-sandbox"
+          "--time-out-ms=30000"
         ],
         "isolate_name": "webgpu_blink_web_tests",
         "merge": {
@@ -4060,8 +4031,7 @@
       {
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
-          "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox"
+          "--flag-specific=webgpu"
         ],
         "isolate_name": "webgpu_blink_web_tests",
         "merge": {
@@ -4092,8 +4062,7 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu-with-partial-backend-validation",
-          "--time-out-ms=30000",
-          "--additional-driver-flag=--disable-gpu-sandbox"
+          "--time-out-ms=30000"
         ],
         "isolate_name": "webgpu_blink_web_tests",
         "merge": {
@@ -4306,8 +4275,7 @@
       {
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
-          "--flag-specific=webgpu",
-          "--additional-driver-flag=--disable-gpu-sandbox"
+          "--flag-specific=webgpu"
         ],
         "isolate_name": "webgpu_blink_web_tests",
         "merge": {
@@ -4338,8 +4306,7 @@
         "args": [
           "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--flag-specific=webgpu-with-partial-backend-validation",
-          "--time-out-ms=30000",
-          "--additional-driver-flag=--disable-gpu-sandbox"
+          "--time-out-ms=30000"
         ],
         "isolate_name": "webgpu_blink_web_tests",
         "merge": {
diff --git a/testing/buildbot/chromium.dev.json b/testing/buildbot/chromium.dev.json
index a1088e3..3072755 100644
--- a/testing/buildbot/chromium.dev.json
+++ b/testing/buildbot/chromium.dev.json
@@ -848,5 +848,81 @@
         "test_id_prefix": "ninja://chrome/test:unit_tests/"
       }
     ]
+  },
+  "win11-rel-swarming": {
+    "gtest_tests": [
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "service_account": "chromium-tester-dev@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "base_unittests",
+        "test_id_prefix": "ninja://base:base_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "service_account": "chromium-tester-dev@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "content_browsertests",
+        "test_id_prefix": "ninja://content/test:content_browsertests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "service_account": "chromium-tester-dev@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "content_unittests",
+        "test_id_prefix": "ninja://content/test:content_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "service_account": "chromium-tester-dev@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "interactive_ui_tests",
+        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "service_account": "chromium-tester-dev@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "net_unittests",
+        "test_id_prefix": "ninja://net:net_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "service_account": "chromium-tester-dev@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "unit_tests",
+        "test_id_prefix": "ninja://chrome/test:unit_tests/"
+      }
+    ]
   }
 }
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 474d08f9a..55942ff 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -13012,46 +13012,6 @@
           "--browser=release",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=metal --use-cmd-decoder=passthrough --force_high_performance_gpu --disable-metal-shader-cache",
-          "--webgl-conformance-version=2.0.1",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json",
-          "--is-asan"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgl2_conformance_metal_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "display_attached": "1",
-              "gpu": "8086:3e9b",
-              "os": "Mac-11.5.2"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 20
-        },
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating --force_high_performance_gpu",
           "--webgl-conformance-version=2.0.1",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json",
@@ -13131,45 +13091,6 @@
           "--browser=release",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=metal --use-cmd-decoder=passthrough --force_high_performance_gpu --disable-metal-shader-cache",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json",
-          "--is-asan"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgl_conformance_metal_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "display_attached": "1",
-              "gpu": "8086:3e9b",
-              "os": "Mac-11.5.2"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=swiftshader --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--test-filter=conformance/rendering/gl-drawelements.html",
           "--is-asan"
diff --git a/testing/buildbot/filters/stable_test_filters/linux-stable-filter-combined-rel/nacl_helper_nonsfi_unittests.filter b/testing/buildbot/filters/stable_test_filters/linux-stable-filter-combined-rel/nacl_helper_nonsfi_unittests.filter
deleted file mode 100644
index cd002c9..0000000
--- a/testing/buildbot/filters/stable_test_filters/linux-stable-filter-combined-rel/nacl_helper_nonsfi_unittests.filter
+++ /dev/null
@@ -1,362 +0,0 @@
--NaClNonSfiSandboxSIGSYSTest._newselect
--NaClNonSfiSandboxSIGSYSTest._sysctl
--NaClNonSfiSandboxSIGSYSTest.access
--NaClNonSfiSandboxSIGSYSTest.acct
--NaClNonSfiSandboxSIGSYSTest.add_key
--NaClNonSfiSandboxSIGSYSTest.adjtimex
--NaClNonSfiSandboxSIGSYSTest.afs_syscall
--NaClNonSfiSandboxSIGSYSTest.alarm
--NaClNonSfiSandboxSIGSYSTest.bdflush
--NaClNonSfiSandboxSIGSYSTest.break
--NaClNonSfiSandboxSIGSYSTest.capget
--NaClNonSfiSandboxSIGSYSTest.capset
--NaClNonSfiSandboxSIGSYSTest.chdir
--NaClNonSfiSandboxSIGSYSTest.chmod
--NaClNonSfiSandboxSIGSYSTest.chown
--NaClNonSfiSandboxSIGSYSTest.chown32
--NaClNonSfiSandboxSIGSYSTest.chroot
--NaClNonSfiSandboxSIGSYSTest.clock_adjtime
--NaClNonSfiSandboxSIGSYSTest.clock_nanosleep
--NaClNonSfiSandboxSIGSYSTest.clock_settime
--NaClNonSfiSandboxSIGSYSTest.creat
--NaClNonSfiSandboxSIGSYSTest.create_module
--NaClNonSfiSandboxSIGSYSTest.delete_module
--NaClNonSfiSandboxSIGSYSTest.dup3
--NaClNonSfiSandboxSIGSYSTest.epoll_create1
--NaClNonSfiSandboxSIGSYSTest.epoll_ctl
--NaClNonSfiSandboxSIGSYSTest.epoll_pwait
--NaClNonSfiSandboxSIGSYSTest.epoll_wait
--NaClNonSfiSandboxSIGSYSTest.eventfd
--NaClNonSfiSandboxSIGSYSTest.eventfd2
--NaClNonSfiSandboxSIGSYSTest.execve
--NaClNonSfiSandboxSIGSYSTest.faccessat
--NaClNonSfiSandboxSIGSYSTest.fadvise64
--NaClNonSfiSandboxSIGSYSTest.fadvise64_64
--NaClNonSfiSandboxSIGSYSTest.fallocate
--NaClNonSfiSandboxSIGSYSTest.fanotify_init
--NaClNonSfiSandboxSIGSYSTest.fanotify_mark
--NaClNonSfiSandboxSIGSYSTest.fchdir
--NaClNonSfiSandboxSIGSYSTest.fchmod
--NaClNonSfiSandboxSIGSYSTest.fchmodat
--NaClNonSfiSandboxSIGSYSTest.fchown
--NaClNonSfiSandboxSIGSYSTest.fchown32
--NaClNonSfiSandboxSIGSYSTest.fchownat
--NaClNonSfiSandboxSIGSYSTest.fcntl
--NaClNonSfiSandboxSIGSYSTest.fdatasync
--NaClNonSfiSandboxSIGSYSTest.fgetxattr
--NaClNonSfiSandboxSIGSYSTest.flistxattr
--NaClNonSfiSandboxSIGSYSTest.flock
--NaClNonSfiSandboxSIGSYSTest.fork
--NaClNonSfiSandboxSIGSYSTest.fremovexattr
--NaClNonSfiSandboxSIGSYSTest.fsetxattr
--NaClNonSfiSandboxSIGSYSTest.fstat
--NaClNonSfiSandboxSIGSYSTest.fstatat64
--NaClNonSfiSandboxSIGSYSTest.fstatfs
--NaClNonSfiSandboxSIGSYSTest.fstatfs64
--NaClNonSfiSandboxSIGSYSTest.fsync
--NaClNonSfiSandboxSIGSYSTest.ftime
--NaClNonSfiSandboxSIGSYSTest.ftruncate
--NaClNonSfiSandboxSIGSYSTest.ftruncate64
--NaClNonSfiSandboxSIGSYSTest.futimesat
--NaClNonSfiSandboxSIGSYSTest.get_kernel_syms
--NaClNonSfiSandboxSIGSYSTest.get_mempolicy
--NaClNonSfiSandboxSIGSYSTest.get_robust_list
--NaClNonSfiSandboxSIGSYSTest.get_thread_area
--NaClNonSfiSandboxSIGSYSTest.getcpu
--NaClNonSfiSandboxSIGSYSTest.getcwd
--NaClNonSfiSandboxSIGSYSTest.getdents
--NaClNonSfiSandboxSIGSYSTest.getdents64
--NaClNonSfiSandboxSIGSYSTest.getgroups
--NaClNonSfiSandboxSIGSYSTest.getgroups32
--NaClNonSfiSandboxSIGSYSTest.getitimer
--NaClNonSfiSandboxSIGSYSTest.getpgid
--NaClNonSfiSandboxSIGSYSTest.getpgrp
--NaClNonSfiSandboxSIGSYSTest.getpid
--NaClNonSfiSandboxSIGSYSTest.getpmsg
--NaClNonSfiSandboxSIGSYSTest.getppid
--NaClNonSfiSandboxSIGSYSTest.getpriority
--NaClNonSfiSandboxSIGSYSTest.getresgid
--NaClNonSfiSandboxSIGSYSTest.getresgid32
--NaClNonSfiSandboxSIGSYSTest.getresuid
--NaClNonSfiSandboxSIGSYSTest.getresuid32
--NaClNonSfiSandboxSIGSYSTest.getrlimit
--NaClNonSfiSandboxSIGSYSTest.getrusage
--NaClNonSfiSandboxSIGSYSTest.getsid
--NaClNonSfiSandboxSIGSYSTest.getxattr
--NaClNonSfiSandboxSIGSYSTest.gtty
--NaClNonSfiSandboxSIGSYSTest.idle
--NaClNonSfiSandboxSIGSYSTest.init_module
--NaClNonSfiSandboxSIGSYSTest.inotify_add_watch
--NaClNonSfiSandboxSIGSYSTest.inotify_init
--NaClNonSfiSandboxSIGSYSTest.inotify_init1
--NaClNonSfiSandboxSIGSYSTest.inotify_rm_watch
--NaClNonSfiSandboxSIGSYSTest.io_cancel
--NaClNonSfiSandboxSIGSYSTest.io_destroy
--NaClNonSfiSandboxSIGSYSTest.io_getevents
--NaClNonSfiSandboxSIGSYSTest.io_setup
--NaClNonSfiSandboxSIGSYSTest.io_submit
--NaClNonSfiSandboxSIGSYSTest.ioctl
--NaClNonSfiSandboxSIGSYSTest.ioperm
--NaClNonSfiSandboxSIGSYSTest.iopl
--NaClNonSfiSandboxSIGSYSTest.ioprio_get
--NaClNonSfiSandboxSIGSYSTest.ioprio_set
--NaClNonSfiSandboxSIGSYSTest.ipc
--NaClNonSfiSandboxSIGSYSTest.kexec_load
--NaClNonSfiSandboxSIGSYSTest.keyctl
--NaClNonSfiSandboxSIGSYSTest.kill
--NaClNonSfiSandboxSIGSYSTest.lchown
--NaClNonSfiSandboxSIGSYSTest.lchown32
--NaClNonSfiSandboxSIGSYSTest.lgetxattr
--NaClNonSfiSandboxSIGSYSTest.link
--NaClNonSfiSandboxSIGSYSTest.linkat
--NaClNonSfiSandboxSIGSYSTest.listxattr
--NaClNonSfiSandboxSIGSYSTest.llistxattr
--NaClNonSfiSandboxSIGSYSTest.lock
--NaClNonSfiSandboxSIGSYSTest.lookup_dcookie
--NaClNonSfiSandboxSIGSYSTest.lremovexattr
--NaClNonSfiSandboxSIGSYSTest.lseek
--NaClNonSfiSandboxSIGSYSTest.lsetxattr
--NaClNonSfiSandboxSIGSYSTest.lstat
--NaClNonSfiSandboxSIGSYSTest.lstat64
--NaClNonSfiSandboxSIGSYSTest.mbind
--NaClNonSfiSandboxSIGSYSTest.migrate_pages
--NaClNonSfiSandboxSIGSYSTest.mincore
--NaClNonSfiSandboxSIGSYSTest.mkdir
--NaClNonSfiSandboxSIGSYSTest.mkdirat
--NaClNonSfiSandboxSIGSYSTest.mknod
--NaClNonSfiSandboxSIGSYSTest.mknodat
--NaClNonSfiSandboxSIGSYSTest.mlock
--NaClNonSfiSandboxSIGSYSTest.mlockall
--NaClNonSfiSandboxSIGSYSTest.mmap
--NaClNonSfiSandboxSIGSYSTest.modify_ldt
--NaClNonSfiSandboxSIGSYSTest.mount
--NaClNonSfiSandboxSIGSYSTest.move_pages
--NaClNonSfiSandboxSIGSYSTest.mpx
--NaClNonSfiSandboxSIGSYSTest.mq_getsetattr
--NaClNonSfiSandboxSIGSYSTest.mq_notify
--NaClNonSfiSandboxSIGSYSTest.mq_open
--NaClNonSfiSandboxSIGSYSTest.mq_timedreceive
--NaClNonSfiSandboxSIGSYSTest.mq_timedsend
--NaClNonSfiSandboxSIGSYSTest.mq_unlink
--NaClNonSfiSandboxSIGSYSTest.mremap
--NaClNonSfiSandboxSIGSYSTest.msync
--NaClNonSfiSandboxSIGSYSTest.munlock
--NaClNonSfiSandboxSIGSYSTest.munlockall
--NaClNonSfiSandboxSIGSYSTest.name_to_handle_at
--NaClNonSfiSandboxSIGSYSTest.nfsservctl
--NaClNonSfiSandboxSIGSYSTest.nice
--NaClNonSfiSandboxSIGSYSTest.oldfstat
--NaClNonSfiSandboxSIGSYSTest.oldlstat
--NaClNonSfiSandboxSIGSYSTest.oldolduname
--NaClNonSfiSandboxSIGSYSTest.oldstat
--NaClNonSfiSandboxSIGSYSTest.olduname
--NaClNonSfiSandboxSIGSYSTest.open_by_handle_at
--NaClNonSfiSandboxSIGSYSTest.pause
--NaClNonSfiSandboxSIGSYSTest.perf_event_open
--NaClNonSfiSandboxSIGSYSTest.personality
--NaClNonSfiSandboxSIGSYSTest.pipe2
--NaClNonSfiSandboxSIGSYSTest.pivot_root
--NaClNonSfiSandboxSIGSYSTest.ppoll
--NaClNonSfiSandboxSIGSYSTest.preadv
--NaClNonSfiSandboxSIGSYSTest.prlimit64
--NaClNonSfiSandboxSIGSYSTest.process_vm_readv
--NaClNonSfiSandboxSIGSYSTest.process_vm_writev
--NaClNonSfiSandboxSIGSYSTest.prof
--NaClNonSfiSandboxSIGSYSTest.profil
--NaClNonSfiSandboxSIGSYSTest.pselect6
--NaClNonSfiSandboxSIGSYSTest.putpmsg
--NaClNonSfiSandboxSIGSYSTest.pwritev
--NaClNonSfiSandboxSIGSYSTest.query_module
--NaClNonSfiSandboxSIGSYSTest.quotactl
--NaClNonSfiSandboxSIGSYSTest.readahead
--NaClNonSfiSandboxSIGSYSTest.readdir
--NaClNonSfiSandboxSIGSYSTest.readlink
--NaClNonSfiSandboxSIGSYSTest.readlinkat
--NaClNonSfiSandboxSIGSYSTest.readv
--NaClNonSfiSandboxSIGSYSTest.reboot
--NaClNonSfiSandboxSIGSYSTest.recvmmsg
--NaClNonSfiSandboxSIGSYSTest.remap_file_pages
--NaClNonSfiSandboxSIGSYSTest.removexattr
--NaClNonSfiSandboxSIGSYSTest.rename
--NaClNonSfiSandboxSIGSYSTest.renameat
--NaClNonSfiSandboxSIGSYSTest.request_key
--NaClNonSfiSandboxSIGSYSTest.rmdir
--NaClNonSfiSandboxSIGSYSTest.rt_sigaction
--NaClNonSfiSandboxSIGSYSTest.rt_sigpending
--NaClNonSfiSandboxSIGSYSTest.rt_sigprocmask
--NaClNonSfiSandboxSIGSYSTest.rt_sigqueueinfo
--NaClNonSfiSandboxSIGSYSTest.rt_sigreturn
--NaClNonSfiSandboxSIGSYSTest.rt_sigsuspend
--NaClNonSfiSandboxSIGSYSTest.rt_sigtimedwait
--NaClNonSfiSandboxSIGSYSTest.rt_tgsigqueueinfo
--NaClNonSfiSandboxSIGSYSTest.sched_get_priority_max
--NaClNonSfiSandboxSIGSYSTest.sched_get_priority_min
--NaClNonSfiSandboxSIGSYSTest.sched_getaffinity
--NaClNonSfiSandboxSIGSYSTest.sched_getparam
--NaClNonSfiSandboxSIGSYSTest.sched_getscheduler
--NaClNonSfiSandboxSIGSYSTest.sched_rr_get_interval
--NaClNonSfiSandboxSIGSYSTest.sched_setaffinity
--NaClNonSfiSandboxSIGSYSTest.sched_setparam
--NaClNonSfiSandboxSIGSYSTest.sched_setscheduler
--NaClNonSfiSandboxSIGSYSTest.select
--NaClNonSfiSandboxSIGSYSTest.sendfile
--NaClNonSfiSandboxSIGSYSTest.sendfile64
--NaClNonSfiSandboxSIGSYSTest.sendmmsg
--NaClNonSfiSandboxSIGSYSTest.set_mempolicy
--NaClNonSfiSandboxSIGSYSTest.set_thread_area
--NaClNonSfiSandboxSIGSYSTest.set_tid_address
--NaClNonSfiSandboxSIGSYSTest.setdomainname
--NaClNonSfiSandboxSIGSYSTest.setfsgid
--NaClNonSfiSandboxSIGSYSTest.setfsgid32
--NaClNonSfiSandboxSIGSYSTest.setfsuid
--NaClNonSfiSandboxSIGSYSTest.setfsuid32
--NaClNonSfiSandboxSIGSYSTest.setgid
--NaClNonSfiSandboxSIGSYSTest.setgid32
--NaClNonSfiSandboxSIGSYSTest.setgroups
--NaClNonSfiSandboxSIGSYSTest.setgroups32
--NaClNonSfiSandboxSIGSYSTest.sethostname
--NaClNonSfiSandboxSIGSYSTest.setitimer
--NaClNonSfiSandboxSIGSYSTest.setns
--NaClNonSfiSandboxSIGSYSTest.setpgid
--NaClNonSfiSandboxSIGSYSTest.setpriority
--NaClNonSfiSandboxSIGSYSTest.setregid
--NaClNonSfiSandboxSIGSYSTest.setregid32
--NaClNonSfiSandboxSIGSYSTest.setresgid
--NaClNonSfiSandboxSIGSYSTest.setresgid32
--NaClNonSfiSandboxSIGSYSTest.setresuid
--NaClNonSfiSandboxSIGSYSTest.setresuid32
--NaClNonSfiSandboxSIGSYSTest.setreuid
--NaClNonSfiSandboxSIGSYSTest.setreuid32
--NaClNonSfiSandboxSIGSYSTest.setrlimit
--NaClNonSfiSandboxSIGSYSTest.setsid
--NaClNonSfiSandboxSIGSYSTest.settimeofday
--NaClNonSfiSandboxSIGSYSTest.setuid
--NaClNonSfiSandboxSIGSYSTest.setuid32
--NaClNonSfiSandboxSIGSYSTest.setxattr
--NaClNonSfiSandboxSIGSYSTest.sgetmask
--NaClNonSfiSandboxSIGSYSTest.sigaction
--NaClNonSfiSandboxSIGSYSTest.signal
--NaClNonSfiSandboxSIGSYSTest.signalfd
--NaClNonSfiSandboxSIGSYSTest.signalfd4
--NaClNonSfiSandboxSIGSYSTest.sigpending
--NaClNonSfiSandboxSIGSYSTest.sigprocmask
--NaClNonSfiSandboxSIGSYSTest.sigreturn
--NaClNonSfiSandboxSIGSYSTest.sigsuspend
--NaClNonSfiSandboxSIGSYSTest.splice
--NaClNonSfiSandboxSIGSYSTest.ssetmask
--NaClNonSfiSandboxSIGSYSTest.stat
--NaClNonSfiSandboxSIGSYSTest.stat64
--NaClNonSfiSandboxSIGSYSTest.statfs
--NaClNonSfiSandboxSIGSYSTest.statfs64
--NaClNonSfiSandboxSIGSYSTest.stime
--NaClNonSfiSandboxSIGSYSTest.stty
--NaClNonSfiSandboxSIGSYSTest.swapoff
--NaClNonSfiSandboxSIGSYSTest.swapon
--NaClNonSfiSandboxSIGSYSTest.symlink
--NaClNonSfiSandboxSIGSYSTest.symlinkat
--NaClNonSfiSandboxSIGSYSTest.sync
--NaClNonSfiSandboxSIGSYSTest.sync_file_range
--NaClNonSfiSandboxSIGSYSTest.syncfs
--NaClNonSfiSandboxSIGSYSTest.sysfs
--NaClNonSfiSandboxSIGSYSTest.sysinfo
--NaClNonSfiSandboxSIGSYSTest.syslog
--NaClNonSfiSandboxSIGSYSTest.tee
--NaClNonSfiSandboxSIGSYSTest.tgkill
--NaClNonSfiSandboxSIGSYSTest.timer_create
--NaClNonSfiSandboxSIGSYSTest.timer_delete
--NaClNonSfiSandboxSIGSYSTest.timer_getoverrun
--NaClNonSfiSandboxSIGSYSTest.timer_gettime
--NaClNonSfiSandboxSIGSYSTest.timer_settime
--NaClNonSfiSandboxSIGSYSTest.timerfd_create
--NaClNonSfiSandboxSIGSYSTest.timerfd_gettime
--NaClNonSfiSandboxSIGSYSTest.timerfd_settime
--NaClNonSfiSandboxSIGSYSTest.tkill
--NaClNonSfiSandboxSIGSYSTest.truncate
--NaClNonSfiSandboxSIGSYSTest.truncate64
--NaClNonSfiSandboxSIGSYSTest.ugetrlimit
--NaClNonSfiSandboxSIGSYSTest.ulimit
--NaClNonSfiSandboxSIGSYSTest.umask
--NaClNonSfiSandboxSIGSYSTest.umount
--NaClNonSfiSandboxSIGSYSTest.umount2
--NaClNonSfiSandboxSIGSYSTest.uname
--NaClNonSfiSandboxSIGSYSTest.unlink
--NaClNonSfiSandboxSIGSYSTest.unlinkat
--NaClNonSfiSandboxSIGSYSTest.unshare
--NaClNonSfiSandboxSIGSYSTest.uselib
--NaClNonSfiSandboxSIGSYSTest.ustat
--NaClNonSfiSandboxSIGSYSTest.utime
--NaClNonSfiSandboxSIGSYSTest.utimensat
--NaClNonSfiSandboxSIGSYSTest.utimes
--NaClNonSfiSandboxSIGSYSTest.vfork
--NaClNonSfiSandboxSIGSYSTest.vhangup
--NaClNonSfiSandboxSIGSYSTest.vm86
--NaClNonSfiSandboxSIGSYSTest.vm86old
--NaClNonSfiSandboxSIGSYSTest.vmsplice
--NaClNonSfiSandboxSIGSYSTest.vserver
--NaClNonSfiSandboxSIGSYSTest.wait4
--NaClNonSfiSandboxSIGSYSTest.waitid
--NaClNonSfiSandboxSIGSYSTest.waitpid
--NaClNonSfiSandboxSIGSYSTest.writev
--NaClNonSfiSandboxTest.BPFIsSupported
--NaClNonSfiSandboxTest.DoFork
--NaClNonSfiSandboxTest.FutexWithRequeuePriorityInheritence
--NaClNonSfiSandboxTest.FutexWithRequeuePriorityInheritencePrivate
--NaClNonSfiSandboxTest.FutexWithUnlockPIPrivate
--NaClNonSfiSandboxTest.accept
--NaClNonSfiSandboxTest.bind
--NaClNonSfiSandboxTest.brk
--NaClNonSfiSandboxTest.clock_gettime_allowed
--NaClNonSfiSandboxTest.clock_gettime_crash_clock_fd
--NaClNonSfiSandboxTest.clone_by_pthread_create
--NaClNonSfiSandboxTest.clone_for_fork
--NaClNonSfiSandboxTest.connect
--NaClNonSfiSandboxTest.epoll_create_EPERM
--NaClNonSfiSandboxTest.fcntl_DUPFD
--NaClNonSfiSandboxTest.fcntl_DUPFD_CLOEXEC
--NaClNonSfiSandboxTest.fcntl_GETFL_SETFL
--NaClNonSfiSandboxTest.fcntl_GETFL_SETFL_allowed
--NaClNonSfiSandboxTest.fcntl_SETFD
--NaClNonSfiSandboxTest.fcntl_SETFD_allowed
--NaClNonSfiSandboxTest.getegid32_EPERM
--NaClNonSfiSandboxTest.getegid_EPERM
--NaClNonSfiSandboxTest.geteuid32_EPERM
--NaClNonSfiSandboxTest.geteuid_EPERM
--NaClNonSfiSandboxTest.getgid32_EPERM
--NaClNonSfiSandboxTest.getgid_EPERM
--NaClNonSfiSandboxTest.getpeername
--NaClNonSfiSandboxTest.getsockname
--NaClNonSfiSandboxTest.getsockopt
--NaClNonSfiSandboxTest.getuid32_EPERM
--NaClNonSfiSandboxTest.getuid_EPERM
--NaClNonSfiSandboxTest.invalid_syscall_crash
--NaClNonSfiSandboxTest.invalid_sysno
--NaClNonSfiSandboxTest.listen
--NaClNonSfiSandboxTest.madvise_EPERM
--NaClNonSfiSandboxTest.mmap_allowed
--NaClNonSfiSandboxTest.mmap_exec
--NaClNonSfiSandboxTest.mmap_read_exec
--NaClNonSfiSandboxTest.mmap_read_write_exec
--NaClNonSfiSandboxTest.mmap_unallowed_flag
--NaClNonSfiSandboxTest.mmap_unallowed_prot
--NaClNonSfiSandboxTest.mmap_write_exec
--NaClNonSfiSandboxTest.mprotect_allowed
--NaClNonSfiSandboxTest.mprotect_unallowed_prot
--NaClNonSfiSandboxTest.open_EPERM
--NaClNonSfiSandboxTest.openat_EPERM
--NaClNonSfiSandboxTest.prctl_SET_DUMPABLE
--NaClNonSfiSandboxTest.prctl_SET_NAME
--NaClNonSfiSandboxTest.ptrace_EPERM
--NaClNonSfiSandboxTest.random
--NaClNonSfiSandboxTest.recv
--NaClNonSfiSandboxTest.recvfrom
--NaClNonSfiSandboxTest.send
--NaClNonSfiSandboxTest.sendto
--NaClNonSfiSandboxTest.set_robust_list_EPERM
--NaClNonSfiSandboxTest.setsockopt
--NaClNonSfiSandboxTest.socket
--NaClNonSfiSandboxTest.tgkill_with_invalid_signal
--NaClNonSfiSandboxTest.tgkill_with_invalid_tgid
--NaClNonSfiSandboxTest.tgkill_with_invalid_tid
--NaClNonSfiSandboxTest.tgkill_with_negative_tgid
--NaClNonSfiSandboxTest.time_EPERM
--NaClNonsfiSandboxTest.socketpair_af_unix_disallowed
diff --git a/testing/buildbot/filters/stable_test_filters/linux-stable-filter-rel/nacl_helper_nonsfi_unittests.filter b/testing/buildbot/filters/stable_test_filters/linux-stable-filter-rel/nacl_helper_nonsfi_unittests.filter
deleted file mode 100644
index cd002c9..0000000
--- a/testing/buildbot/filters/stable_test_filters/linux-stable-filter-rel/nacl_helper_nonsfi_unittests.filter
+++ /dev/null
@@ -1,362 +0,0 @@
--NaClNonSfiSandboxSIGSYSTest._newselect
--NaClNonSfiSandboxSIGSYSTest._sysctl
--NaClNonSfiSandboxSIGSYSTest.access
--NaClNonSfiSandboxSIGSYSTest.acct
--NaClNonSfiSandboxSIGSYSTest.add_key
--NaClNonSfiSandboxSIGSYSTest.adjtimex
--NaClNonSfiSandboxSIGSYSTest.afs_syscall
--NaClNonSfiSandboxSIGSYSTest.alarm
--NaClNonSfiSandboxSIGSYSTest.bdflush
--NaClNonSfiSandboxSIGSYSTest.break
--NaClNonSfiSandboxSIGSYSTest.capget
--NaClNonSfiSandboxSIGSYSTest.capset
--NaClNonSfiSandboxSIGSYSTest.chdir
--NaClNonSfiSandboxSIGSYSTest.chmod
--NaClNonSfiSandboxSIGSYSTest.chown
--NaClNonSfiSandboxSIGSYSTest.chown32
--NaClNonSfiSandboxSIGSYSTest.chroot
--NaClNonSfiSandboxSIGSYSTest.clock_adjtime
--NaClNonSfiSandboxSIGSYSTest.clock_nanosleep
--NaClNonSfiSandboxSIGSYSTest.clock_settime
--NaClNonSfiSandboxSIGSYSTest.creat
--NaClNonSfiSandboxSIGSYSTest.create_module
--NaClNonSfiSandboxSIGSYSTest.delete_module
--NaClNonSfiSandboxSIGSYSTest.dup3
--NaClNonSfiSandboxSIGSYSTest.epoll_create1
--NaClNonSfiSandboxSIGSYSTest.epoll_ctl
--NaClNonSfiSandboxSIGSYSTest.epoll_pwait
--NaClNonSfiSandboxSIGSYSTest.epoll_wait
--NaClNonSfiSandboxSIGSYSTest.eventfd
--NaClNonSfiSandboxSIGSYSTest.eventfd2
--NaClNonSfiSandboxSIGSYSTest.execve
--NaClNonSfiSandboxSIGSYSTest.faccessat
--NaClNonSfiSandboxSIGSYSTest.fadvise64
--NaClNonSfiSandboxSIGSYSTest.fadvise64_64
--NaClNonSfiSandboxSIGSYSTest.fallocate
--NaClNonSfiSandboxSIGSYSTest.fanotify_init
--NaClNonSfiSandboxSIGSYSTest.fanotify_mark
--NaClNonSfiSandboxSIGSYSTest.fchdir
--NaClNonSfiSandboxSIGSYSTest.fchmod
--NaClNonSfiSandboxSIGSYSTest.fchmodat
--NaClNonSfiSandboxSIGSYSTest.fchown
--NaClNonSfiSandboxSIGSYSTest.fchown32
--NaClNonSfiSandboxSIGSYSTest.fchownat
--NaClNonSfiSandboxSIGSYSTest.fcntl
--NaClNonSfiSandboxSIGSYSTest.fdatasync
--NaClNonSfiSandboxSIGSYSTest.fgetxattr
--NaClNonSfiSandboxSIGSYSTest.flistxattr
--NaClNonSfiSandboxSIGSYSTest.flock
--NaClNonSfiSandboxSIGSYSTest.fork
--NaClNonSfiSandboxSIGSYSTest.fremovexattr
--NaClNonSfiSandboxSIGSYSTest.fsetxattr
--NaClNonSfiSandboxSIGSYSTest.fstat
--NaClNonSfiSandboxSIGSYSTest.fstatat64
--NaClNonSfiSandboxSIGSYSTest.fstatfs
--NaClNonSfiSandboxSIGSYSTest.fstatfs64
--NaClNonSfiSandboxSIGSYSTest.fsync
--NaClNonSfiSandboxSIGSYSTest.ftime
--NaClNonSfiSandboxSIGSYSTest.ftruncate
--NaClNonSfiSandboxSIGSYSTest.ftruncate64
--NaClNonSfiSandboxSIGSYSTest.futimesat
--NaClNonSfiSandboxSIGSYSTest.get_kernel_syms
--NaClNonSfiSandboxSIGSYSTest.get_mempolicy
--NaClNonSfiSandboxSIGSYSTest.get_robust_list
--NaClNonSfiSandboxSIGSYSTest.get_thread_area
--NaClNonSfiSandboxSIGSYSTest.getcpu
--NaClNonSfiSandboxSIGSYSTest.getcwd
--NaClNonSfiSandboxSIGSYSTest.getdents
--NaClNonSfiSandboxSIGSYSTest.getdents64
--NaClNonSfiSandboxSIGSYSTest.getgroups
--NaClNonSfiSandboxSIGSYSTest.getgroups32
--NaClNonSfiSandboxSIGSYSTest.getitimer
--NaClNonSfiSandboxSIGSYSTest.getpgid
--NaClNonSfiSandboxSIGSYSTest.getpgrp
--NaClNonSfiSandboxSIGSYSTest.getpid
--NaClNonSfiSandboxSIGSYSTest.getpmsg
--NaClNonSfiSandboxSIGSYSTest.getppid
--NaClNonSfiSandboxSIGSYSTest.getpriority
--NaClNonSfiSandboxSIGSYSTest.getresgid
--NaClNonSfiSandboxSIGSYSTest.getresgid32
--NaClNonSfiSandboxSIGSYSTest.getresuid
--NaClNonSfiSandboxSIGSYSTest.getresuid32
--NaClNonSfiSandboxSIGSYSTest.getrlimit
--NaClNonSfiSandboxSIGSYSTest.getrusage
--NaClNonSfiSandboxSIGSYSTest.getsid
--NaClNonSfiSandboxSIGSYSTest.getxattr
--NaClNonSfiSandboxSIGSYSTest.gtty
--NaClNonSfiSandboxSIGSYSTest.idle
--NaClNonSfiSandboxSIGSYSTest.init_module
--NaClNonSfiSandboxSIGSYSTest.inotify_add_watch
--NaClNonSfiSandboxSIGSYSTest.inotify_init
--NaClNonSfiSandboxSIGSYSTest.inotify_init1
--NaClNonSfiSandboxSIGSYSTest.inotify_rm_watch
--NaClNonSfiSandboxSIGSYSTest.io_cancel
--NaClNonSfiSandboxSIGSYSTest.io_destroy
--NaClNonSfiSandboxSIGSYSTest.io_getevents
--NaClNonSfiSandboxSIGSYSTest.io_setup
--NaClNonSfiSandboxSIGSYSTest.io_submit
--NaClNonSfiSandboxSIGSYSTest.ioctl
--NaClNonSfiSandboxSIGSYSTest.ioperm
--NaClNonSfiSandboxSIGSYSTest.iopl
--NaClNonSfiSandboxSIGSYSTest.ioprio_get
--NaClNonSfiSandboxSIGSYSTest.ioprio_set
--NaClNonSfiSandboxSIGSYSTest.ipc
--NaClNonSfiSandboxSIGSYSTest.kexec_load
--NaClNonSfiSandboxSIGSYSTest.keyctl
--NaClNonSfiSandboxSIGSYSTest.kill
--NaClNonSfiSandboxSIGSYSTest.lchown
--NaClNonSfiSandboxSIGSYSTest.lchown32
--NaClNonSfiSandboxSIGSYSTest.lgetxattr
--NaClNonSfiSandboxSIGSYSTest.link
--NaClNonSfiSandboxSIGSYSTest.linkat
--NaClNonSfiSandboxSIGSYSTest.listxattr
--NaClNonSfiSandboxSIGSYSTest.llistxattr
--NaClNonSfiSandboxSIGSYSTest.lock
--NaClNonSfiSandboxSIGSYSTest.lookup_dcookie
--NaClNonSfiSandboxSIGSYSTest.lremovexattr
--NaClNonSfiSandboxSIGSYSTest.lseek
--NaClNonSfiSandboxSIGSYSTest.lsetxattr
--NaClNonSfiSandboxSIGSYSTest.lstat
--NaClNonSfiSandboxSIGSYSTest.lstat64
--NaClNonSfiSandboxSIGSYSTest.mbind
--NaClNonSfiSandboxSIGSYSTest.migrate_pages
--NaClNonSfiSandboxSIGSYSTest.mincore
--NaClNonSfiSandboxSIGSYSTest.mkdir
--NaClNonSfiSandboxSIGSYSTest.mkdirat
--NaClNonSfiSandboxSIGSYSTest.mknod
--NaClNonSfiSandboxSIGSYSTest.mknodat
--NaClNonSfiSandboxSIGSYSTest.mlock
--NaClNonSfiSandboxSIGSYSTest.mlockall
--NaClNonSfiSandboxSIGSYSTest.mmap
--NaClNonSfiSandboxSIGSYSTest.modify_ldt
--NaClNonSfiSandboxSIGSYSTest.mount
--NaClNonSfiSandboxSIGSYSTest.move_pages
--NaClNonSfiSandboxSIGSYSTest.mpx
--NaClNonSfiSandboxSIGSYSTest.mq_getsetattr
--NaClNonSfiSandboxSIGSYSTest.mq_notify
--NaClNonSfiSandboxSIGSYSTest.mq_open
--NaClNonSfiSandboxSIGSYSTest.mq_timedreceive
--NaClNonSfiSandboxSIGSYSTest.mq_timedsend
--NaClNonSfiSandboxSIGSYSTest.mq_unlink
--NaClNonSfiSandboxSIGSYSTest.mremap
--NaClNonSfiSandboxSIGSYSTest.msync
--NaClNonSfiSandboxSIGSYSTest.munlock
--NaClNonSfiSandboxSIGSYSTest.munlockall
--NaClNonSfiSandboxSIGSYSTest.name_to_handle_at
--NaClNonSfiSandboxSIGSYSTest.nfsservctl
--NaClNonSfiSandboxSIGSYSTest.nice
--NaClNonSfiSandboxSIGSYSTest.oldfstat
--NaClNonSfiSandboxSIGSYSTest.oldlstat
--NaClNonSfiSandboxSIGSYSTest.oldolduname
--NaClNonSfiSandboxSIGSYSTest.oldstat
--NaClNonSfiSandboxSIGSYSTest.olduname
--NaClNonSfiSandboxSIGSYSTest.open_by_handle_at
--NaClNonSfiSandboxSIGSYSTest.pause
--NaClNonSfiSandboxSIGSYSTest.perf_event_open
--NaClNonSfiSandboxSIGSYSTest.personality
--NaClNonSfiSandboxSIGSYSTest.pipe2
--NaClNonSfiSandboxSIGSYSTest.pivot_root
--NaClNonSfiSandboxSIGSYSTest.ppoll
--NaClNonSfiSandboxSIGSYSTest.preadv
--NaClNonSfiSandboxSIGSYSTest.prlimit64
--NaClNonSfiSandboxSIGSYSTest.process_vm_readv
--NaClNonSfiSandboxSIGSYSTest.process_vm_writev
--NaClNonSfiSandboxSIGSYSTest.prof
--NaClNonSfiSandboxSIGSYSTest.profil
--NaClNonSfiSandboxSIGSYSTest.pselect6
--NaClNonSfiSandboxSIGSYSTest.putpmsg
--NaClNonSfiSandboxSIGSYSTest.pwritev
--NaClNonSfiSandboxSIGSYSTest.query_module
--NaClNonSfiSandboxSIGSYSTest.quotactl
--NaClNonSfiSandboxSIGSYSTest.readahead
--NaClNonSfiSandboxSIGSYSTest.readdir
--NaClNonSfiSandboxSIGSYSTest.readlink
--NaClNonSfiSandboxSIGSYSTest.readlinkat
--NaClNonSfiSandboxSIGSYSTest.readv
--NaClNonSfiSandboxSIGSYSTest.reboot
--NaClNonSfiSandboxSIGSYSTest.recvmmsg
--NaClNonSfiSandboxSIGSYSTest.remap_file_pages
--NaClNonSfiSandboxSIGSYSTest.removexattr
--NaClNonSfiSandboxSIGSYSTest.rename
--NaClNonSfiSandboxSIGSYSTest.renameat
--NaClNonSfiSandboxSIGSYSTest.request_key
--NaClNonSfiSandboxSIGSYSTest.rmdir
--NaClNonSfiSandboxSIGSYSTest.rt_sigaction
--NaClNonSfiSandboxSIGSYSTest.rt_sigpending
--NaClNonSfiSandboxSIGSYSTest.rt_sigprocmask
--NaClNonSfiSandboxSIGSYSTest.rt_sigqueueinfo
--NaClNonSfiSandboxSIGSYSTest.rt_sigreturn
--NaClNonSfiSandboxSIGSYSTest.rt_sigsuspend
--NaClNonSfiSandboxSIGSYSTest.rt_sigtimedwait
--NaClNonSfiSandboxSIGSYSTest.rt_tgsigqueueinfo
--NaClNonSfiSandboxSIGSYSTest.sched_get_priority_max
--NaClNonSfiSandboxSIGSYSTest.sched_get_priority_min
--NaClNonSfiSandboxSIGSYSTest.sched_getaffinity
--NaClNonSfiSandboxSIGSYSTest.sched_getparam
--NaClNonSfiSandboxSIGSYSTest.sched_getscheduler
--NaClNonSfiSandboxSIGSYSTest.sched_rr_get_interval
--NaClNonSfiSandboxSIGSYSTest.sched_setaffinity
--NaClNonSfiSandboxSIGSYSTest.sched_setparam
--NaClNonSfiSandboxSIGSYSTest.sched_setscheduler
--NaClNonSfiSandboxSIGSYSTest.select
--NaClNonSfiSandboxSIGSYSTest.sendfile
--NaClNonSfiSandboxSIGSYSTest.sendfile64
--NaClNonSfiSandboxSIGSYSTest.sendmmsg
--NaClNonSfiSandboxSIGSYSTest.set_mempolicy
--NaClNonSfiSandboxSIGSYSTest.set_thread_area
--NaClNonSfiSandboxSIGSYSTest.set_tid_address
--NaClNonSfiSandboxSIGSYSTest.setdomainname
--NaClNonSfiSandboxSIGSYSTest.setfsgid
--NaClNonSfiSandboxSIGSYSTest.setfsgid32
--NaClNonSfiSandboxSIGSYSTest.setfsuid
--NaClNonSfiSandboxSIGSYSTest.setfsuid32
--NaClNonSfiSandboxSIGSYSTest.setgid
--NaClNonSfiSandboxSIGSYSTest.setgid32
--NaClNonSfiSandboxSIGSYSTest.setgroups
--NaClNonSfiSandboxSIGSYSTest.setgroups32
--NaClNonSfiSandboxSIGSYSTest.sethostname
--NaClNonSfiSandboxSIGSYSTest.setitimer
--NaClNonSfiSandboxSIGSYSTest.setns
--NaClNonSfiSandboxSIGSYSTest.setpgid
--NaClNonSfiSandboxSIGSYSTest.setpriority
--NaClNonSfiSandboxSIGSYSTest.setregid
--NaClNonSfiSandboxSIGSYSTest.setregid32
--NaClNonSfiSandboxSIGSYSTest.setresgid
--NaClNonSfiSandboxSIGSYSTest.setresgid32
--NaClNonSfiSandboxSIGSYSTest.setresuid
--NaClNonSfiSandboxSIGSYSTest.setresuid32
--NaClNonSfiSandboxSIGSYSTest.setreuid
--NaClNonSfiSandboxSIGSYSTest.setreuid32
--NaClNonSfiSandboxSIGSYSTest.setrlimit
--NaClNonSfiSandboxSIGSYSTest.setsid
--NaClNonSfiSandboxSIGSYSTest.settimeofday
--NaClNonSfiSandboxSIGSYSTest.setuid
--NaClNonSfiSandboxSIGSYSTest.setuid32
--NaClNonSfiSandboxSIGSYSTest.setxattr
--NaClNonSfiSandboxSIGSYSTest.sgetmask
--NaClNonSfiSandboxSIGSYSTest.sigaction
--NaClNonSfiSandboxSIGSYSTest.signal
--NaClNonSfiSandboxSIGSYSTest.signalfd
--NaClNonSfiSandboxSIGSYSTest.signalfd4
--NaClNonSfiSandboxSIGSYSTest.sigpending
--NaClNonSfiSandboxSIGSYSTest.sigprocmask
--NaClNonSfiSandboxSIGSYSTest.sigreturn
--NaClNonSfiSandboxSIGSYSTest.sigsuspend
--NaClNonSfiSandboxSIGSYSTest.splice
--NaClNonSfiSandboxSIGSYSTest.ssetmask
--NaClNonSfiSandboxSIGSYSTest.stat
--NaClNonSfiSandboxSIGSYSTest.stat64
--NaClNonSfiSandboxSIGSYSTest.statfs
--NaClNonSfiSandboxSIGSYSTest.statfs64
--NaClNonSfiSandboxSIGSYSTest.stime
--NaClNonSfiSandboxSIGSYSTest.stty
--NaClNonSfiSandboxSIGSYSTest.swapoff
--NaClNonSfiSandboxSIGSYSTest.swapon
--NaClNonSfiSandboxSIGSYSTest.symlink
--NaClNonSfiSandboxSIGSYSTest.symlinkat
--NaClNonSfiSandboxSIGSYSTest.sync
--NaClNonSfiSandboxSIGSYSTest.sync_file_range
--NaClNonSfiSandboxSIGSYSTest.syncfs
--NaClNonSfiSandboxSIGSYSTest.sysfs
--NaClNonSfiSandboxSIGSYSTest.sysinfo
--NaClNonSfiSandboxSIGSYSTest.syslog
--NaClNonSfiSandboxSIGSYSTest.tee
--NaClNonSfiSandboxSIGSYSTest.tgkill
--NaClNonSfiSandboxSIGSYSTest.timer_create
--NaClNonSfiSandboxSIGSYSTest.timer_delete
--NaClNonSfiSandboxSIGSYSTest.timer_getoverrun
--NaClNonSfiSandboxSIGSYSTest.timer_gettime
--NaClNonSfiSandboxSIGSYSTest.timer_settime
--NaClNonSfiSandboxSIGSYSTest.timerfd_create
--NaClNonSfiSandboxSIGSYSTest.timerfd_gettime
--NaClNonSfiSandboxSIGSYSTest.timerfd_settime
--NaClNonSfiSandboxSIGSYSTest.tkill
--NaClNonSfiSandboxSIGSYSTest.truncate
--NaClNonSfiSandboxSIGSYSTest.truncate64
--NaClNonSfiSandboxSIGSYSTest.ugetrlimit
--NaClNonSfiSandboxSIGSYSTest.ulimit
--NaClNonSfiSandboxSIGSYSTest.umask
--NaClNonSfiSandboxSIGSYSTest.umount
--NaClNonSfiSandboxSIGSYSTest.umount2
--NaClNonSfiSandboxSIGSYSTest.uname
--NaClNonSfiSandboxSIGSYSTest.unlink
--NaClNonSfiSandboxSIGSYSTest.unlinkat
--NaClNonSfiSandboxSIGSYSTest.unshare
--NaClNonSfiSandboxSIGSYSTest.uselib
--NaClNonSfiSandboxSIGSYSTest.ustat
--NaClNonSfiSandboxSIGSYSTest.utime
--NaClNonSfiSandboxSIGSYSTest.utimensat
--NaClNonSfiSandboxSIGSYSTest.utimes
--NaClNonSfiSandboxSIGSYSTest.vfork
--NaClNonSfiSandboxSIGSYSTest.vhangup
--NaClNonSfiSandboxSIGSYSTest.vm86
--NaClNonSfiSandboxSIGSYSTest.vm86old
--NaClNonSfiSandboxSIGSYSTest.vmsplice
--NaClNonSfiSandboxSIGSYSTest.vserver
--NaClNonSfiSandboxSIGSYSTest.wait4
--NaClNonSfiSandboxSIGSYSTest.waitid
--NaClNonSfiSandboxSIGSYSTest.waitpid
--NaClNonSfiSandboxSIGSYSTest.writev
--NaClNonSfiSandboxTest.BPFIsSupported
--NaClNonSfiSandboxTest.DoFork
--NaClNonSfiSandboxTest.FutexWithRequeuePriorityInheritence
--NaClNonSfiSandboxTest.FutexWithRequeuePriorityInheritencePrivate
--NaClNonSfiSandboxTest.FutexWithUnlockPIPrivate
--NaClNonSfiSandboxTest.accept
--NaClNonSfiSandboxTest.bind
--NaClNonSfiSandboxTest.brk
--NaClNonSfiSandboxTest.clock_gettime_allowed
--NaClNonSfiSandboxTest.clock_gettime_crash_clock_fd
--NaClNonSfiSandboxTest.clone_by_pthread_create
--NaClNonSfiSandboxTest.clone_for_fork
--NaClNonSfiSandboxTest.connect
--NaClNonSfiSandboxTest.epoll_create_EPERM
--NaClNonSfiSandboxTest.fcntl_DUPFD
--NaClNonSfiSandboxTest.fcntl_DUPFD_CLOEXEC
--NaClNonSfiSandboxTest.fcntl_GETFL_SETFL
--NaClNonSfiSandboxTest.fcntl_GETFL_SETFL_allowed
--NaClNonSfiSandboxTest.fcntl_SETFD
--NaClNonSfiSandboxTest.fcntl_SETFD_allowed
--NaClNonSfiSandboxTest.getegid32_EPERM
--NaClNonSfiSandboxTest.getegid_EPERM
--NaClNonSfiSandboxTest.geteuid32_EPERM
--NaClNonSfiSandboxTest.geteuid_EPERM
--NaClNonSfiSandboxTest.getgid32_EPERM
--NaClNonSfiSandboxTest.getgid_EPERM
--NaClNonSfiSandboxTest.getpeername
--NaClNonSfiSandboxTest.getsockname
--NaClNonSfiSandboxTest.getsockopt
--NaClNonSfiSandboxTest.getuid32_EPERM
--NaClNonSfiSandboxTest.getuid_EPERM
--NaClNonSfiSandboxTest.invalid_syscall_crash
--NaClNonSfiSandboxTest.invalid_sysno
--NaClNonSfiSandboxTest.listen
--NaClNonSfiSandboxTest.madvise_EPERM
--NaClNonSfiSandboxTest.mmap_allowed
--NaClNonSfiSandboxTest.mmap_exec
--NaClNonSfiSandboxTest.mmap_read_exec
--NaClNonSfiSandboxTest.mmap_read_write_exec
--NaClNonSfiSandboxTest.mmap_unallowed_flag
--NaClNonSfiSandboxTest.mmap_unallowed_prot
--NaClNonSfiSandboxTest.mmap_write_exec
--NaClNonSfiSandboxTest.mprotect_allowed
--NaClNonSfiSandboxTest.mprotect_unallowed_prot
--NaClNonSfiSandboxTest.open_EPERM
--NaClNonSfiSandboxTest.openat_EPERM
--NaClNonSfiSandboxTest.prctl_SET_DUMPABLE
--NaClNonSfiSandboxTest.prctl_SET_NAME
--NaClNonSfiSandboxTest.ptrace_EPERM
--NaClNonSfiSandboxTest.random
--NaClNonSfiSandboxTest.recv
--NaClNonSfiSandboxTest.recvfrom
--NaClNonSfiSandboxTest.send
--NaClNonSfiSandboxTest.sendto
--NaClNonSfiSandboxTest.set_robust_list_EPERM
--NaClNonSfiSandboxTest.setsockopt
--NaClNonSfiSandboxTest.socket
--NaClNonSfiSandboxTest.tgkill_with_invalid_signal
--NaClNonSfiSandboxTest.tgkill_with_invalid_tgid
--NaClNonSfiSandboxTest.tgkill_with_invalid_tid
--NaClNonSfiSandboxTest.tgkill_with_negative_tgid
--NaClNonSfiSandboxTest.time_EPERM
--NaClNonsfiSandboxTest.socketpair_af_unix_disallowed
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index acfefe2..a515996 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -2397,6 +2397,14 @@
     ],
   },
   'perfetto_unittests': {
+    'modifications': {
+      'android-12-x64-fyi-rel': {
+        'args': [
+          # TODO(crbug.com/1260440): Fix the failed test
+          '--gtest_filter=-ScopedDirTest.CloseOutOfScope',
+        ],
+      },
+    },
     'remove_from': [
       # TODO(crbug.com/931138): Fix permission issue when creating tmp files
       'android-arm64-proguard-rel',
@@ -3205,6 +3213,7 @@
   },
   'webgl2_conformance_metal_passthrough_tests': {
     'remove_from': [
+      'Mac FYI ASAN (Intel)', # crbug.com/1270755
       # Not enough capacity.
       'Mac FYI Retina Release (NVIDIA)',
     ],
@@ -3256,6 +3265,7 @@
   },
   'webgl_conformance_metal_passthrough_tests': {
     'remove_from': [
+      'Mac FYI ASAN (Intel)', # crbug.com/1270755
       # crbug.com/1158857: re-enable when switching to Metal by default.
       'Mac FYI Retina Release (NVIDIA)',
     ],
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index b97a1109..3a221c77 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -4048,9 +4048,6 @@
           '--flag-specific=webgpu-with-backend-validation',
           # Increase the timeout when using backend validation layers (crbug.com/1208253)
           '--time-out-ms=30000',
-          # TODO(crbug.com/1274975): Remove this once loading on newer NVIDIA
-          # GPUs is fixed.
-          '--additional-driver-flag=--disable-gpu-sandbox',
         ],
         'win64_args': [ '--target=Release_x64' ],
         'mac_args': [
@@ -4083,9 +4080,6 @@
           # crbug.com/953991 Ensure WebGPU is ready before running tests
           '--initialize-webgpu-adapter-at-startup-timeout-ms=60000',
           '--flag-specific=webgpu',
-          # TODO(crbug.com/1274975): Remove this once loading on newer NVIDIA
-          # GPUs is fixed.
-          '--additional-driver-flag=--disable-gpu-sandbox',
         ],
         'win64_args': [ '--target=Release_x64' ],
         'mac_args': [
@@ -4122,9 +4116,6 @@
           '--flag-specific=webgpu-with-partial-backend-validation',
           # Increase the timeout when using backend validation layers (crbug.com/1208253)
           '--time-out-ms=30000',
-          # TODO(crbug.com/1274975): Remove this once loading on newer NVIDIA
-          # GPUs is fixed.
-          '--additional-driver-flag=--disable-gpu-sandbox',
         ],
         'win64_args': [ '--target=Release_x64' ],
         'mac_args': [
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 574cebb3..72fe812 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2418,6 +2418,11 @@
            'gtest_tests': 'chromium_swarm_desktop_gtests',
         },
       },
+      'win11-rel-swarming': {
+        'test_suites': {
+           'gtest_tests': 'chromium_swarm_desktop_gtests',
+        },
+      },
     },
   },
   {
diff --git a/testing/scripts/wpt_common.py b/testing/scripts/wpt_common.py
index b002d99..a6e66b4 100644
--- a/testing/scripts/wpt_common.py
+++ b/testing/scripts/wpt_common.py
@@ -293,6 +293,8 @@
         unexpected = result_node['actual'] not in result_node['expected']
 
         for artifact_name, paths in result_node.get('artifacts', {}).items():
+            if artifact_name in ["wpt_actual_status", "wpt_subtest_failure"]:
+                continue
             for path in paths:
                 artifacts.AddArtifact(artifact_name, path)
 
@@ -313,7 +315,7 @@
         test_path = test_name[:index] if index != -1 else test_name
 
         self.sink.report_individual_test_result(
-            test_name, result, self.layout_test_results_subdir,
+            test_name, result, os.path.dirname(self.wpt_output),
             None, os.path.join(WEB_TESTS_DIR, test_path))
 
     def _maybe_write_expected_output(self, results_dir, test_name):
diff --git a/testing/scripts/wpt_common_unittest.py b/testing/scripts/wpt_common_unittest.py
index da83eb6..9a4b81f 100755
--- a/testing/scripts/wpt_common_unittest.py
+++ b/testing/scripts/wpt_common_unittest.py
@@ -122,8 +122,7 @@
         self._create_json_output(json_dict)
         self.wpt_adapter.do_post_test_run_tasks()
 
-        baseline_artifacts = {'wpt_actual_status': ['OK'],
-                              'actual_text': [
+        baseline_artifacts = {'actual_text': [
                                   (os.path.join('layout-test-results',
                                                 'external', 'wpt', 'fail',
                                                 'test_variant1-actual.txt'))]}
@@ -156,8 +155,7 @@
                                      'external', 'wpt', 'fail', 'test.html')
         self._create_json_output(json_dict)
         self.wpt_adapter.do_post_test_run_tasks()
-        baseline_artifacts = {'wpt_actual_status': ['OK'],
-                              'actual_text': [
+        baseline_artifacts = {'actual_text': [
                                   (os.path.join('layout-test-results',
                                                 'external', 'wpt', 'fail',
                                                 'test_variant1-actual.txt'))]}
@@ -190,8 +188,7 @@
         self.wpt_adapter.do_post_test_run_tasks()
         test_abs_path = os.path.join(WEB_TESTS_DIR,
                                      'external', 'wpt', 'fail', 'test.html')
-        baseline_artifacts = {'wpt_actual_status': ['OK'],
-                              'actual_text': [
+        baseline_artifacts = {'actual_text': [
                                   os.path.join('layout-test-results',
                                                'external', 'wpt', 'fail',
                                                'test-actual.txt')]}
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 0e2c75d1..46b7d1c 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -541,6 +541,21 @@
             ]
         }
     ],
+    "AndroidMessagesPWAInstall": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "InstallableAmbientBadgeMessage"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidMessagesPermissionUpdate": [
         {
             "platforms": [
@@ -7307,19 +7322,21 @@
                 {
                     "name": "Enabled_Snooze_1_week_20211208",
                     "params": {
-                        "IPH_DownloadHome_availability": ">=0",
+                        "IPH_DownloadHome_availability": ">=14",
                         "IPH_DownloadHome_event_1": "name:download_completed;comparator:>=1;window:90;storage:360",
                         "IPH_DownloadHome_event_trigger": "name:download_home_iph_trigger;comparator:==0;window:90;storage:360",
                         "IPH_DownloadHome_event_used": "name:download_home_opened;comparator:==0;window:90;storage:360",
                         "IPH_DownloadHome_session_rate": "==0",
-                        "IPH_TabSwitcherButton_availability": ">=0",
+                        "IPH_TabSwitcherButton_availability": ">=14",
                         "IPH_TabSwitcherButton_event_trigger": "name:tab_switcher_iph_triggered;comparator:==0;window:90;storage:90",
                         "IPH_TabSwitcherButton_event_used": "name:tab_switcher_button_clicked;comparator:==0;window:14;storage:90",
                         "IPH_TabSwitcherButton_session_rate": "<1",
                         "snooze_params": "max_limit:3,snooze_interval:7"
                     },
                     "enable_features": [
-                        "IPH_Snooze"
+                        "IPH_DownloadHome",
+                        "IPH_Snooze",
+                        "IPH_TabSwitcherButton"
                     ],
                     "disable_features": [
                         "EnableAutomaticSnooze"
diff --git a/third_party/blink/public/mojom/webid/federated_auth_request.mojom b/third_party/blink/public/mojom/webid/federated_auth_request.mojom
index fd33a7c2..11ffe04 100644
--- a/third_party/blink/public/mojom/webid/federated_auth_request.mojom
+++ b/third_party/blink/public/mojom/webid/federated_auth_request.mojom
@@ -21,6 +21,7 @@
   kErrorInvalidSigninResponse,
   kErrorInvalidAccountsResponse,
   kErrorInvalidTokenResponse,
+  kErrorCanceled,
   kError,
 };
 
@@ -68,6 +69,9 @@
                  bool prefer_auto_sign_in) =>
       (RequestIdTokenStatus status, string? id_token);
 
+  // Cancels the pending token request, if any.
+  CancelTokenRequest();
+
   // Revokes a token for the specified |account_id| from |provider| for
   // the RP identified by |client_id|.
   Revoke(url.mojom.Url provider,
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.cc b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.cc
index 8200831..ca5c3c74 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.cc
@@ -19,6 +19,12 @@
       return SerializedColorSpace::kRec2020;
     case PredefinedColorSpace::kP3:
       return SerializedColorSpace::kP3;
+    case PredefinedColorSpace::kRec2100HLG:
+      return SerializedColorSpace::kRec2100HLG;
+    case PredefinedColorSpace::kRec2100PQ:
+      return SerializedColorSpace::kRec2100PQ;
+    case PredefinedColorSpace::kSRGBLinear:
+      return SerializedColorSpace::kSRGBLinear;
   }
   NOTREACHED();
   return SerializedColorSpace::kSRGB;
@@ -34,6 +40,12 @@
       return PredefinedColorSpace::kRec2020;
     case SerializedColorSpace::kP3:
       return PredefinedColorSpace::kP3;
+    case SerializedColorSpace::kRec2100HLG:
+      return PredefinedColorSpace::kRec2100HLG;
+    case SerializedColorSpace::kRec2100PQ:
+      return PredefinedColorSpace::kRec2100PQ;
+    case SerializedColorSpace::kSRGBLinear:
+      return PredefinedColorSpace::kSRGBLinear;
   }
   NOTREACHED();
   return PredefinedColorSpace::kSRGB;
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.h b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.h
index 03bed60..07a65faa 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.h
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.h
@@ -41,7 +41,10 @@
   kSRGB = 1,
   kRec2020 = 2,
   kP3 = 3,
-  kLast = kP3,
+  kRec2100HLG = 4,
+  kRec2100PQ = 5,
+  kSRGBLinear = 6,
+  kLast = kSRGBLinear,
 };
 
 // This enumeration specifies the values used to serialize CanvasPixelFormat.
diff --git a/third_party/blink/renderer/core/frame/window_or_worker_global_scope.cc b/third_party/blink/renderer/core/frame/window_or_worker_global_scope.cc
index 22a03df..c278f90 100644
--- a/third_party/blink/renderer/core/frame/window_or_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/frame/window_or_worker_global_scope.cc
@@ -97,6 +97,7 @@
 void WindowOrWorkerGlobalScope::reportError(ScriptState* script_state,
                                             EventTarget& event_target,
                                             const ScriptValue& e) {
+  ScriptState::Scope scope(script_state);
   V8ScriptRunner::ReportException(script_state->GetIsolate(), e.V8Value());
 }
 
@@ -232,6 +233,7 @@
     const ScriptValue& message,
     const StructuredSerializeOptions* options,
     ExceptionState& exception_state) {
+  ScriptState::Scope scope(script_state);
   v8::Isolate* isolate = script_state->GetIsolate();
 
   Transferables transferables;
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
index 5d418b8..83d0d6b 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
@@ -3534,10 +3534,29 @@
         if (!MovePastBreakpoint(ConstraintSpace(), grid_item.node, *result,
                                 fragment_relative_block_offset, appeal_before,
                                 /* builder */ nullptr)) {
-          // TODO(ikilpatrick): We may have break-before:avoid on this row, we
-          // should search upwards (ensuring that we are still in this
-          // fragmentainer), for the first row with the highest break appeal.
           breakpoint_row_set_index = item_row_set_index;
+
+          // We may have "break-before:avoid" or similar on this row. Instead
+          // of just breaking on this row, search upwards for a row with a
+          // better EBreakBetween.
+          if (IsAvoidBreakValue(ConstraintSpace(), break_between)) {
+            for (int index = item_row_set_index - 1; index >= 0; --index) {
+              // Only consider rows within this fragmentainer.
+              LayoutUnit offset =
+                  grid_geometry->row_geometry.sets[index].offset +
+                  (*row_offset_adjustments)[index] -
+                  previous_consumed_block_size;
+              if (offset <= LayoutUnit())
+                break;
+
+              // Forced row breaks should have been already handled, accept any
+              // row with an "auto" break-between.
+              if (row_break_between[index] == EBreakBetween::kAuto) {
+                breakpoint_row_set_index = index;
+                break;
+              }
+            }
+          }
           continue;
         }
       }
diff --git a/third_party/blink/renderer/core/timing/window_performance.cc b/third_party/blink/renderer/core/timing/window_performance.cc
index d5dbd493..8a9285e 100644
--- a/third_party/blink/renderer/core/timing/window_performance.cc
+++ b/third_party/blink/renderer/core/timing/window_performance.cc
@@ -496,13 +496,17 @@
     }
 
     if (!first_input_timing_) {
-      if (entry->name() == "pointerdown") {
+      if (entry->name() == event_type_names::kPointerdown) {
         first_pointer_down_event_timing_ =
             PerformanceEventTiming::CreateFirstInputTiming(entry);
-      } else if (entry->name() == "pointerup") {
+      } else if (entry->name() == event_type_names::kPointerup) {
         DispatchFirstInputTiming(first_pointer_down_event_timing_);
-      } else if (entry->name() == "click" || entry->name() == "keydown" ||
-                 entry->name() == "mousedown") {
+      } else if (entry->name() == event_type_names::kPointercancel) {
+        first_pointer_down_event_timing_.Clear();
+      } else if ((entry->name() == event_type_names::kMousedown ||
+                  entry->name() == event_type_names::kClick ||
+                  entry->name() == event_type_names::kKeydown) &&
+                 !first_pointer_down_event_timing_) {
         DispatchFirstInputTiming(
             PerformanceEventTiming::CreateFirstInputTiming(entry));
       }
diff --git a/third_party/blink/renderer/core/timing/window_performance_test.cc b/third_party/blink/renderer/core/timing/window_performance_test.cc
index 7fd9558..31e35ca 100644
--- a/third_party/blink/renderer/core/timing/window_performance_test.cc
+++ b/third_party/blink/renderer/core/timing/window_performance_test.cc
@@ -464,6 +464,46 @@
       1u, performance_->getEntriesByName("pointerdown", "first-input").size());
 }
 
+// When the pointerdown is optimized out, the mousedown works as a
+// 'first-input'.
+TEST_F(WindowPerformanceTest, PointerdownOptimizedOut) {
+  base::TimeTicks start_time = GetTimeStamp(0);
+  base::TimeTicks processing_start = GetTimeStamp(1);
+  base::TimeTicks processing_end = GetTimeStamp(2);
+  base::TimeTicks swap_time = GetTimeStamp(3);
+  RegisterPointerEvent("mousedown", start_time, processing_start,
+                       processing_end, 4);
+  SimulateSwapPromise(swap_time);
+  EXPECT_EQ(1u, performance_->getEntriesByType("first-input").size());
+  // The name of the entry should be "pointerdown".
+  EXPECT_EQ(1u,
+            performance_->getEntriesByName("mousedown", "first-input").size());
+}
+
+// Test that pointerdown followed by mousedown, pointerup works as a
+// 'first-input'.
+TEST_F(WindowPerformanceTest, PointerdownOnDesktop) {
+  base::TimeTicks start_time = GetTimeStamp(0);
+  base::TimeTicks processing_start = GetTimeStamp(1);
+  base::TimeTicks processing_end = GetTimeStamp(2);
+  base::TimeTicks swap_time = GetTimeStamp(3);
+  RegisterPointerEvent("pointerdown", start_time, processing_start,
+                       processing_end, 4);
+  SimulateSwapPromise(swap_time);
+  EXPECT_EQ(0u, performance_->getEntriesByType("first-input").size());
+  RegisterPointerEvent("mousedown", start_time, processing_start,
+                       processing_end, 4);
+  SimulateSwapPromise(swap_time);
+  EXPECT_EQ(0u, performance_->getEntriesByType("first-input").size());
+  RegisterPointerEvent("pointerup", start_time, processing_start,
+                       processing_end, 4);
+  SimulateSwapPromise(swap_time);
+  EXPECT_EQ(1u, performance_->getEntriesByType("first-input").size());
+  // The name of the entry should be "pointerdown".
+  EXPECT_EQ(
+      1u, performance_->getEntriesByName("pointerdown", "first-input").size());
+}
+
 TEST_F(WindowPerformanceTest, OneKeyboardInteraction) {
   base::TimeTicks keydown_timestamp = GetTimeStamp(0);
   // Keydown
diff --git a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
index 17317b5f..214b3a50 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
@@ -481,6 +481,16 @@
   webotp_service->Abort();
 }
 
+// Abort an ongoing FederatedCredential get() operation.
+void AbortFederatedCredentialRequest(ScriptState* script_state) {
+  if (!script_state->ContextIsValid())
+    return;
+
+  auto* fedcm_get_request =
+      CredentialManagerProxy::From(script_state)->FedCmGetRequest();
+  fedcm_get_request->CancelTokenRequest();
+}
+
 void OnStoreComplete(std::unique_ptr<ScopedPromiseResolver> scoped_resolver) {
   auto* resolver = scoped_resolver->Release();
   AssertSecurityRequirementsBeforeResponse(
@@ -886,6 +896,11 @@
           "Provider's token response is invalid"));
       return;
     }
+    case RequestIdTokenStatus::kErrorCanceled: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kAbortError, "Request has been aborted"));
+      return;
+    }
     case RequestIdTokenStatus::kError: {
       resolver->Reject(MakeGarbageCollected<DOMException>(
           DOMExceptionCode::kNetworkError, "Error retrieving an id token."));
@@ -1151,6 +1166,15 @@
           return promise;
         }
         DCHECK(options->federated()->hasPreferAutoSignIn());
+        if (options->hasSignal()) {
+          if (options->signal()->aborted()) {
+            resolver->Reject(MakeGarbageCollected<DOMException>(
+                DOMExceptionCode::kAbortError, "Request has been aborted."));
+            return promise;
+          }
+          options->signal()->AddAlgorithm(WTF::Bind(
+              &AbortFederatedCredentialRequest, WrapPersistent(script_state)));
+        }
         bool prefer_auto_sign_in = options->federated()->preferAutoSignIn();
         auto* fedcm_get_request =
             CredentialManagerProxy::From(script_state)->FedCmGetRequest();
diff --git a/third_party/blink/renderer/platform/graphics/canvas_color_params.cc b/third_party/blink/renderer/platform/graphics/canvas_color_params.cc
index 07ebc38..f4d6686 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_color_params.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_color_params.cc
@@ -29,6 +29,14 @@
                              gfx::ColorSpace::TransferID::GAMMA24);
     case PredefinedColorSpace::kP3:
       return gfx::ColorSpace::CreateDisplayP3D65();
+    case PredefinedColorSpace::kRec2100HLG:
+      return gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT2020,
+                             gfx::ColorSpace::TransferID::ARIB_STD_B67);
+    case PredefinedColorSpace::kRec2100PQ:
+      return gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT2020,
+                             gfx::ColorSpace::TransferID::SMPTEST2084);
+    case PredefinedColorSpace::kSRGBLinear:
+      return gfx::ColorSpace::CreateSCRGBLinear();
   }
   NOTREACHED();
 }
@@ -46,9 +54,9 @@
   // |sk_color_space| does not exactly match one of the named color spaces. It
   // should find the best named match.
   PredefinedColorSpace color_spaces[] = {
-      PredefinedColorSpace::kSRGB,
-      PredefinedColorSpace::kRec2020,
-      PredefinedColorSpace::kP3,
+      PredefinedColorSpace::kSRGB,      PredefinedColorSpace::kRec2020,
+      PredefinedColorSpace::kP3,        PredefinedColorSpace::kRec2100HLG,
+      PredefinedColorSpace::kRec2100PQ, PredefinedColorSpace::kSRGBLinear,
   };
   for (const auto& color_space : color_spaces) {
     if (SkColorSpace::Equals(sk_color_space,
diff --git a/third_party/blink/renderer/platform/graphics/graphics_types.cc b/third_party/blink/renderer/platform/graphics/graphics_types.cc
index d352097..4bf5aa1 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_types.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_types.cc
@@ -239,6 +239,12 @@
       return "rec2020";
     case PredefinedColorSpace::kP3:
       return "display-p3";
+    case PredefinedColorSpace::kRec2100HLG:
+      return "rec2100-hlg";
+    case PredefinedColorSpace::kRec2100PQ:
+      return "rec2100-pq";
+    case PredefinedColorSpace::kSRGBLinear:
+      return "srgb-linear";
   };
   NOTREACHED();
   return String();
@@ -257,6 +263,18 @@
     format = PredefinedColorSpace::kP3;
     return true;
   }
+  if (s == "rec2100-hlg") {
+    format = PredefinedColorSpace::kRec2100HLG;
+    return true;
+  }
+  if (s == "rec2100-pq") {
+    format = PredefinedColorSpace::kRec2100PQ;
+    return true;
+  }
+  if (s == "srgb-linear") {
+    format = PredefinedColorSpace::kSRGBLinear;
+    return true;
+  }
   return false;
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/graphics_types.h b/third_party/blink/renderer/platform/graphics/graphics_types.h
index 0d3371bd..80f065d8 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_types.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_types.h
@@ -47,6 +47,9 @@
   kSRGB,
   kRec2020,
   kP3,
+  kRec2100HLG,
+  kRec2100PQ,
+  kSRGBLinear,
 };
 
 enum DataU8ColorType {
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 39ab0a7..9275f08a 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1293,6 +1293,8 @@
 virtual/layout_ng_grid_frag/external/wpt/css/css-break/grid/grid-item-fragmentation-032.html [ Pass ]
 virtual/layout_ng_grid_frag/external/wpt/css/css-break/grid/grid-item-fragmentation-033.html [ Pass ]
 virtual/layout_ng_grid_frag/external/wpt/css/css-break/grid/grid-item-fragmentation-034.html [ Pass ]
+virtual/layout_ng_grid_frag/external/wpt/css/css-break/grid/grid-item-fragmentation-037.html [ Pass ]
+virtual/layout_ng_grid_frag/external/wpt/css/css-break/grid/grid-item-fragmentation-038.html [ Pass ]
 
 ### With LayoutNGPrinting enabled:
 
@@ -4288,6 +4290,8 @@
 crbug.com/614667 external/wpt/css/css-break/grid/grid-item-fragmentation-032.html [ Failure ]
 crbug.com/614667 external/wpt/css/css-break/grid/grid-item-fragmentation-033.html [ Failure ]
 crbug.com/614667 external/wpt/css/css-break/grid/grid-item-fragmentation-034.html [ Failure ]
+crbug.com/614667 external/wpt/css/css-break/grid/grid-item-fragmentation-037.html [ Failure ]
+crbug.com/614667 external/wpt/css/css-break/grid/grid-item-fragmentation-038.html [ Failure ]
 crbug.com/1066629 external/wpt/css/css-break/hit-test-transformed.html [ Failure ]
 crbug.com/1058792 external/wpt/css/css-break/transform-007.html [ Failure ]
 crbug.com/1224888 external/wpt/css/css-break/transform-009.html [ Failure ]
diff --git a/third_party/blink/web_tests/android/ChromeWPTOverrideExpectations b/third_party/blink/web_tests/android/ChromeWPTOverrideExpectations
index 2e4796a7..661b3d25 100644
--- a/third_party/blink/web_tests/android/ChromeWPTOverrideExpectations
+++ b/third_party/blink/web_tests/android/ChromeWPTOverrideExpectations
@@ -429,14 +429,14 @@
 crbug.com/1050754 external/wpt/bluetooth/requestDevice/request-from-sandboxed-iframe.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/requestDevice/same-device.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/requestDevice/single-filter-single-service.https.window.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/connect/connection-succeeds.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/connect/garbage-collection-ran-during-success.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/connect/get-same-gatt-server.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/device-same-object.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/disconnect/connect-disconnect-twice.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/disconnect/detach-gc.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/disconnect/disconnect-twice-in-a-row.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/disconnect/gc-detach.https.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/connect/connection-succeeds.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/connect/garbage-collection-ran-during-success.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/connect/get-same-gatt-server.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/device-same-object.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/disconnect/connect-disconnect-twice.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/disconnect/detach-gc.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/disconnect/disconnect-twice-in-a-row.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/disconnect/gc-detach.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-disconnect-called-before.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-disconnect-called-during-error.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-disconnect-called-during-success.https.window.html [ Failure ]
@@ -453,11 +453,11 @@
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-no-permission-present-service.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-service-not-found.https.window.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/service-found.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/correct-services.https.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/service-found.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/correct-services.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-disconnect-called-before-with-uuid.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-disconnect-called-before.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error-with-uuid.https.window.html [ Failure ]
@@ -484,9 +484,9 @@
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-service-not-found-with-uuid.https.window.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-found-with-uuid.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-found.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-not-found.https.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-found-with-uuid.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-found.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-not-found.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/service/device-same-from-2-services.https.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/service/device-same-object.https.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/service/getCharacteristic/characteristic-found.https.html [ Failure ]
diff --git a/third_party/blink/web_tests/android/WebLayerWPTOverrideExpectations b/third_party/blink/web_tests/android/WebLayerWPTOverrideExpectations
index bf596e78..c95e7b1c 100644
--- a/third_party/blink/web_tests/android/WebLayerWPTOverrideExpectations
+++ b/third_party/blink/web_tests/android/WebLayerWPTOverrideExpectations
@@ -498,14 +498,14 @@
 crbug.com/1050754 external/wpt/bluetooth/requestDevice/request-from-sandboxed-iframe.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/requestDevice/same-device.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/requestDevice/single-filter-single-service.https.window.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/connect/connection-succeeds.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/connect/garbage-collection-ran-during-success.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/connect/get-same-gatt-server.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/device-same-object.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/disconnect/connect-disconnect-twice.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/disconnect/detach-gc.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/disconnect/disconnect-twice-in-a-row.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/disconnect/gc-detach.https.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/connect/connection-succeeds.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/connect/garbage-collection-ran-during-success.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/connect/get-same-gatt-server.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/device-same-object.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/disconnect/connect-disconnect-twice.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/disconnect/detach-gc.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/disconnect/disconnect-twice-in-a-row.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/disconnect/gc-detach.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-disconnect-called-before.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-disconnect-called-during-error.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-disconnect-called-during-success.https.window.html [ Failure ]
@@ -522,11 +522,11 @@
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-no-permission-present-service.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-service-not-found.https.window.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/service-found.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/correct-services.https.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/service-found.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/correct-services.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-disconnect-called-before-with-uuid.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-disconnect-called-before.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error-with-uuid.https.window.html [ Failure ]
@@ -553,9 +553,9 @@
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-service-not-found-with-uuid.https.window.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-found-with-uuid.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-found.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-not-found.https.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-found-with-uuid.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-found.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-not-found.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/service/device-same-from-2-services.https.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/service/device-same-object.https.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/service/getCharacteristic/characteristic-found.https.html [ Failure ]
diff --git a/third_party/blink/web_tests/android/WebviewWPTExpectations b/third_party/blink/web_tests/android/WebviewWPTExpectations
index 854b1c1..3ebd097 100644
--- a/third_party/blink/web_tests/android/WebviewWPTExpectations
+++ b/third_party/blink/web_tests/android/WebviewWPTExpectations
@@ -387,14 +387,14 @@
 crbug.com/1050754 external/wpt/bluetooth/requestDevice/request-from-sandboxed-iframe.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/requestDevice/same-device.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/requestDevice/single-filter-single-service.https.window.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/connect/connection-succeeds.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/connect/garbage-collection-ran-during-success.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/connect/get-same-gatt-server.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/device-same-object.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/disconnect/connect-disconnect-twice.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/disconnect/detach-gc.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/disconnect/disconnect-twice-in-a-row.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/disconnect/gc-detach.https.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/connect/connection-succeeds.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/connect/garbage-collection-ran-during-success.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/connect/get-same-gatt-server.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/device-same-object.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/disconnect/connect-disconnect-twice.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/disconnect/detach-gc.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/disconnect/disconnect-twice-in-a-row.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/disconnect/gc-detach.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-disconnect-called-before.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-disconnect-called-during-error.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-disconnect-called-during-success.https.window.html [ Failure ]
@@ -411,11 +411,11 @@
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-no-permission-present-service.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/gen-service-not-found.https.window.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/service-found.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/correct-services.https.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/service-found.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/correct-services.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-disconnect-called-before-with-uuid.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-disconnect-called-before.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error-with-uuid.https.window.html [ Failure ]
@@ -442,9 +442,9 @@
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/gen-service-not-found-with-uuid.https.window.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-found-with-uuid.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-found.https.html [ Failure ]
-crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-not-found.https.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-found-with-uuid.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-found.https.window.html [ Failure ]
+crbug.com/1050754 external/wpt/bluetooth/server/getPrimaryServices/services-not-found.https.window.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/service/device-same-from-2-services.https.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/service/device-same-object.https.html [ Failure ]
 crbug.com/1050754 external/wpt/bluetooth/service/getCharacteristic/characteristic-found.https.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/connection-succeeds.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/connection-succeeds.https.html
deleted file mode 100644
index 1326b07..0000000
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/connection-succeeds.https.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/bluetooth/resources/bluetooth-test.js"></script>
-<script src="/bluetooth/resources/bluetooth-fake-devices.js"></script>
-<script>
-'use strict';
-const test_desc = 'Device will connect';
-let device, fake_peripheral;
-
-bluetooth_test(() => getDiscoveredHealthThermometerDevice()
-    .then(_ => ({device, fake_peripheral} = _))
-    .then(() =>
-      fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS}))
-    .then(() => device.gatt.connect())
-    .then(gatt => assert_true(gatt.connected)),
-    test_desc);
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/connection-succeeds.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/connection-succeeds.https.window.js
new file mode 100644
index 0000000..d78d2613
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/connection-succeeds.https.window.js
@@ -0,0 +1,15 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Device will connect';
+
+bluetooth_test(async () => {
+  let {device, fake_peripheral} = await getDiscoveredHealthThermometerDevice();
+  await fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS});
+  let gatt = await device.gatt.connect();
+  assert_true(gatt.connected);
+}, test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/garbage-collection-ran-during-success.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/garbage-collection-ran-during-success.https.html
deleted file mode 100644
index d9c07b6..0000000
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/garbage-collection-ran-during-success.https.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/bluetooth/resources/bluetooth-test.js"></script>
-<script src="/bluetooth/resources/bluetooth-fake-devices.js"></script>
-<script>
-'use strict';
-const test_desc = 'Garbage Collection ran during a connect call that ' +
-    'succeeds. Should not crash.';
-let device, fake_peripheral;
-
-bluetooth_test(() => getDiscoveredHealthThermometerDevice()
-    .then(_ => ({device, fake_peripheral} = _))
-    .then(() =>
-      fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS}))
-    // Don't return the promise and let |device| go out of scope
-    // so that it gets garbage collected.
-    .then(() => device.gatt.connect())
-    .then(runGarbageCollection),
-    test_desc);
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/garbage-collection-ran-during-success.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/garbage-collection-ran-during-success.https.window.js
new file mode 100644
index 0000000..1a4ad471
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/garbage-collection-ran-during-success.https.window.js
@@ -0,0 +1,20 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Garbage Collection ran during a connect call that ' +
+    'succeeds. Should not crash.';
+
+bluetooth_test(async () => {
+  let connectPromise;
+  {
+    let {device, fake_peripheral} =
+        await getDiscoveredHealthThermometerDevice();
+    await fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS});
+    connectPromise = device.gatt.connect();
+  }
+  await Promise.all([connectPromise, runGarbageCollection()]);
+}, test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/get-same-gatt-server.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/get-same-gatt-server.https.html
deleted file mode 100644
index 5e93838..0000000
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/get-same-gatt-server.https.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/bluetooth/resources/bluetooth-test.js"></script>
-<script src="/bluetooth/resources/bluetooth-fake-devices.js"></script>
-<script>
-'use strict';
-const test_desc = 'Multiple connects should return the same gatt object.';
-let device, fake_peripheral;
-
-bluetooth_test(() => getDiscoveredHealthThermometerDevice()
-    .then(_ => ({device, fake_peripheral} = _))
-    .then(() =>
-      fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS}))
-    .then(() => device.gatt.connect())
-    // No second response is necessary because an ATT Bearer
-    // already exists from the first connection.
-    // See https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect
-    // step 5.1.
-    .then(gatt1 => device.gatt.connect()
-        .then(gatt2 => [gatt1, gatt2]))
-    .then(([gatt1, gatt2]) => assert_equals(gatt1, gatt2)),
-    test_desc);
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/get-same-gatt-server.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/get-same-gatt-server.https.window.js
new file mode 100644
index 0000000..a962d9d2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/connect/get-same-gatt-server.https.window.js
@@ -0,0 +1,21 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Multiple connects should return the same gatt object.';
+
+bluetooth_test(async () => {
+  let {device, fake_peripheral} = await getDiscoveredHealthThermometerDevice();
+  await fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS});
+  // No second response is necessary because an ATT Bearer
+  // already exists from the first connection.
+  // See
+  // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect
+  // step 5.1.
+  let gatt1 = await device.gatt.connect();
+  let gatt2 = await device.gatt.connect();
+  assert_equals(gatt1, gatt2);
+}, test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/device-same-object.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/device-same-object.https.html
deleted file mode 100644
index b861040b..0000000
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/device-same-object.https.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/bluetooth/resources/bluetooth-test.js"></script>
-<script src="/bluetooth/resources/bluetooth-fake-devices.js"></script>
-<script>
-'use strict';
-const test_desc = '[SameObject] test for BluetoothRemoteGATTServer\'s device.';
-let device, fake_peripheral;
-
-bluetooth_test(
-    () => getDiscoveredHealthThermometerDevice()
-              .then(_ => ({device, fake_peripheral} = _))
-              .then(
-                  () => fake_peripheral.setNextGATTConnectionResponse(
-                      {code: HCI_SUCCESS}))
-              .then(() => device.gatt.connect())
-              .then(gatt => assert_equals(gatt.device, gatt.device)),
-    test_desc);
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/device-same-object.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/server/device-same-object.https.window.js
new file mode 100644
index 0000000..1c10a3448
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/device-same-object.https.window.js
@@ -0,0 +1,15 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = '[SameObject] test for BluetoothRemoteGATTServer\'s device.';
+
+bluetooth_test(async () => {
+  let {device, fake_peripheral} = await getDiscoveredHealthThermometerDevice();
+  await fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS});
+  let gatt = await device.gatt.connect();
+  assert_equals(gatt.device, device);
+}, test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/connect-disconnect-twice.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/connect-disconnect-twice.https.html
deleted file mode 100644
index 7ed2cb9..0000000
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/connect-disconnect-twice.https.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/bluetooth/resources/bluetooth-test.js"></script>
-<script src="/bluetooth/resources/bluetooth-fake-devices.js"></script>
-<script>
-'use strict';
-const test_desc = 'Connect + Disconnect twice still results in ' +
-  '\'connected\' being false.';
-let device, fake_peripheral;
-
-// TODO(569716): Test that the disconnect signal was sent to the device.
-bluetooth_test(() => getDiscoveredHealthThermometerDevice()
-    .then(_ => ({device, fake_peripheral} = _))
-    .then(() => fake_peripheral.setNextGATTConnectionResponse({
-      code: HCI_SUCCESS,
-    }))
-    .then(() => device.gatt.connect()
-    .then(gattServer => {
-      gattServer.disconnect();
-      assert_false(gattServer.connected);
-    })
-    .then(() => device.gatt.connect())
-    .then(gattServer => {
-      gattServer.disconnect();
-      assert_false(gattServer.connected);
-    })), test_desc);
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/connect-disconnect-twice.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/connect-disconnect-twice.https.window.js
new file mode 100644
index 0000000..4d832f2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/connect-disconnect-twice.https.window.js
@@ -0,0 +1,25 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Connect + Disconnect twice still results in ' +
+    '\'connected\' being false.';
+let device, fake_peripheral;
+
+// TODO(569716): Test that the disconnect signal was sent to the device.
+bluetooth_test(async () => {
+  let {device, fake_peripheral} = await getDiscoveredHealthThermometerDevice();
+  await fake_peripheral.setNextGATTConnectionResponse({
+    code: HCI_SUCCESS,
+  });
+  let gattServer = await device.gatt.connect();
+  await gattServer.disconnect();
+  assert_false(gattServer.connected);
+
+  gattServer = await device.gatt.connect();
+  await gattServer.disconnect();
+  assert_false(gattServer.connected);
+}, test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/detach-gc.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/detach-gc.https.html
deleted file mode 100644
index c1863e923..0000000
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/detach-gc.https.html
+++ /dev/null
@@ -1,37 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/bluetooth/resources/bluetooth-test.js"></script>
-<script src="/bluetooth/resources/bluetooth-fake-devices.js"></script>
-<body>
-<script>
-'use strict';
-const test_desc = 'Detach frame then garbage collect. We shouldn\'t crash.';
-let iframe = document.createElement('iframe');
-
-bluetooth_test(() => setUpConnectableHealthThermometerDevice()
-    // 1. Load the iframe.
-    .then(() => new Promise(resolve => {
-      iframe.src = '/bluetooth/resources/health-thermometer-iframe.html';
-      document.body.appendChild(iframe);
-      iframe.addEventListener('load', resolve);
-    }))
-    // 2. Connect device, detach the iframe, and run garbage collection.
-    .then(() => new Promise(resolve => {
-      callWithTrustedClick(() => {
-        iframe.contentWindow.postMessage({
-          type: 'RequestAndConnect',
-          options: {filters: [{services: ['health_thermometer']}]}
-        }, '*');
-      });
-
-      window.onmessage = messageEvent => {
-        assert_equals(messageEvent.data, 'Connected');
-        iframe.remove();
-        runGarbageCollection().then(resolve);
-      }
-    })), test_desc)
-</script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/detach-gc.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/detach-gc.https.window.js
new file mode 100644
index 0000000..7495c9d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/detach-gc.https.window.js
@@ -0,0 +1,35 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Detach frame then garbage collect. We shouldn\'t crash.';
+let iframe = document.createElement('iframe');
+
+bluetooth_test(async () => {
+  await setUpConnectableHealthThermometerDevice();
+  // 1. Load the iframe.
+  await new Promise(resolve => {
+    iframe.src = '/bluetooth/resources/health-thermometer-iframe.html';
+    document.body.appendChild(iframe);
+    iframe.addEventListener('load', resolve);
+  });
+  // 2. Connect device, detach the iframe, and run garbage collection.
+  await new Promise(resolve => {
+    callWithTrustedClick(() => {
+      iframe.contentWindow.postMessage(
+          {
+            type: 'RequestAndConnect',
+            options: {filters: [{services: ['health_thermometer']}]}
+          },
+          '*');
+    });
+    window.onmessage = messageEvent => {
+      assert_equals(messageEvent.data, 'Connected');
+      iframe.remove();
+      runGarbageCollection().then(resolve);
+    }
+  })
+}, test_desc)
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/disconnect-twice-in-a-row.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/disconnect-twice-in-a-row.https.html
deleted file mode 100644
index d4d6e2f..0000000
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/disconnect-twice-in-a-row.https.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/bluetooth/resources/bluetooth-test.js"></script>
-<script src="/bluetooth/resources/bluetooth-fake-devices.js"></script>
-<script>
-'use strict';
-const test_desc = 'Calling disconnect twice in a row still results in ' +
-  '\'connected\' being false.';
-let device, fake_peripheral;
-
-// TODO(569716): Test that the disconnect signal was sent to the device.
-bluetooth_test(() => getDiscoveredHealthThermometerDevice()
-    .then(_ => ({device, fake_peripheral} = _))
-    .then(() => fake_peripheral.setNextGATTConnectionResponse({
-      code: HCI_SUCCESS,
-    }))
-    .then(() => device.gatt.connect())
-    .then(gattServer => {
-      gattServer.disconnect();
-      assert_false(gattServer.connected);
-      gattServer.disconnect();
-      assert_false(gattServer.connected);
-    }), test_desc);
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/disconnect-twice-in-a-row.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/disconnect-twice-in-a-row.https.window.js
new file mode 100644
index 0000000..6f40bf3b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/disconnect-twice-in-a-row.https.window.js
@@ -0,0 +1,22 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Calling disconnect twice in a row still results in ' +
+    '\'connected\' being false.';
+
+// TODO(569716): Test that the disconnect signal was sent to the device.
+bluetooth_test(async () => {
+  let {device, fake_peripheral} = await getDiscoveredHealthThermometerDevice();
+  await fake_peripheral.setNextGATTConnectionResponse({
+    code: HCI_SUCCESS,
+  });
+  let gattServer = await device.gatt.connect();
+  await gattServer.disconnect();
+  assert_false(gattServer.connected);
+  await gattServer.disconnect();
+  assert_false(gattServer.connected);
+}, test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/gc-detach.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/gc-detach.https.html
deleted file mode 100644
index 41d2b15..0000000
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/gc-detach.https.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/bluetooth/resources/bluetooth-test.js"></script>
-<script src="/bluetooth/resources/bluetooth-fake-devices.js"></script>
-<body>
-<script>
-'use strict';
-const test_desc = 'Garbage collect then detach frame. We shouldn\'t crash.';
-let iframe = document.createElement('iframe');
-
-bluetooth_test(() => setUpConnectableHealthThermometerDevice()
-    // 1. Load the iframe.
-    .then((f) => new Promise(resolve => {
-      iframe.src = '/bluetooth/resources/health-thermometer-iframe.html';
-      document.body.appendChild(iframe);
-      iframe.addEventListener('load', resolve);
-    }))
-    // 2. Connect device, run garbage collection, and detach iframe.
-    .then(() => new Promise(resolve => {
-      callWithTrustedClick(() => {
-        iframe.contentWindow.postMessage({
-          type: 'RequestAndConnect',
-          options: {filters: [{services: ['health_thermometer']}]}
-        }, '*');
-      });
-
-      window.onmessage = messageEvent => {
-        assert_equals(messageEvent.data, 'Connected');
-        runGarbageCollection().then(() => {
-          iframe.remove();
-          resolve();
-        });
-      }
-    })), test_desc)
-</script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/gc-detach.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/gc-detach.https.window.js
new file mode 100644
index 0000000..cb0899bb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/disconnect/gc-detach.https.window.js
@@ -0,0 +1,37 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Garbage collect then detach frame. We shouldn\'t crash.';
+let iframe = document.createElement('iframe');
+
+bluetooth_test(async () => {
+  await setUpConnectableHealthThermometerDevice();
+  // 1. Load the iframe.
+  await new Promise(resolve => {
+    iframe.src = '/bluetooth/resources/health-thermometer-iframe.html';
+    document.body.appendChild(iframe);
+    iframe.addEventListener('load', resolve);
+  });
+  // 2. Connect device, run garbage collection, and detach iframe.
+  await new Promise(resolve => {
+    callWithTrustedClick(() => {
+      iframe.contentWindow.postMessage(
+          {
+            type: 'RequestAndConnect',
+            options: {filters: [{services: ['health_thermometer']}]}
+          },
+          '*');
+    });
+    window.onmessage = messageEvent => {
+      assert_equals(messageEvent.data, 'Connected');
+      runGarbageCollection().then(() => {
+        iframe.remove();
+        resolve();
+      });
+    }
+  })
+}, test_desc)
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryService/service-found.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryService/service-found.https.html
deleted file mode 100644
index 6484b822..0000000
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryService/service-found.https.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/bluetooth/resources/bluetooth-test.js"></script>
-<script src="/bluetooth/resources/bluetooth-fake-devices.js"></script>
-<script>
-'use strict';
-const test_desc = 'Request for service. Should return right service';
-let device;
-
-bluetooth_test(() => getHealthThermometerDevice({
-  filters: [{services: ['health_thermometer']}],
-  optionalServices: ['generic_access']
-})
-    .then(_ => ({device} = _))
-    .then(() => Promise.all([
-      device.gatt.getPrimaryService(generic_access.alias),
-      device.gatt.getPrimaryService(generic_access.name),
-      device.gatt.getPrimaryService(generic_access.uuid)]))
-    .then(services => {
-      services.forEach(service => {
-        assert_equals(service.uuid, generic_access.uuid,
-            'Service UUID should be the same as requested UUID.');
-        assert_true(service.isPrimary,
-            'getPrimaryService should return a primary service.');
-        assert_equals(service.device, device,
-            'Service device should be the same as device.');
-      })
-    }), test_desc);
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryService/service-found.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryService/service-found.https.window.js
new file mode 100644
index 0000000..fe700d6e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryService/service-found.https.window.js
@@ -0,0 +1,30 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Request for service. Should return right service';
+
+bluetooth_test(async () => {
+  let {device} = await getHealthThermometerDevice({
+    filters: [{services: ['health_thermometer']}],
+    optionalServices: ['generic_access']
+  });
+  let services = await Promise.all([
+    device.gatt.getPrimaryService(generic_access.alias),
+    device.gatt.getPrimaryService(generic_access.name),
+    device.gatt.getPrimaryService(generic_access.uuid)
+  ]);
+  services.forEach(service => {
+    assert_equals(
+        service.uuid, generic_access.uuid,
+        'Service UUID should be the same as requested UUID.');
+    assert_true(
+        service.isPrimary,
+        'getPrimaryService should return a primary service.');
+    assert_equals(
+        service.device, device, 'Service device should be the same as device.');
+  })
+}, test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.html
deleted file mode 100644
index 064d8d2..0000000
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.html
+++ /dev/null
@@ -1,81 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/bluetooth/resources/bluetooth-test.js"></script>
-<script src="/bluetooth/resources/bluetooth-fake-devices.js"></script>
-<script>
-"use strict";
-const test_desc = 'Two iframes in the same origin should be able to access ' +
-    'each other\'s services';
-
-const iframe1 = document.createElement('iframe');
-const iframe2 = document.createElement('iframe');
-
-function add_iframe(iframe) {
-  let promise = new Promise(resolve => iframe.addEventListener('load', resolve));
-  iframe.src = '/bluetooth/resources/health-thermometer-iframe.html';
-  document.body.appendChild(iframe);
-  return promise;
-}
-
-function send_message(iframe, command, arg, assert_func) {
-  let promise = new Promise((resolve, reject) => {
-    window.addEventListener('message', (messageEvent) => {
-      try {
-        assert_func(messageEvent.data);
-      } catch (e) {
-        reject(e);
-      }
-      resolve();
-    }, { once: true });
-  });
-  if (command === 'RequestAndConnect') {
-    arg = {filters: [{services: [arg]}]};
-  }
-  callWithTrustedClick(() => iframe.contentWindow.postMessage({
-      type: command,
-      options: arg,
-    }, '*'));
-  return promise;
-}
-
-bluetooth_test(() => getHealthThermometerDevice()
-    // 1. Add the first iframe.
-    .then(() => add_iframe(iframe1))
-    // 2. Connect with the first iframe, requesting the health thermometer
-    // service.
-    .then(() => send_message(iframe1, 'RequestAndConnect', 'health_thermometer',
-        msg => assert_equals(msg, 'Connected')))
-    // 3. Access the health thermometer service with the first iframe
-    // (successfully).
-    .then(() => send_message(iframe1, 'GetService', 'health_thermometer',
-        msg => assert_equals(msg, 'ServiceReceived')))
-    // 4. Access the generic access service with the first iframe
-    // (unsuccessfully).
-    .then(() => send_message(iframe1, 'GetService', 'generic_access', msg => {
-        let split_msg = msg.split(': ');
-        assert_equals(split_msg[0], 'FAIL');
-        assert_equals(split_msg[1], 'SecurityError');
-    }))
-    // 5. Add the second iframe.
-    .then(() => add_iframe(iframe2))
-    // 6. Connect with the second iframe, requesting the generic access service.
-    .then(() => send_message(iframe2, 'RequestAndConnect', 'generic_access',
-        msg => assert_equals(msg, 'Connected')))
-    // 7. Access the health thermometer service with the second iframe
-    // (successfully).  Both iframes should have access to both services at this
-    // point since they have the same origin.
-    .then(() => send_message(iframe2, 'GetService', 'health_thermometer',
-        msg => assert_equals(msg, 'ServiceReceived')))
-    // 8. Access the generic access service with the second iframe
-    // (unsuccessfully).
-    .then(() => send_message(iframe2, 'GetService', 'generic_access',
-        msg => assert_equals(msg, 'ServiceReceived')))
-    // 9. Access the generic access service with the first iframe
-    // (successfully).
-    .then(() => send_message(iframe1, 'GetService', 'generic_access',
-        msg => assert_equals(msg, 'ServiceReceived'))),
-    test_desc);
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.window.js
new file mode 100644
index 0000000..cbb3c19
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.window.js
@@ -0,0 +1,90 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Two iframes in the same origin should be able to access ' +
+    'each other\'s services';
+
+const iframe1 = document.createElement('iframe');
+const iframe2 = document.createElement('iframe');
+
+function add_iframe(iframe) {
+  let promise =
+      new Promise(resolve => iframe.addEventListener('load', resolve));
+  iframe.src = '/bluetooth/resources/health-thermometer-iframe.html';
+  document.body.appendChild(iframe);
+  return promise;
+}
+
+function send_message(iframe, command, arg, assert_func) {
+  let promise = new Promise((resolve, reject) => {
+    window.addEventListener('message', (messageEvent) => {
+      try {
+        assert_func(messageEvent.data);
+      } catch (e) {
+        reject(e);
+      }
+      resolve();
+    }, {once: true});
+  });
+  if (command === 'RequestAndConnect') {
+    arg = {filters: [{services: [arg]}]};
+  }
+  callWithTrustedClick(
+      () => iframe.contentWindow.postMessage(
+          {
+            type: command,
+            options: arg,
+          },
+          '*'));
+  return promise;
+}
+
+bluetooth_test(async () => {
+  await getHealthThermometerDevice();
+  // 1. Add the first iframe.
+  await add_iframe(iframe1);
+  // 2. Connect with the first iframe, requesting the health
+  // thermometer service.
+  await send_message(
+      iframe1, 'RequestAndConnect', 'health_thermometer',
+      msg => assert_equals(msg, 'Connected'));
+  // 3. Access the health thermometer service with the first iframe
+  // (successfully).
+  await send_message(
+      iframe1, 'GetService', 'health_thermometer',
+      msg => assert_equals(msg, 'ServiceReceived'));
+  // 4. Access the generic access service with the first iframe
+  // (unsuccessfully).
+  await send_message(iframe1, 'GetService', 'generic_access', msg => {
+    let split_msg = msg.split(': ');
+    assert_equals(split_msg[0], 'FAIL');
+    assert_equals(split_msg[1], 'SecurityError');
+  });
+  // 5. Add the second iframe.
+  await add_iframe(iframe2);
+  // 6. Connect with the second iframe, requesting the generic
+  // access service.
+  await send_message(
+      iframe2, 'RequestAndConnect', 'generic_access',
+      msg => assert_equals(msg, 'Connected'));
+  // 7. Access the health thermometer service with the second iframe
+  // (successfully).  Both iframes should have access to both
+  // services at this point since they have the same origin.
+  await send_message(
+      iframe2, 'GetService', 'health_thermometer',
+      msg => assert_equals(msg, 'ServiceReceived'));
+  // 8. Access the generic access service with the second iframe
+  // (unsuccessfully).
+  await send_message(
+      iframe2, 'GetService', 'generic_access',
+      msg => assert_equals(msg, 'ServiceReceived'));
+  // 9. Access the generic access service with the first iframe
+  // (successfully).
+  await send_message(
+      iframe1, 'GetService', 'generic_access',
+      msg => assert_equals(msg, 'ServiceReceived'));
+}, test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.html
deleted file mode 100644
index 90d776c7..0000000
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/bluetooth/resources/bluetooth-test.js"></script>
-<script src="/bluetooth/resources/bluetooth-fake-devices.js"></script>
-<script>
-'use strict';
-const test_desc = 'Request for services. Does not return blocklisted service.';
-const expected = new DOMException(
-    'Origin is not allowed to access the service. Tip: Add the service ' +
-    'UUID to \'optionalServices\' in requestDevice() options. ' +
-    'https://goo.gl/HxfxSQ', 'SecurityError');
-
-bluetooth_test(() => getConnectedHIDDevice({
-  filters: [{services: ['device_information']}],
-  optionalServices: ['human_interface_device']
-})
-    .then(({device}) => assert_promise_rejects_with_message(
-        device.gatt.getPrimaryServices('human_interface_device'),
-        expected)),
-    test_desc);
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.window.js
new file mode 100644
index 0000000..5d57e9dc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.window.js
@@ -0,0 +1,22 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Request for services. Does not return blocklisted service.';
+const expected = new DOMException(
+    'Origin is not allowed to access the service. Tip: Add the service ' +
+        'UUID to \'optionalServices\' in requestDevice() options. ' +
+        'https://goo.gl/HxfxSQ',
+    'SecurityError');
+
+bluetooth_test(async () => {
+  let {device} = await getConnectedHIDDevice({
+    filters: [{services: ['device_information']}],
+    optionalServices: ['human_interface_device']
+  });
+  assert_promise_rejects_with_message(
+      device.gatt.getPrimaryServices('human_interface_device'), expected)
+}, test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services.https.html
deleted file mode 100644
index df83dfe..0000000
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services.https.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/bluetooth/resources/bluetooth-test.js"></script>
-<script src="/bluetooth/resources/bluetooth-fake-devices.js"></script>
-<script>
-'use strict';
-const test_desc = 'Request for services. Does not return blocklisted service.';
-
-bluetooth_test(() => getHIDDevice({
-  filters: [{services: ['device_information']}],
-  optionalServices: ['generic_access', 'human_interface_device']
-})
-    .then(({device}) => device.gatt.getPrimaryServices())
-    .then(services => {
-      assert_equals(services.length, 2);
-      let uuid_set = new Set(services.map(s => s.uuid));
-
-      assert_equals(uuid_set.size, 2);
-      assert_true(uuid_set.has(BluetoothUUID.getService('generic_access')));
-      assert_true(uuid_set.has(BluetoothUUID.getService('device_information')));
-      assert_false(
-          uuid_set.has(BluetoothUUID.getService('human_interface_device')));
-    }), test_desc);
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services.https.window.js
new file mode 100644
index 0000000..4dc78bf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/blocklisted-services.https.window.js
@@ -0,0 +1,24 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Request for services. Does not return blocklisted service.';
+
+bluetooth_test(async () => {
+  let {device} = await getHIDDevice({
+    filters: [{services: ['device_information']}],
+    optionalServices: ['generic_access', 'human_interface_device']
+  })
+  let services = await device.gatt.getPrimaryServices();
+  assert_equals(services.length, 2);
+  let uuid_set = new Set(services.map(s => s.uuid));
+
+  assert_equals(uuid_set.size, 2);
+  assert_true(uuid_set.has(BluetoothUUID.getService('generic_access')));
+  assert_true(uuid_set.has(BluetoothUUID.getService('device_information')));
+  assert_false(
+      uuid_set.has(BluetoothUUID.getService('human_interface_device')));
+}, test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/correct-services.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/correct-services.https.html
deleted file mode 100644
index 9672d50..0000000
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/correct-services.https.html
+++ /dev/null
@@ -1,37 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/bluetooth/resources/bluetooth-test.js"></script>
-<script src="/bluetooth/resources/bluetooth-fake-devices.js"></script>
-<script>
-'use strict';
-const test_desc = 'Find correct services with UUID.';
-let device, fake_peripheral;
-
-bluetooth_test(() => getConnectedHealthThermometerDevice({
-  filters: [{services: ['health_thermometer']}]
-})
-    .then(_ => ({device, fake_peripheral} = _))
-    .then(() => fake_peripheral.addFakeService({uuid: 'health_thermometer'}))
-    .then(fake_service => Promise.all([
-      fake_service.addFakeCharacteristic({
-        uuid: 'temperature_measurement', properties: ['indicate']}),
-      fake_service.addFakeCharacteristic({
-        uuid: 'temperature_measurement', properties: ['indicate']})
-    ]))
-    .then(() => fake_peripheral.setNextGATTDiscoveryResponse({code:HCI_SUCCESS}))
-    .then(() => device.gatt.getPrimaryServices('health_thermometer'))
-    .then(services => Promise.all([
-      services[0].getCharacteristics(),
-      services[1].getCharacteristics()]))
-    .then(([characteristics1, characteristics2]) => {
-      if (characteristics1.length === 2)
-        assert_equals(characteristics2.length, 3);
-      else if (characteristics2.length === 2)
-        assert_equals(characteristics1.length, 3);
-      else
-        assert_unreached('Invalid lengths.');
-    }), test_desc);
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/correct-services.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/correct-services.https.window.js
new file mode 100644
index 0000000..4debf5de
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/correct-services.https.window.js
@@ -0,0 +1,32 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Find correct services with UUID.';
+let device, fake_peripheral;
+
+bluetooth_test(async () => {
+  let {device, fake_peripheral} = await getConnectedHealthThermometerDevice(
+      {filters: [{services: ['health_thermometer']}]});
+  let fake_service =
+      await fake_peripheral.addFakeService({uuid: 'health_thermometer'});
+  await Promise.all([
+    fake_service.addFakeCharacteristic(
+        {uuid: 'temperature_measurement', properties: ['indicate']}),
+    fake_service.addFakeCharacteristic(
+        {uuid: 'temperature_measurement', properties: ['indicate']})
+  ]);
+  await fake_peripheral.setNextGATTDiscoveryResponse({code: HCI_SUCCESS});
+  let services = await device.gatt.getPrimaryServices('health_thermometer');
+  let [characteristics1, characteristics2] = await Promise.all(
+      [services[0].getCharacteristics(), services[1].getCharacteristics()]);
+  if (characteristics1.length === 2)
+    assert_equals(characteristics2.length, 3);
+  else if (characteristics2.length === 2)
+    assert_equals(characteristics1.length, 3);
+  else
+    assert_unreached('Invalid lengths.');
+}, test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-found-with-uuid.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-found-with-uuid.https.html
deleted file mode 100644
index 92ca4ea..0000000
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-found-with-uuid.https.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/bluetooth/resources/bluetooth-test.js"></script>
-<script src="/bluetooth/resources/bluetooth-fake-devices.js"></script>
-<script>
-'use strict';
-const test_desc = 'Request for services. Should return right number of ' +
-    'services.';
-
-bluetooth_test(() => getTwoHealthThermometerServicesDevice({
-  filters: [{services: ['health_thermometer']}]
-})
-    .then(({device}) => Promise.all([
-      device.gatt.getPrimaryServices(health_thermometer.alias),
-      device.gatt.getPrimaryServices(health_thermometer.name),
-      device.gatt.getPrimaryServices(health_thermometer.uuid)]))
-    .then(services_arrays => services_arrays.forEach(services => {
-        assert_equals(services.length, 2);
-        services.forEach(service => {
-          assert_equals(service.uuid,
-              BluetoothUUID.getService('health_thermometer'));
-          assert_true(service.isPrimary);
-        });
-    })), test_desc);
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-found-with-uuid.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-found-with-uuid.https.window.js
new file mode 100644
index 0000000..b18bda82
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-found-with-uuid.https.window.js
@@ -0,0 +1,27 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Request for services. Should return right number of ' +
+    'services.';
+
+bluetooth_test(async () => {
+  let {device} = await getTwoHealthThermometerServicesDevice(
+      {filters: [{services: ['health_thermometer']}]});
+  let services_arrays = await Promise.all([
+    device.gatt.getPrimaryServices(health_thermometer.alias),
+    device.gatt.getPrimaryServices(health_thermometer.name),
+    device.gatt.getPrimaryServices(health_thermometer.uuid)
+  ]);
+  services_arrays.forEach(services => {
+    assert_equals(services.length, 2);
+    services.forEach(service => {
+      assert_equals(
+          service.uuid, BluetoothUUID.getService('health_thermometer'));
+      assert_true(service.isPrimary);
+    });
+  })
+}, test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-found.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-found.https.html
deleted file mode 100644
index d39c69fc..0000000
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-found.https.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/bluetooth/resources/bluetooth-test.js"></script>
-<script src="/bluetooth/resources/bluetooth-fake-devices.js"></script>
-<script>
-'use strict';
-const test_desc = 'Find all services in a device.';
-
-bluetooth_test(() => getTwoHealthThermometerServicesDevice({
-  filters: [{services: ['health_thermometer']}],
-  optionalServices: ['generic_access']
-})
-    .then(({device}) => device.gatt.getPrimaryServices())
-    .then(services => {
-      // Expect three service instances.
-      assert_equals(services.length, 3);
-      services.forEach(s => assert_true(s.isPrimary));
-
-      let uuid_set = new Set(services.map(s => s.uuid));
-      // Two of the expected services are 'health_thermometer', so
-      // only 2 unique UUIDs.
-      assert_equals(uuid_set.size, 2);
-
-      assert_true(uuid_set.has(BluetoothUUID.getService('generic_access')));
-      assert_true(uuid_set.has(BluetoothUUID.getService('health_thermometer')));
-    }), test_desc);
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-found.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-found.https.window.js
new file mode 100644
index 0000000..8098e8e8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-found.https.window.js
@@ -0,0 +1,27 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Find all services in a device.';
+
+bluetooth_test(async () => {
+  let {device} = await getTwoHealthThermometerServicesDevice({
+    filters: [{services: ['health_thermometer']}],
+    optionalServices: ['generic_access']
+  });
+  let services = await device.gatt.getPrimaryServices();
+  // Expect three service instances.
+  assert_equals(services.length, 3);
+  services.forEach(s => assert_true(s.isPrimary));
+
+  let uuid_set = new Set(services.map(s => s.uuid));
+  // Two of the expected services are 'health_thermometer', so
+  // only 2 unique UUIDs.
+  assert_equals(uuid_set.size, 2);
+
+  assert_true(uuid_set.has(BluetoothUUID.getService('generic_access')));
+  assert_true(uuid_set.has(BluetoothUUID.getService('health_thermometer')));
+}, test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-not-found.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-not-found.https.html
deleted file mode 100644
index 4c78ad1..0000000
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-not-found.https.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/bluetooth/resources/bluetooth-test.js"></script>
-<script src="/bluetooth/resources/bluetooth-fake-devices.js"></script>
-<script>
-'use strict';
-const test_desc = 'Request for services in a device with no services. Reject ' +
-    'with NotFoundError.';
-const expected = new DOMException('No Services found in device.',
-    'NotFoundError');
-
-bluetooth_test(() => getEmptyHealthThermometerDevice()
-    .then(({device}) => assert_promise_rejects_with_message(
-        device.gatt.getPrimaryServices(),
-        expected)),
-    test_desc);
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-not-found.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-not-found.https.window.js
new file mode 100644
index 0000000..6dda95e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/getPrimaryServices/services-not-found.https.window.js
@@ -0,0 +1,17 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Request for services in a device with no services. Reject ' +
+    'with NotFoundError.';
+const expected =
+    new DOMException('No Services found in device.', 'NotFoundError');
+
+bluetooth_test(async () => {
+  let {device} = await getEmptyHealthThermometerDevice();
+  return assert_promise_rejects_with_message(
+      device.gatt.getPrimaryServices(), expected);
+}, test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/fedcm.https.html b/third_party/blink/web_tests/external/wpt/credential-management/fedcm.https.html
index 655bff8c2..71ff0ab 100644
--- a/third_party/blink/web_tests/external/wpt/credential-management/fedcm.https.html
+++ b/third_party/blink/web_tests/external/wpt/credential-management/fedcm.https.html
@@ -101,6 +101,58 @@
     assert_equals(token, "a_token");
   }, "nonce is not required in FederatedIdentityProvider.");
 
+  fedcm_test(async (t, mock) => {
+    let controller = new AbortController();
+    mock.returnPendingPromise();
+    let aborted = false;
+    const token_promise = navigator.credentials.get({
+    federated: {
+      providers: [{
+        url: 'https://idp.test',
+        clientId: '1',
+      }],
+    },
+    signal: controller.signal,
+    });
+    assert_equals(aborted, false);
+    controller.abort();
+    try {
+      await token_promise;
+    } catch (e) {
+      aborted = true;
+      assert_equals(e.name, "AbortError");
+    }
+    assert_equals(aborted, true);
+  }, "test the abort signal");
+
+  fedcm_test(async (t, mock) => {
+    let controller = new AbortController();
+    mock.returnPendingPromise();
+    let aborted = false;
+    const token_promise = navigator.credentials.get({
+    federated: {
+      providers: [{
+        url: 'https://idp.test',
+        clientId: '1',
+      }],
+    },
+    signal: controller.signal,
+    });
+    assert_equals(aborted, false);
+    controller.abort();
+    try {
+      await token_promise;
+    } catch (e) {
+      aborted = true;
+      assert_equals(e.name, "AbortError");
+    }
+    assert_equals(aborted, true);
+
+    mock.returnIdToken("a_token");
+    const token = await navigator.credentials.get(test_options);
+    assert_equals(token, "a_token");
+  }, "get after abort should work");
+
   promise_test(function(t) {
     // Logout API not supported yet.
     return promise_rejects_dom(t, "NotSupportedError",
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-mock.js b/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-mock.js
index 31841979..2fa3514 100644
--- a/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-mock.js
+++ b/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-mock.js
@@ -2,20 +2,6 @@
 
 function toMojoIdTokenStatus(status) {
   return RequestIdTokenStatus["k" + status];
-//  switch(status) {
-//    case "Success": return RequestIdTokenStatus.kSuccess;
-//    case "ApprovalDeclined": return RequestIdTokenStatus.kApprovalDeclined;
-//    case "ErrorTooManyRequests": return RequestIdTokenStatus.kErrorTooManyRequests;
-//    case "ErrorWebIdNotSupportedByProvider": return RequestIdTokenStatus.kErrorWebIdNotSupportedByProvider;
-//    case "ErrorFetchingWellKnown": return RequestIdTokenStatus.kErrorFetchingWellKnown;
-//    case "ErrorInvalidWellKnown": return RequestIdTokenStatus.kErrorInvalidWellKnown;
-//    case "ErrorFetchingSignin": return RequestIdTokenStatus.kErrorFetchingSignin;
-//    case "ErrorInvalidSigninResponse": return RequestIdTokenStatus.kErrorInvalidSigninResponse;
-//    case "ErrorInvalidAccountsResponse": return RequestIdTokenStatus.kErrorInvalidAccountsResponse;
-//    case "ErrorInvalidTokenResponse": return RequestIdTokenStatus.kErrorInvalidTokenResponse;
-//    case "Error": return RequestIdTokenStatus.kError;
-//    default: throw new Error(`Invalid status: ${status}`);
-//  }
 }
 
 // A mock service for responding to federated auth requests.
@@ -31,12 +17,15 @@
     this.status_ = RequestIdTokenStatus.kError;
     this.logoutStatus_ = LogoutStatus.kError;
     this.revokeStatus_ = RevokeStatus.kError;
+    this.returnPending_ = false;
+    this.pendingPromiseResolve_ = null;
   }
 
   // Causes the subsequent `navigator.credentials.get()` to resolve with the token.
   returnIdToken(token) {
     this.status_ = RequestIdTokenStatus.kSuccess;
     this.idToken_ = token;
+    this.returnPending_ = false;
   }
 
   // Causes the subsequent `navigator.credentials.get()` to reject with the error.
@@ -45,6 +34,13 @@
       throw new Error("Success is not a valid error");
     this.status_ = toMojoIdTokenStatus(error);
     this.idToken_ = null;
+    this.returnPending_ = false;
+  }
+
+  // Causes the subsequent `navigator.credentials.get()` to return a pending promise
+  // that can be cancelled using `cancelTokenRequest()`.
+  returnPendingPromise() {
+    this.returnPending_ = true;
   }
 
   // Causes the subsequent `FederatedCredential.revoke` to reject with this
@@ -59,12 +55,26 @@
   // Implements
   //   RequestIdToken(url.mojom.Url provider, string id_request, RequestMode mode) => (RequestIdTokenStatus status, string? id_token);
   async requestIdToken(provider, idRequest, mode) {
+    if (this.returnPending_) {
+      this.pendingPromise_ = new Promise((resolve, reject) => {
+        this.pendingPromiseResolve_ = resolve;
+      });
+      return this.pendingPromise_;
+    }
     return Promise.resolve({
       status: this.status_,
       idToken: this.idToken_
     });
   }
 
+  async cancelTokenRequest() {
+    this.pendingPromiseResolve_({
+      status: toMojoIdTokenStatus("ErrorCanceled"),
+      idToken: null
+    });
+    this.pendingPromiseResolve_ = null;
+  }
+
   async logout(logout_endpoints) {
     return Promise.resolve({
       status: this.logoutStatus_
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/grid/grid-item-fragmentation-037.html b/third_party/blink/web_tests/external/wpt/css/css-break/grid/grid-item-fragmentation-037.html
new file mode 100644
index 0000000..d5d97394
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/grid/grid-item-fragmentation-037.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-grid-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<title>Tests that break-before:avoid on a grid-item is respected.</title>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="position: relative; width: 100px; height: 100px; columns: 2; column-gap: 0; column-fill: auto; background: red;">
+  <div style="display: grid;">
+    <div style="background: green; height: 50px;"></div>
+    <div style="background: green; height: 40px;"></div>
+    <div style="background: green; break-before: avoid; line-height: 0;">
+      <span style="display: inline-block; width: 100%; height: 60px;"></span>
+    </div>
+  </div>
+  <div style="background: green; position: absolute; width: 50px; height: 50px; bottom: 0; left: 0;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/grid/grid-item-fragmentation-038.html b/third_party/blink/web_tests/external/wpt/css/css-break/grid/grid-item-fragmentation-038.html
new file mode 100644
index 0000000..40008b5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/grid/grid-item-fragmentation-038.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-grid-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<title>Tests that break-before:avoid on a grid-item is respected.</title>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="position: relative; width: 100px; height: 100px; columns: 4; column-gap: 0; column-fill: auto; background: red;">
+  <div style="display: grid;">
+    <div style="background: green; height: 150px;"></div>
+    <div style="background: green; height: 110px;"></div>
+    <div style="background: green; break-before: avoid; line-height: 0;">
+      <span style="display: inline-block; width: 100%; height: 60px;"></span>
+    </div>
+  </div>
+  <div style="background: green; position: absolute; width: 50px; height: 40px; bottom: 0; right: 0;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/buffered-and-duration-threshold.html b/third_party/blink/web_tests/external/wpt/event-timing/buffered-and-duration-threshold.html
index dbc9d92..4a7a63f5 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/buffered-and-duration-threshold.html
+++ b/third_party/blink/web_tests/external/wpt/event-timing/buffered-and-duration-threshold.html
@@ -23,15 +23,15 @@
     });
     const afterFirstClick = performance.now();
     new PerformanceObserver(t.step_func(list => {
-      const mouseDowns = list.getEntriesByName('mousedown');
-      mouseDowns.forEach(entry => {
+      const pointerDowns = list.getEntriesByName('pointerdown');
+      pointerDowns.forEach(entry => {
         if (entry.processingStart < afterFirstClick) {
           // It is possible that the first event gets a slow duration and hence gets buffered.
           // In this case the minDuration must be at least 104, otherwise it shouldn't have been
           // buffered.
           verifyClickEvent(entry, 'myDiv', true /* isFirst */);
         } else {
-          verifyClickEvent(mouseDowns[0], 'myDiv', false /* isFirst */, 16 /* minDuration*/);
+          verifyClickEvent(pointerDowns[0], 'myDiv', false /* isFirst */, 16 /* minDuration*/);
           resolve1();
         }
       });
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/click-timing.html b/third_party/blink/web_tests/external/wpt/event-timing/click-timing.html
index 84943f95..24bad0a 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/click-timing.html
+++ b/third_party/blink/web_tests/external/wpt/event-timing/click-timing.html
@@ -24,7 +24,7 @@
     assert_implements(window.PerformanceEventTiming, 'Event Timing is not supported.');
     new PerformanceObserver(t.step_func(entryList => {
       observedEntries = observedEntries.concat(entryList.getEntries().filter(
-        entry => entry.name === 'mousedown'));
+        entry => entry.name === 'pointerdown'));
       if (observedEntries.length < 2)
         return;
 
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/crossiframe.html b/third_party/blink/web_tests/external/wpt/event-timing/crossiframe.html
index 7a9ede0f..df4d94f0 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/crossiframe.html
+++ b/third_party/blink/web_tests/external/wpt/event-timing/crossiframe.html
@@ -38,9 +38,8 @@
     }
     // |childFrameData| has properties with the same names as its
     // PerformanceEventTiming counterpart.
-    verifyClickEvent(childFrameData, null);
     assert_equals(childFrameData.target, 'iframe_div');
-
+    verifyClickEvent(childFrameData, null, false, 104, 'mousedown');
     assert_less_than(childFrameData.processingStart, childFrameData.clickDone,
         "The entry's processing start should be before than the child frame's clickDone.");
   }
@@ -52,18 +51,18 @@
       window.addEventListener('load', resolve);
     });
     clickTimeMin = performance.now();
-    let observedMouseDown = false;
+    let observedPointerDown = false;
     const observerPromise = new Promise(resolve => {
       new PerformanceObserver(t.step_func(entries => {
-        const mouseDowns = entries.getEntriesByName('mousedown');
-        // Ignore the callback if there were no mousedown entries.
-        if (mouseDowns.length === 0)
+        const pointerDowns = entries.getEntriesByName('pointerdown');
+        // Ignore the callback if there were no pointerdown entries.
+        if (pointerDowns.length === 0)
           return;
 
-        assert_false(observedMouseDown,
+        assert_false(observedPointerDown,
             "Observer of main frames should only capture main-frame event-timing entries");
-        validateEntries(mouseDowns);
-        observedMouseDown = true;
+        validateEntries(pointerDowns);
+        observedPointerDown = true;
         resolve();
       })).observe({type: 'event'});
     });
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/disconnect-target.html b/third_party/blink/web_tests/external/wpt/event-timing/disconnect-target.html
index 6d6770d..04c7c29 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/disconnect-target.html
+++ b/third_party/blink/web_tests/external/wpt/event-timing/disconnect-target.html
@@ -14,7 +14,7 @@
 async_test(function(t) {
   assert_implements(window.PerformanceEventTiming, 'Event Timing is not supported.');
   const observer = new PerformanceObserver(t.step_func_done((entryList) => {
-    const entries = entryList.getEntries().filter(e => e.name === 'mousedown');
+    const entries = entryList.getEntries().filter(e => e.name === 'pointerdown');
     if (entries.length === 0)
       return;
     // There must only be one click entry.
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/event-retarget.html b/third_party/blink/web_tests/external/wpt/event-timing/event-retarget.html
index 29c114f..2ddc85d 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/event-retarget.html
+++ b/third_party/blink/web_tests/external/wpt/event-timing/event-retarget.html
@@ -24,11 +24,11 @@
     }
   });
   const observer = new PerformanceObserver(t.step_func((entryList) => {
-    const entries = entryList.getEntriesByName('mousedown');
+    const entries = entryList.getEntriesByName('pointerdown');
     if (entries.length === 0)
       return;
 
-    // There must only be one mousedown entry.
+    // There must only be one pointerdown entry.
     assert_equals(entries.length, 1);
     verifyClickEvent(entries[0], 'custom_button', true);
     assert_true(innerButtonClicked);
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/first-input-shadow-dom.html b/third_party/blink/web_tests/external/wpt/event-timing/first-input-shadow-dom.html
index 66eb6597..5911dab 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/first-input-shadow-dom.html
+++ b/third_party/blink/web_tests/external/wpt/event-timing/first-input-shadow-dom.html
@@ -18,7 +18,7 @@
     connectedCallback() {
       this.attachShadow({mode: 'open'});
       this.shadowRoot.innerHTML = `<button id='inner_button_id'>Click me</button>`;
-      this.shadowRoot.getElementById('inner_button_id').onmousedown = () => {
+      this.shadowRoot.getElementById('inner_button_id').onpointerdown = () => {
         innerButtonClicked = true;
       };
     }
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/only-observe-firstInput.html b/third_party/blink/web_tests/external/wpt/event-timing/only-observe-firstInput.html
index 54a6036..53f9641 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/only-observe-firstInput.html
+++ b/third_party/blink/web_tests/external/wpt/event-timing/only-observe-firstInput.html
@@ -26,10 +26,10 @@
         assert_false(hasObservedFirstInput);
         hasObservedFirstInput = true;
         const observedEntries = entryList.getEntries().filter(
-            entry => entry.name === 'mousedown');
+            entry => entry.name === 'pointerdown');
         assert_equals(observedEntries.length, 1);
         assert_equals(observedEntries[0].entryType, 'first-input');
-        assert_equals(observedEntries[0].name, 'mousedown');
+        assert_equals(observedEntries[0].name, 'pointerdown');
     })).observe({ entryTypes: ['first-input'] });
     on_event(window, 'load', () => {
       clickAndBlockMain('button').then(() => {
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/programmatic-click-not-observed.html b/third_party/blink/web_tests/external/wpt/event-timing/programmatic-click-not-observed.html
index 4060d2d0..049607e 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/programmatic-click-not-observed.html
+++ b/third_party/blink/web_tests/external/wpt/event-timing/programmatic-click-not-observed.html
@@ -19,22 +19,22 @@
   }
   promise_test(function(t) {
     assert_implements(window.PerformanceEventTiming, 'Event Timing is not supported.');
-    let observedMouseDown = false;
+    let observedPointerDown = false;
     const observerPromise = new Promise(resolve => {
       const observer = new PerformanceObserver(t.step_func(entryList => {
-        const mouseDowns = entryList.getEntriesByName('mousedown');
-        // Ignore cases in which there is no mousedown.
-        if (mouseDowns.length === 0)
+        const pointerDowns = entryList.getEntriesByName('pointerdown');
+        // Ignore cases in which there is no pointerdown.
+        if (pointerDowns.length === 0)
           return;
 
-        assert_false(observedMouseDown, 'There must only be one mousedown entry.');
-        assert_equals(mouseDowns.length, 1);
-        const entry = mouseDowns[0];
+        assert_false(observedPointerDown, 'There must only be one pointerdown entry.');
+        assert_equals(pointerDowns.length, 1);
+        const entry = pointerDowns[0];
         // This ensures that the entry is exposing timing from the second click, i.e.
         // the one from the clickAndBlockMain() call.
         assert_greater_than_equal(entry.processingStart, beforeClick);
         verifyClickEvent(entry, 'div2', true);
-        observedMouseDown = true;
+        observedPointerDown = true;
         resolve();
       }));
       observer.observe({entryTypes: ['event']});
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/resources/event-timing-test-utils.js b/third_party/blink/web_tests/external/wpt/event-timing/resources/event-timing-test-utils.js
index 70fd66ab..32ff2b23 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/resources/event-timing-test-utils.js
+++ b/third_party/blink/web_tests/external/wpt/event-timing/resources/event-timing-test-utils.js
@@ -7,9 +7,9 @@
     mainThreadBusy(delay);
     if (callback)
       callback();
-    element.removeEventListener("mousedown", clickHandler);
+    element.removeEventListener("pointerdown", clickHandler);
   };
-  element.addEventListener("mousedown", clickHandler);
+  element.addEventListener("pointerdown", clickHandler);
   await test_driver.click(element);
 }
 
@@ -52,8 +52,8 @@
     assert_equals(entry.target, document.getElementById(targetId));
 }
 
-function verifyClickEvent(entry, targetId, isFirst=false, minDuration=104) {
-  verifyEvent(entry, 'mousedown', targetId, isFirst, minDuration);
+function verifyClickEvent(entry, targetId, isFirst=false, minDuration=104, event='pointerdown') {
+  verifyEvent(entry, event, targetId, isFirst, minDuration);
 }
 
 function wait() {
@@ -99,13 +99,13 @@
     minDuration = Math.max(minDuration, 16);
     let numEntriesReceived = 0;
     new PerformanceObserver(list => {
-      const mouseDowns = list.getEntriesByName('mousedown');
-      mouseDowns.forEach(e => {
+      const pointerDowns = list.getEntriesByName('pointerdown');
+      pointerDowns.forEach(e => {
         t.step(() => {
           verifyClickEvent(e, id, false /* isFirst */, minDuration);
         });
       });
-      numEntriesReceived += mouseDowns.length;
+      numEntriesReceived += pointerDowns.length;
       // Note that we may receive more entries if the 'fast' click events turn out slower
       // than expected.
       if (numEntriesReceived >= numEntries)
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/retrieve-firstInput.html b/third_party/blink/web_tests/external/wpt/event-timing/retrieve-firstInput.html
index 27b17cd1..c4536cb 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/retrieve-firstInput.html
+++ b/third_party/blink/web_tests/external/wpt/event-timing/retrieve-firstInput.html
@@ -22,7 +22,7 @@
       assert_equals(performance.getEntriesByType('first-input').length, 1,
         "There should be a first-input entry in the performance timeline");
       const entry = performance.getEntriesByType('first-input')[0];
-      assert_equals(entry.name, 'mousedown');
+      assert_equals(entry.name, 'pointerdown');
       assert_equals(entry.entryType, 'first-input');
       assert_greater_than_equal(entry.duration, 104,
         "The first input was a long one.");
diff --git a/third_party/blink/web_tests/external/wpt/html/webappapis/scripting/reporterror-cross-realm-method-expected.txt b/third_party/blink/web_tests/external/wpt/html/webappapis/scripting/reporterror-cross-realm-method-expected.txt
deleted file mode 100644
index 0f9f13ad..0000000
--- a/third_party/blink/web_tests/external/wpt/html/webappapis/scripting/reporterror-cross-realm-method-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL self.reportError() dispatches an "error" event for this's relevant global object assert_unreached: 'error' event should not be dispatched for top window! Reached unreachable code
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/webappapis/structured-clone/structured-clone-cross-realm-method-expected.txt b/third_party/blink/web_tests/external/wpt/html/webappapis/structured-clone/structured-clone-cross-realm-method-expected.txt
deleted file mode 100644
index 3422f26..0000000
--- a/third_party/blink/web_tests/external/wpt/html/webappapis/structured-clone/structured-clone-cross-realm-method-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-FAIL Object instance assert_true: expected true got false
-FAIL Array instance assert_true: expected true got false
-FAIL Date instance assert_true: expected true got false
-FAIL RegExp instance assert_true: expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/html/webappapis/scripting/reporterror-cross-realm-method-expected.txt b/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/html/webappapis/scripting/reporterror-cross-realm-method-expected.txt
deleted file mode 100644
index 0f9f13ad..0000000
--- a/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/html/webappapis/scripting/reporterror-cross-realm-method-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL self.reportError() dispatches an "error" event for this's relevant global object assert_unreached: 'error' event should not be dispatched for top window! Reached unreachable code
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/html/webappapis/structured-clone/structured-clone-cross-realm-method-expected.txt b/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/html/webappapis/structured-clone/structured-clone-cross-realm-method-expected.txt
deleted file mode 100644
index 3422f26..0000000
--- a/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/html/webappapis/structured-clone/structured-clone-cross-realm-method-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-FAIL Object instance assert_true: expected true got false
-FAIL Array instance assert_true: expected true got false
-FAIL Date instance assert_true: expected true got false
-FAIL RegExp instance assert_true: expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/harfbuzz-ng/BUILD.gn b/third_party/harfbuzz-ng/BUILD.gn
index 1119bd7..43541ae 100644
--- a/third_party/harfbuzz-ng/BUILD.gn
+++ b/third_party/harfbuzz-ng/BUILD.gn
@@ -141,6 +141,7 @@
       "src/src/hb-ot-color-cpal-table.hh",
       "src/src/hb-ot-color-sbix-table.hh",
       "src/src/hb-ot-color-svg-table.hh",
+      "src/src/hb-ot-color.cc",
       "src/src/hb-ot-deprecated.h",
       "src/src/hb-ot-face-table-list.hh",
       "src/src/hb-ot-face.cc",
diff --git a/third_party/harfbuzz-ng/README.chromium b/third_party/harfbuzz-ng/README.chromium
index a7037815..35b5f56 100644
--- a/third_party/harfbuzz-ng/README.chromium
+++ b/third_party/harfbuzz-ng/README.chromium
@@ -45,7 +45,6 @@
     hb-null.hh
     hb-ot-color-sbix-table.hh
     hb-ot-color-svg-table.hh
-    hb-ot-color.cc
     hb-uniscribe.cc
     hb-uniscribe.h
     test-unicode-ranges.cc
diff --git a/tools/android/build_speed/benchmark.py b/tools/android/build_speed/benchmark.py
index 8f8a03d..cf9c544 100755
--- a/tools/android/build_speed/benchmark.py
+++ b/tools/android/build_speed/benchmark.py
@@ -413,6 +413,8 @@
         '--use-emulator',
         action='store_true',
         help='Use an emulator to include install/launch timing.')
+    parser.add_argument('--target',
+                        help='Specify this to override the default target.')
     parser.add_argument('-v',
                         '--verbose',
                         action='count',
@@ -439,7 +441,10 @@
         devil_chromium.Initialize()
         gn_args += _EMULATOR_GN_ARGS
 
-    target = _TARGETS['bundle' if args.bundle else 'apk']
+    if args.target:
+        target = args.target
+    else:
+        target = _TARGETS['bundle' if args.bundle else 'apk']
     results = run_benchmarks(args.benchmark, gn_args, out_dir, target,
                              args.repeat, args.no_server, args.use_emulator)
     server_str = f'{"not " if args.no_server else ""}using build server'
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 1713946..de6c003 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -359,7 +359,6 @@
       'linux-wpt-fyi-rel': 'release_bot_minimal_symbols',
       'linux-wpt-identity-fyi-rel': 'release_bot_minimal_symbols',
       'linux-wpt-input-fyi-rel': 'release_bot_minimal_symbols',
-      'mac-arm64-on-arm64-rel': 'mac_arm64_release_bot',
       # TODO(crbug.com/1252626): remove this after the migration.
       'mac-arm64-on-arm64-rel-reclient': 'mac_arm64_release_bot_reclient',
       'mac-code-coverage': 'clang_code_coverage',
@@ -536,6 +535,7 @@
       'ios-simulator': 'ios_simulator_debug_static_bot_xctest',
       'ios-simulator-full-configs': 'ios_simulator_debug_static_bot_xctest',
       'ios-simulator-noncq': 'ios_simulator_debug_static_bot_xctest',
+      'mac-arm64-on-arm64-rel': 'mac_arm64_release_bot',
       'mac-arm64-rel': 'mac_arm64_gpu_tests_release_bot_minimal_symbols_no_nacl',
     },
 
@@ -629,6 +629,7 @@
       'mac-arm-rel-swarming': 'gpu_tests_release_bot_minimal_symbols',
 
       # This should be the same with 'Win x64 Builder'.
+      'win11-rel-swarming': 'gpu_tests_release_bot_minimal_symbols',
       'win-rel-swarming': 'gpu_tests_release_bot_minimal_symbols',
       'win-rel-swarming-staging': 'gpu_tests_release_bot_minimal_symbols',
     },
diff --git a/tools/mb/mb_config_expectations/chromium.dev.json b/tools/mb/mb_config_expectations/chromium.dev.json
index 46d6b9966..4841746c 100644
--- a/tools/mb/mb_config_expectations/chromium.dev.json
+++ b/tools/mb/mb_config_expectations/chromium.dev.json
@@ -90,5 +90,16 @@
       "symbol_level": 1,
       "use_goma": true
     }
+  },
+  "win11-rel-swarming": {
+    "gn_args": {
+      "dcheck_always_on": false,
+      "ffmpeg_branding": "Chrome",
+      "is_component_build": false,
+      "is_debug": false,
+      "proprietary_codecs": true,
+      "symbol_level": 1,
+      "use_goma": true
+    }
   }
 }
\ No newline at end of file
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json
index dbe731d..049be177 100644
--- a/tools/mb/mb_config_expectations/chromium.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -1034,15 +1034,6 @@
       "use_goma": true
     }
   },
-  "mac-arm64-on-arm64-rel": {
-    "gn_args": {
-      "dcheck_always_on": false,
-      "is_component_build": false,
-      "is_debug": false,
-      "target_cpu": "arm64",
-      "use_goma": true
-    }
-  },
   "mac-arm64-on-arm64-rel-reclient": {
     "gn_args": {
       "dcheck_always_on": false,
diff --git a/tools/mb/mb_config_expectations/chromium.mac.json b/tools/mb/mb_config_expectations/chromium.mac.json
index 98fd360..462aa7b 100644
--- a/tools/mb/mb_config_expectations/chromium.mac.json
+++ b/tools/mb/mb_config_expectations/chromium.mac.json
@@ -79,6 +79,15 @@
       "use_goma": true
     }
   },
+  "mac-arm64-on-arm64-rel": {
+    "gn_args": {
+      "dcheck_always_on": false,
+      "is_component_build": false,
+      "is_debug": false,
+      "target_cpu": "arm64",
+      "use_goma": true
+    }
+  },
   "mac-arm64-rel": {
     "gn_args": {
       "dcheck_always_on": false,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index abbb6e3..bafdb58 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -52600,6 +52600,7 @@
   <int value="151825088" label="MessagesForAndroidPermissionUpdate:enabled"/>
   <int value="153347646" label="SmartDimModelV3:disabled"/>
   <int value="155977192" label="EnableFileManagerFormatDialog:disabled"/>
+  <int value="156285060" label="UseMultipleOverlays:disabled"/>
   <int value="157217034" label="enable-tab-for-desktop-share"/>
   <int value="157318016" label="AutomaticTabDiscarding:enabled"/>
   <int value="158490417" label="PhoneHubCameraRoll:enabled"/>
@@ -53749,6 +53750,7 @@
   <int value="1021573543" label="QuickSettingsPWA:disabled"/>
   <int value="1021848000" label="SharedHighlightingUseBlocklist:disabled"/>
   <int value="1022424308" label="SignedExchangeSubresourcePrefetch:enabled"/>
+  <int value="1022504679" label="UseMultipleOverlays:enabled"/>
   <int value="1022992701" label="enable-origin-chip-always"/>
   <int value="1023319882" label="HttpsOnlyMode:disabled"/>
   <int value="1023668536" label="PrintServerUi:enabled"/>
@@ -55321,6 +55323,8 @@
   <int value="5" label="Field trial logged-in site"/>
   <int value="6" label="Password manager saved site"/>
   <int value="7" label="OAuth popup based first time login flow"/>
+  <int value="8"
+      label="Common logged-in site received from optimization guide hints"/>
 </enum>
 
 <enum name="LoginFailureReason">
@@ -74449,6 +74453,7 @@
       label="Using shared RELRO failed: failed to mmap on top of previous
              RELRO"/>
   <int value="7" label="Using shared RELRO failed: invalid RELRO information"/>
+  <int value="8" label="Using shared RELRO failed: load address was reset"/>
 </enum>
 
 <enum name="RemoteBookmarkUpdateError">
@@ -78548,6 +78553,13 @@
   </int>
 </enum>
 
+<enum name="SetNicknameResult">
+  <int value="0" label="Invalid nickname format"/>
+  <int value="1" label="Device not found"/>
+  <int value="2" label="Prefs unavailable"/>
+  <int value="3" label="Success"/>
+</enum>
+
 <enum name="SettingId">
   <int value="1" label="Wifi"/>
   <int value="2" label="Cellular"/>
@@ -92842,6 +92854,9 @@
 </enum>
 
 <enum name="WebViewOpenWebVisibility">
+  <obsolete>
+    Deprecated in December 2021.
+  </obsolete>
   <int value="0" label="OpenWebContentVisible"/>
   <int value="1" label="OpenWebContentNotVisible"/>
 </enum>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 87f77d0..1737514 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -4740,7 +4740,7 @@
     Records the global visibility state of WebView in seconds, updated with
     every Metrics upload. The Visible bucket counts total seconds that any
     WebView was visible (the view itself was visible, and was attached to the
-    view hierarchy of a visible window). The NotVisble bucket counts time since
+    view hierarchy of a visible window). The NotVisible bucket counts time since
     WebView initialization for which no WebView was considered visible, or no
     WebView existed. The total of these two buckets reflects the amount of time
     covered by metrics collection.
@@ -4765,8 +4765,38 @@
   </summary>
 </histogram>
 
+<histogram name="Android.WebView.VisibleScheme.Global" enum="WebViewUrlScheme"
+    expires_after="2022-06-01">
+  <owner>mvanouwerkerk@chromium.org</owner>
+  <owner>ntfschr@chromium.org</owner>
+  <owner>src/android_webview/OWNERS</owner>
+  <summary>
+    Records the number of seconds any WebView was visible (the view itself was
+    visible, and was attached to the view hierarchy of a visible window) with a
+    particular URL scheme in the primary main frame. Updated with every Metrics
+    upload.
+  </summary>
+</histogram>
+
+<histogram name="Android.WebView.VisibleScheme.PerWebView"
+    enum="WebViewUrlScheme" expires_after="2022-06-01">
+  <owner>mvanouwerkerk@chromium.org</owner>
+  <owner>ntfschr@chromium.org</owner>
+  <owner>src/android_webview/OWNERS</owner>
+  <summary>
+    Records the number of seconds each WebView was visible (the view itself was
+    visible, and was attached to the view hierarchy of a visible window) with a
+    particular URL scheme in the primary main frame. Updated with every Metrics
+    upload.
+  </summary>
+</histogram>
+
 <histogram name="Android.WebView.WebViewOpenWebVisible.Global"
     enum="WebViewOpenWebVisibility" expires_after="2022-06-01">
+  <obsolete>
+    Removed in December 2021, use Android.WebView.VisibleScheme.Global instead
+    which breaks down all the visible time spent into more schemes.
+  </obsolete>
   <owner>idries@google.com</owner>
   <owner>ntfschr@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
@@ -4783,6 +4813,10 @@
 
 <histogram name="Android.WebView.WebViewOpenWebVisible.PerWebView"
     enum="WebViewOpenWebVisibility" expires_after="2022-06-01">
+  <obsolete>
+    Deprecated in December 2021, use Android.WebView.VisibleScheme.PerWebView
+    instead which breaks down all the visible time spent into more schemes.
+  </obsolete>
   <owner>idries@google.com</owner>
   <owner>ntfschr@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index fcf443d..da8bb12 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -149,6 +149,18 @@
   <variant name=".Reshows" summary="Bubble was reopened after being closed"/>
 </variants>
 
+<variants name="AutofillMeasurementTime">
+  <variant name="">
+    <obsolete>
+      Base histogram. Use suffixes of this histogram instead.
+    </obsolete>
+  </variant>
+  <variant name=".AtFillTime" summary="Recorded at filling time."/>
+  <variant name=".AtSubmissionTime"
+      summary="Recorded at submission time. May be missed due to submission
+               detection problems."/>
+</variants>
+
 <variants name="AutofillRealPanResultGroups">
   <variant name=".Failure" summary="Failure">
     <obsolete>
@@ -809,24 +821,26 @@
   </summary>
 </histogram>
 
-<histogram name="Autofill.CreditCard.NumberFills" units="NumberFills"
-    expires_after="2022-10-31">
+<histogram name="Autofill.CreditCard.NumberFills{AutofillMeasurementTime}"
+    units="Filled" expires_after="2022-10-31">
   <owner>schwering@google.com</owner>
   <owner>chrome-autofill-alerts@google.com</owner>
   <summary>
     Records for each credit card form submission whether a credit card number
-    has been autofilled.
+    has been autofilled. {AutofillMeasurementTime}
 
     That is, emits 1 if a form contains an autofilled credit card number.
   </summary>
+  <token key="AutofillMeasurementTime" variants="AutofillMeasurementTime"/>
 </histogram>
 
-<histogram name="Autofill.CreditCard.SeamlessFills"
+<histogram name="Autofill.CreditCard.SeamlessFills{AutofillMeasurementTime}"
     enum="CreditCardSeamlessFill" expires_after="2022-10-31">
   <owner>schwering@google.com</owner>
   <owner>chrome-autofill-alerts@google.com</owner>
   <summary>
-    Records for each credit card form submission the quality of the fill.
+    Records for each credit card form the quality of the fill.
+    {AutofillMeasurementTime}
 
     The fields in question are: cardholder name (full, or first and last),
     credit card number, expiration date (MM/YY, MM/YYYY, MM and YY, or MM and
@@ -850,6 +864,7 @@
     Any form that contains at least one credit card field counts as credit card
     form.
   </summary>
+  <token key="AutofillMeasurementTime" variants="AutofillMeasurementTime"/>
 </histogram>
 
 <histogram name="Autofill.CreditCardFillingInfoBar"
diff --git a/tools/metrics/histograms/metadata/bluetooth/histograms.xml b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
index ef6e33c..e41acf4 100644
--- a/tools/metrics/histograms/metadata/bluetooth/histograms.xml
+++ b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
@@ -236,6 +236,18 @@
   </summary>
 </histogram>
 
+<histogram name="Bluetooth.ChromeOS.FastPair.RetroactivePairing.Result"
+    enum="BooleanSuccess" expires_after="2022-09-20">
+  <owner>shanefitz@google.com</owner>
+  <owner>julietlevesque@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the success or failure of retroactive pairings. This metric is
+    emitted after the account key is attempted to be written to the device in
+    the retroactive pairing scenario.
+  </summary>
+</histogram>
+
 <histogram
     name="Bluetooth.ChromeOS.FastPair.TotalUxPairTime.{FastPairPairingProtocol}"
     units="ms" expires_after="2022-09-20">
@@ -366,6 +378,13 @@
   </token>
 </histogram>
 
+<histogram name="Bluetooth.ChromeOS.SetNickname.Result"
+    enum="SetNicknameResult" expires_after="2022-12-10">
+  <owner>khorimoto@chromium.org</owner>
+  <owner>cros-connectivity@google.com</owner>
+  <summary>Emitted each time a nickname change attempt completes.</summary>
+</histogram>
+
 <histogram name="Bluetooth.ChromeOS.UiSurfaceDisplayed"
     enum="BluetoothUiSurface" expires_after="2022-07-01">
   <owner>khorimoto@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index c27e44d..a3b5facd 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -1441,7 +1441,7 @@
 
 <histogram
     name="ChromeOS.Settings.Accessibility.FullscreenMagnifierMouseFollowingMode"
-    enum="MagnifierMouseFollowingMode" expires_after="2022-01-26">
+    enum="MagnifierMouseFollowingMode" expires_after="2022-12-13">
   <owner>josiahk@chromium.org</owner>
   <owner>chromium-accessibility@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/leveldb_proto/histograms.xml b/tools/metrics/histograms/metadata/leveldb_proto/histograms.xml
index 41478b5..0e57e73 100644
--- a/tools/metrics/histograms/metadata/leveldb_proto/histograms.xml
+++ b/tools/metrics/histograms/metadata/leveldb_proto/histograms.xml
@@ -79,6 +79,8 @@
       summary="Database for RemoteSuggestion snippets."/>
   <variant name="OfflinePageMetadataStore"
       summary="Databases for OfflinePageMetadataStore"/>
+  <variant name="PageEntityMetadataDatabase"
+      summary="Database for page entity metadata."/>
   <variant name="PersistedStateDatabase"
       summary="Database for NonCriticalPersistedTabData"/>
   <variant name="PreviewsHintCacheStore"
diff --git a/tools/metrics/histograms/metadata/login/histograms.xml b/tools/metrics/histograms/metadata/login/histograms.xml
index 06e0b2f..92bc6b5 100644
--- a/tools/metrics/histograms/metadata/login/histograms.xml
+++ b/tools/metrics/histograms/metadata/login/histograms.xml
@@ -162,7 +162,7 @@
 </histogram>
 
 <histogram name="Login.PageLoad.DetectionType" enum="LoginDetectionType"
-    expires_after="2021-11-30">
+    expires_after="2022-11-30">
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -204,7 +204,7 @@
 </histogram>
 
 <histogram name="Login.PasswordStoreSites.InitializedBeforeQuery"
-    enum="Boolean" expires_after="2022-06-05">
+    enum="Boolean" expires_after="2022-11-30">
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index daf5e2e..e059b05 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -9080,9 +9080,11 @@
 
 <histogram name="LoadingPredictor.OptimizationHintsReceiveStatus"
     enum="LoadingPredictorOptimizationHintsReceiveStatus"
-    expires_after="2022-04-24">
+    expires_after="2022-12-19">
   <owner>sophiechang@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@chromium.org</owner>
   <summary>
     The status of when the optimization hints were received by the Loading
     Predictor. This will be recorded on navigations for which predictions from
@@ -9091,8 +9093,9 @@
 </histogram>
 
 <histogram name="LoadingPredictor.PreconnectCount" units="origins"
-    expires_after="2021-12-19">
-  <owner>alexilin@chromium.org</owner>
+    expires_after="2022-12-19">
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@chromium.org</owner>
   <summary>
     The number of origins that were preconnected for a page load. It includes
     preconnect attempts that don't result in new opened connection. Logged after
@@ -9101,8 +9104,9 @@
 </histogram>
 
 <histogram name="LoadingPredictor.PreconnectHitsPercentage" units="%"
-    expires_after="2021-12-19">
-  <owner>alexilin@chromium.org</owner>
+    expires_after="2022-12-19">
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@chromium.org</owner>
   <summary>
     The percentage of origins that were preconnected and requested by a page
     load to the total number of origins that were preconnected for a page load.
@@ -9112,9 +9116,9 @@
 </histogram>
 
 <histogram base="true" name="LoadingPredictor.PreconnectLearningCount"
-    units="origins" expires_after="2021-08-15">
-  <owner>alexilin@chromium.org</owner>
-  <owner>tbansal@chromium.org</owner>
+    units="origins" expires_after="2022-12-19">
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@chromium.org</owner>
   <summary>
     When the loading predictor has origins in the local database for a given
     navigation to preconnect and preresolve, the count of predicted origins.
@@ -9122,9 +9126,9 @@
 </histogram>
 
 <histogram base="true" name="LoadingPredictor.PreconnectLearningPrecision"
-    units="%" expires_after="2021-08-15">
-  <owner>alexilin@chromium.org</owner>
-  <owner>tbansal@chromium.org</owner>
+    units="%" expires_after="2022-12-19">
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@chromium.org</owner>
   <summary>
     When the loading predictor has origins in the local database for a given
     navigation to preconnect and preresolve, the precision of the predictions in
@@ -9134,9 +9138,9 @@
 </histogram>
 
 <histogram base="true" name="LoadingPredictor.PreconnectLearningRecall"
-    units="%" expires_after="2021-08-09">
-  <owner>alexilin@chromium.org</owner>
-  <owner>tbansal@chromium.org</owner>
+    units="%" expires_after="2022-12-19">
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@chromium.org</owner>
   <summary>
     When the loading predictor has origins in the local database for a given
     navigation to preconnect and preresolve, the recall of the predictions, in
@@ -9146,9 +9150,9 @@
 </histogram>
 
 <histogram base="true" name="LoadingPredictor.PreconnectLearningRedirectStatus"
-    enum="ResourcePrefetchPredictorRedirectStatus" expires_after="M82">
-  <owner>alexilin@chromium.org</owner>
-  <owner>tbansal@chromium.org</owner>
+    enum="ResourcePrefetchPredictorRedirectStatus" expires_after="2022-12-19">
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@chromium.org</owner>
   <summary>
     When the prefetch predictor has origins in the local database for a given
     navigation to preconnect and preresolve, records stats about whether
@@ -9157,8 +9161,9 @@
 </histogram>
 
 <histogram name="LoadingPredictor.PreresolveCount" units="hosts"
-    expires_after="2021-12-19">
-  <owner>alexilin@chromium.org</owner>
+    expires_after="2022-12-19">
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@chromium.org</owner>
   <summary>
     The number of hosts that were preresolved for a page load. It includes only
     successful DNS lookups. Logged after the preconnect manager completes all
@@ -9167,8 +9172,9 @@
 </histogram>
 
 <histogram name="LoadingPredictor.PreresolveHitsPercentage" units="%"
-    expires_after="M85">
-  <owner>alexilin@chromium.org</owner>
+    expires_after="2022-12-19">
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@chromium.org</owner>
   <summary>
     The percentage of hosts that were preresolved and requested by a page load
     to the total number of hosts that were preresolved for a page load. Logged
@@ -12307,7 +12313,7 @@
 
 <histogram name="PrivacySandbox.AggregationService.ReportStatus"
     enum="PrivacySandboxAggregationServiceReportStatus"
-    expires_after="2022-01-16">
+    expires_after="2022-07-16">
   <owner>alexmt@chromium.org</owner>
   <owner>linnan@chromium.org</owner>
   <summary>
@@ -12322,7 +12328,7 @@
 
 <histogram name="PrivacySandbox.AggregationService.Storage.Sql.InitStatus"
     enum="PrivacySandboxAggregationServiceStorageSqlInitStatus"
-    expires_after="2022-01-16">
+    expires_after="2022-07-16">
   <owner>alexmt@chromium.org</owner>
   <owner>linnan@chromium.org</owner>
   <summary>
diff --git a/tools/perf/benchmarks/rendering.py b/tools/perf/benchmarks/rendering.py
index 2eee04a..c8756bb 100644
--- a/tools/perf/benchmarks/rendering.py
+++ b/tools/perf/benchmarks/rendering.py
@@ -30,6 +30,9 @@
     'Graphics.Smoothness.PercentDroppedFrames.AllAnimations',
     'Graphics.Smoothness.PercentDroppedFrames.AllInteractions',
     'Graphics.Smoothness.PercentDroppedFrames.AllSequences',
+    'Graphics.Smoothness.PercentDroppedFrames2.AllAnimations',
+    'Graphics.Smoothness.PercentDroppedFrames2.AllInteractions',
+    'Graphics.Smoothness.PercentDroppedFrames2.AllSequences',
     'Memory.GPU.PeakMemoryUsage2.Scroll',
     'Memory.GPU.PeakMemoryUsage2.PageLoad',
 ]
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 2ecb291f..06154e9 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -9,16 +9,16 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/win/1357bd132733ccc23a295739e4bc432d7a53d120/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "6043cb87e9b062d4faba99fe10f46a6a44b0d873",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/1357bd132733ccc23a295739e4bc432d7a53d120/trace_processor_shell"
+            "hash": "851992a7ccb80791df659bcca23f47292cfce319",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/f9abf9948a180a56a3595ec54ff9f2f5c2c9947c/trace_processor_shell"
         },
         "linux_arm64": {
             "hash": "5074025a2898ec41a872e70a5719e417acb0a380",
             "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "linux": {
-            "hash": "39d6ae444da9ed567efaf34322170f188fcaf559",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/1357bd132733ccc23a295739e4bc432d7a53d120/trace_processor_shell"
+            "hash": "bfcfedfd42706342bbca38ea59198da6673d7a06",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/f9abf9948a180a56a3595ec54ff9f2f5c2c9947c/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/accessibility/BUILD.gn b/ui/accessibility/BUILD.gn
index 2ccf84b..f146bae 100644
--- a/ui/accessibility/BUILD.gn
+++ b/ui/accessibility/BUILD.gn
@@ -285,6 +285,17 @@
     "//ui/gfx:test_support",
   ]
 
+  if (is_fuchsia) {
+    sources += [ "platform/fuchsia/semantic_provider_test.cc" ]
+
+    deps += [
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.math",
+      "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp",
+      "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
+    ]
+  }
+
   if (has_native_accessibility) {
     # This test depends heavily on NativeViewAccessible, which is only
     # implemented on these platforms.
diff --git a/ui/accessibility/platform/BUILD.gn b/ui/accessibility/platform/BUILD.gn
index 20df50a6..853cb4b9 100644
--- a/ui/accessibility/platform/BUILD.gn
+++ b/ui/accessibility/platform/BUILD.gn
@@ -93,10 +93,17 @@
       "fuchsia/accessibility_bridge_fuchsia.h",
       "fuchsia/accessibility_bridge_fuchsia_registry.cc",
       "fuchsia/accessibility_bridge_fuchsia_registry.h",
+      "fuchsia/semantic_provider.cc",
+      "fuchsia/semantic_provider.h",
     ]
 
-    public_deps +=
-        [ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics" ]
+    public_deps += [
+      "//base",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.math",
+      "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
+    ]
   }
 
   if (has_native_accessibility) {
diff --git a/ui/accessibility/platform/fuchsia/semantic_provider.cc b/ui/accessibility/platform/fuchsia/semantic_provider.cc
new file mode 100644
index 0000000..dd0a9776
--- /dev/null
+++ b/ui/accessibility/platform/fuchsia/semantic_provider.cc
@@ -0,0 +1,306 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/accessibility/platform/fuchsia/semantic_provider.h"
+
+#include <lib/sys/cpp/component_context.h>
+
+#include "base/check.h"
+#include "base/check_op.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/fuchsia/process_context.h"
+
+namespace ui {
+namespace {
+
+using fuchsia::accessibility::semantics::Node;
+
+constexpr uint32_t kFuchsiaRootNodeId = 0;
+constexpr size_t kMaxOperationsPerBatch = 16;
+
+}  // namespace
+
+AXFuchsiaSemanticProvider::Batch::Batch(Type type) : type_(type) {}
+AXFuchsiaSemanticProvider::Batch::Batch(Batch&& other) = default;
+AXFuchsiaSemanticProvider::Batch::~Batch() = default;
+
+bool AXFuchsiaSemanticProvider::Batch::IsFull() const {
+  return (
+      (type_ == Type::kUpdate && updates_.size() >= kMaxOperationsPerBatch) ||
+      (type_ == Type::kDelete &&
+       delete_node_ids_.size() >= kMaxOperationsPerBatch));
+}
+
+void AXFuchsiaSemanticProvider::Batch::Append(
+    fuchsia::accessibility::semantics::Node node) {
+  DCHECK_EQ(type_, Type::kUpdate);
+  DCHECK(!IsFull());
+  updates_.push_back(std::move(node));
+}
+
+void AXFuchsiaSemanticProvider::Batch::AppendDeletion(uint32_t delete_node_id) {
+  DCHECK_EQ(type_, Type::kDelete);
+  DCHECK(!IsFull());
+  delete_node_ids_.push_back(delete_node_id);
+}
+
+void AXFuchsiaSemanticProvider::Batch::Apply(
+    fuchsia::accessibility::semantics::SemanticTreePtr* semantic_tree) {
+  if (type_ == Type::kUpdate && !updates_.empty())
+    (*semantic_tree)->UpdateSemanticNodes(std::move(updates_));
+  else if (type_ == Type::kDelete && !delete_node_ids_.empty())
+    (*semantic_tree)->DeleteSemanticNodes(std::move(delete_node_ids_));
+}
+
+AXFuchsiaSemanticProvider::NodeInfo ::NodeInfo() = default;
+AXFuchsiaSemanticProvider::NodeInfo ::~NodeInfo() = default;
+
+AXFuchsiaSemanticProvider::Delegate::Delegate() = default;
+AXFuchsiaSemanticProvider::Delegate::~Delegate() = default;
+
+AXFuchsiaSemanticProvider::AXFuchsiaSemanticProvider(
+    fuchsia::ui::views::ViewRef view_ref,
+    float pixel_scale,
+    Delegate* delegate)
+    : view_ref_(std::move(view_ref)),
+      pixel_scale_(pixel_scale),
+      delegate_(delegate),
+      semantic_listener_binding_(this) {
+  sys::ComponentContext* component_context = base::ComponentContextForProcess();
+  DCHECK(component_context);
+  DCHECK(delegate_);
+
+  component_context->svc()
+      ->Connect<fuchsia::accessibility::semantics::SemanticsManager>()
+      ->RegisterViewForSemantics(std::move(view_ref_),
+                                 semantic_listener_binding_.NewBinding(),
+                                 semantic_tree_.NewRequest());
+  semantic_tree_.set_error_handler([this](zx_status_t status) {
+    ZX_LOG(ERROR, status) << "SemanticTree disconnected";
+    delegate_->OnSemanticsManagerConnectionClosed();
+    semantic_updates_enabled_ = false;
+  });
+}
+
+AXFuchsiaSemanticProvider::~AXFuchsiaSemanticProvider() = default;
+
+bool AXFuchsiaSemanticProvider::Update(
+    fuchsia::accessibility::semantics::Node node) {
+  if (!semantic_updates_enabled())
+    return false;
+
+  DCHECK(node.has_node_id());
+
+  if (node.node_id() != kFuchsiaRootNodeId) {
+    auto found_not_reachable = not_reachable_.find(node.node_id());
+    const bool is_not_reachable = found_not_reachable != not_reachable_.end();
+    const absl::optional<uint32_t> parent_node_id =
+        GetParentForNode(node.node_id());
+    if (is_not_reachable && parent_node_id) {
+      // Connection parent -> |node| exists now.
+      not_reachable_.erase(found_not_reachable);
+      nodes_[node.node_id()].parents.insert(*parent_node_id);
+    } else if (!parent_node_id) {
+      // No node or multiple nodes points to this one, so it is not reachable.
+      if (!is_not_reachable)
+        not_reachable_[node.node_id()] = {};
+    }
+  }
+
+  // If the node is not present in the map, the list of children will be empty
+  // so this is a no-op in the call below.
+  std::vector<uint32_t>& children = nodes_[node.node_id()].children;
+
+  // Before updating the node, update the list of children to be not reachable,
+  // in case the new list of children change.
+  MarkChildrenAsNotReachable(children, node.node_id());
+  children = node.has_child_ids() ? node.child_ids() : std::vector<uint32_t>();
+  MarkChildrenAsReachable(children, node.node_id());
+
+  Batch& batch = GetCurrentUnfilledBatch(Batch::Type::kUpdate);
+  batch.Append(std::move(node));
+  TryToCommit();
+  return true;
+}
+
+void AXFuchsiaSemanticProvider::TryToCommit() {
+  // Don't send out updates while the tree is mid-mutation.
+  if (commit_inflight_ || batches_.empty())
+    return;
+
+  // If a tree has nodes but no root, wait until the root is present or all
+  // nodes are deleted.
+  if (!nodes_.empty() && nodes_.find(kFuchsiaRootNodeId) == nodes_.end())
+    return;
+
+  if (!not_reachable_.empty())
+    return;
+
+  for (auto& batch : batches_) {
+    batch.Apply(&semantic_tree_);
+  }
+
+  batches_.clear();
+  semantic_tree_->CommitUpdates(
+      fit::bind_member(this, &AXFuchsiaSemanticProvider::OnCommitComplete));
+  commit_inflight_ = true;
+}
+
+bool AXFuchsiaSemanticProvider::Delete(uint32_t node_id) {
+  if (!semantic_updates_enabled())
+    return false;
+
+  auto it = nodes_.find(node_id);
+  if (it == nodes_.end())
+    return false;
+
+  if (it->second.parents.empty()) {
+    // No node points to this one, so it is safe to remove it from the tree.
+    not_reachable_.erase(node_id);
+  } else {
+    not_reachable_[node_id] =
+        it->second
+            .parents;  // Zero or more parents can be pointing to this node.
+  }
+  MarkChildrenAsNotReachable(it->second.children, node_id);
+
+  nodes_.erase(it);
+
+  Batch& batch = GetCurrentUnfilledBatch(Batch::Type::kDelete);
+  batch.AppendDeletion(node_id);
+  TryToCommit();
+  return true;
+}
+
+void AXFuchsiaSemanticProvider::SendEvent(
+    fuchsia::accessibility::semantics::SemanticEvent event) {
+  semantic_tree_->SendSemanticEvent(std::move(event), [](auto...) {});
+}
+
+bool AXFuchsiaSemanticProvider::HasPendingUpdates() const {
+  return commit_inflight_ || !batches_.empty();
+}
+
+bool AXFuchsiaSemanticProvider::Clear() {
+  if (!semantic_updates_enabled())
+    return false;
+
+  batches_.clear();
+  not_reachable_.clear();
+  nodes_.clear();
+  Batch& batch = GetCurrentUnfilledBatch(Batch::Type::kDelete);
+  batch.AppendDeletion(kFuchsiaRootNodeId);
+  TryToCommit();
+  return true;
+}
+
+void AXFuchsiaSemanticProvider::OnAccessibilityActionRequested(
+    uint32_t node_id,
+    fuchsia::accessibility::semantics::Action action,
+    fuchsia::accessibility::semantics::SemanticListener::
+        OnAccessibilityActionRequestedCallback callback) {
+  if (delegate_->OnAccessibilityAction(node_id, action))
+    callback(true);
+
+  // The action was not handled.
+  callback(false);
+}
+
+void AXFuchsiaSemanticProvider::HitTest(fuchsia::math::PointF local_point,
+                                        HitTestCallback callback) {
+  fuchsia::math::PointF point;
+  point.x = local_point.x * pixel_scale_;
+  point.y = local_point.y * pixel_scale_;
+
+  delegate_->OnHitTest(point, std::move(callback));
+  return;
+}
+
+void AXFuchsiaSemanticProvider::OnSemanticsModeChanged(
+    bool update_enabled,
+    OnSemanticsModeChangedCallback callback) {
+  if (semantic_updates_enabled_ != update_enabled)
+    delegate_->OnSemanticsEnabled(update_enabled);
+
+  semantic_updates_enabled_ = update_enabled;
+  callback();
+}
+
+void AXFuchsiaSemanticProvider::MarkChildrenAsNotReachable(
+    const std::vector<uint32_t>& child_ids,
+    uint32_t parent_id) {
+  for (const uint32_t child_id : child_ids) {
+    const auto it = nodes_.find(child_id);
+    if (it != nodes_.end()) {
+      it->second.parents.erase(parent_id);
+      if (it->second.parents.empty())
+        not_reachable_[child_id] = {};
+      else
+        not_reachable_.erase(child_id);
+    } else {
+      auto not_reachable_it = not_reachable_.find(child_id);
+      // Child id is no longer in the regular map, deletes it also from
+      // not_reachable_ if no parent points to it anymore.
+      if (not_reachable_it != not_reachable_.end()) {
+        not_reachable_it->second.erase(parent_id);
+        if (not_reachable_it->second.empty())
+          not_reachable_.erase(not_reachable_it);
+      }
+    }
+  }
+}
+
+void AXFuchsiaSemanticProvider::MarkChildrenAsReachable(
+    const std::vector<uint32_t>& child_ids,
+    uint32_t parent_id) {
+  for (const uint32_t child_id : child_ids) {
+    auto it = nodes_.find(child_id);
+    if (it == nodes_.end())
+      not_reachable_[child_id].insert(parent_id);
+    else {
+      it->second.parents.insert(parent_id);
+      if (it->second.parents.size() == 1)
+        not_reachable_.erase(child_id);
+      else
+        not_reachable_[child_id].insert(parent_id);
+    }
+  }
+}
+
+absl::optional<uint32_t> AXFuchsiaSemanticProvider::GetParentForNode(
+    const uint32_t node_id) {
+  const auto it = nodes_.find(node_id);
+  if (it != nodes_.end()) {
+    if (it->second.parents.size() == 1)
+      return *it->second.parents.begin();
+    else
+      return absl::nullopt;
+  }
+
+  const auto not_reachable_it = not_reachable_.find(node_id);
+  if (not_reachable_it != not_reachable_.end()) {
+    if (not_reachable_it->second.size() == 1)
+      return *not_reachable_it->second.begin();
+    else
+      return absl::nullopt;
+  }
+
+  return absl::nullopt;
+}
+
+AXFuchsiaSemanticProvider::Batch&
+AXFuchsiaSemanticProvider::GetCurrentUnfilledBatch(Batch::Type type) {
+  if (batches_.empty() || batches_.back().type() != type ||
+      batches_.back().IsFull())
+    batches_.push_back(Batch(type));
+
+  return batches_.back();
+}
+
+void AXFuchsiaSemanticProvider::OnCommitComplete() {
+  commit_inflight_ = false;
+  TryToCommit();
+}
+
+}  // namespace ui
\ No newline at end of file
diff --git a/ui/accessibility/platform/fuchsia/semantic_provider.h b/ui/accessibility/platform/fuchsia/semantic_provider.h
new file mode 100644
index 0000000..dce679d
--- /dev/null
+++ b/ui/accessibility/platform/fuchsia/semantic_provider.h
@@ -0,0 +1,229 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_ACCESSIBILITY_PLATFORM_FUCHSIA_SEMANTIC_PROVIDER_H_
+#define UI_ACCESSIBILITY_PLATFORM_FUCHSIA_SEMANTIC_PROVIDER_H_
+
+#include <fuchsia/accessibility/semantics/cpp/fidl.h>
+#include <fuchsia/ui/views/cpp/fidl.h>
+#include <lib/fidl/cpp/binding.h>
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/accessibility/ax_export.h"
+
+namespace ui {
+
+// Manages the connection with the Fuchsia Semantics API.
+//
+// Clients instantiate this class, which connects to the Fuchsia semantics API.
+// Semantic nodes can be added or deleted. When a batch of nodes would leave the
+// Fuchsia semantic tree in a valid state, they are committed. Please see
+// |fuchsia.accessibility.semantics| API for more documentation on valid
+// semantic trees.
+class AX_EXPORT AXFuchsiaSemanticProvider
+    : public fuchsia::accessibility::semantics::SemanticListener {
+ public:
+  // A delegate that can be registered by clients of this library to be notified
+  // about Semantic changes.
+  class Delegate {
+   public:
+    Delegate();
+    virtual ~Delegate();
+
+    // Called when the FIDL channel to the Semantics Manager is closed. If this
+    // callback returns true, an attempt to reconnect will be made.
+    virtual bool OnSemanticsManagerConnectionClosed() = 0;
+
+    // Processes an incoming accessibility action from Fuchsia. It
+    // receives the Fuchsia node ID and the action requested. If this
+    // method returns true, this means that the action will be handled.
+    virtual bool OnAccessibilityAction(
+        uint32_t node_id,
+        fuchsia::accessibility::semantics::Action action) = 0;
+
+    // Processes an incoming hit test request from Fuchsia. It
+    // receives a point in Scenic View pixel coordinates and a callback to
+    // return the result when the hit test is done. Please see
+    // |fuchsia.accessibility.semantics.SemanticListener| for documentation on
+    // hit tests.
+    virtual void OnHitTest(fuchsia::math::PointF point,
+                           HitTestCallback callback) = 0;
+
+    // Called whenever Fuchsia enables / disables semantic updates.
+    virtual void OnSemanticsEnabled(bool enabled) = 0;
+  };
+
+  // Arguments:
+  // |view_ref|: identifies the view providing semantics. Please consult
+  // |fuchsia.accessibility.semantics| API documentation.
+  // |pixel_scale|: Scales Scenic view coordinates to pixel coordinates.
+  // |delegate|: Handles semantic requests, please see Delegate class for more
+  // documentation. Caller is responsible for ensuring that |delegate| outlives
+  // |this|.
+  // During construction, this class connects to
+  // |fuchsia.accessibility.semantics.SemanticsManager| to register itself as a
+  // semantic provider.
+  AXFuchsiaSemanticProvider(fuchsia::ui::views::ViewRef view_ref,
+                            float pixel_scale,
+                            Delegate* delegate);
+  ~AXFuchsiaSemanticProvider() override;
+
+  // Returns true if Fuchsia has enabled semantics.
+  bool semantic_updates_enabled() const { return semantic_updates_enabled_; }
+
+  // Adds a semantic node to be updated. It is mandatory that the node has at
+  // least an unique ID.
+  bool Update(fuchsia::accessibility::semantics::Node node);
+
+  // Marks a semantic node to be deleted. Returns false if the node is not
+  // present in the list of semantic nodes known by this provider.
+  bool Delete(uint32_t node_id);
+
+  // Clears the semantic tree.
+  bool Clear();
+
+  // Sends an accessibility event to Fuchsia. Please consult
+  // https://cs.opensource.google/fuchsia/fuchsia/+/master:sdk/fidl/fuchsia.accessibility.semantics/semantics_manager.fidl
+  // for documentation on events.
+  void SendEvent(fuchsia::accessibility::semantics::SemanticEvent event);
+
+  // Returns true if there are pending updates or deletions to be made.
+  bool HasPendingUpdates() const;
+
+ private:
+  // Holds information about a Fuchsia Semantic Node. It contains only the
+  // fields needed to check that the resulting tree would be valid.
+  struct NodeInfo {
+    NodeInfo();
+    ~NodeInfo();
+
+    // During a tree update a node may have multiple parents pointing to it,
+    // although after all updates are processed only one should be present.
+    std::set<uint32_t> parents;
+    std::vector<uint32_t> children;
+  };
+
+  // Represents a batch of nodes to be sent to Fuchsia.
+  // Batches can hold exactly one type: a series of updates or a series of
+  // deletions.
+  class Batch {
+   public:
+    enum class Type { kUpdate, kDelete };
+
+    Batch(Type type);
+    Batch(Batch&& other);
+    ~Batch();
+    Batch(const Batch& other) = delete;
+
+    Type type() const { return type_; }
+
+    // Returns true if the batch has reached its size limit.
+    bool IsFull() const;
+
+    // Adds an update or deletion to the batch. This fails if the batch is full
+    // or if the new item is not the same type of the batch.
+    void Append(fuchsia::accessibility::semantics::Node node);
+    void AppendDeletion(uint32_t delete_node_id);
+
+    // Sends enqueued operations to SemanticsManager.
+    void Apply(
+        fuchsia::accessibility::semantics::SemanticTreePtr* semantic_tree);
+
+   private:
+    Type type_;
+    std::vector<fuchsia::accessibility::semantics::Node> updates_;
+    std::vector<uint32_t> delete_node_ids_;
+  };
+
+  // Attempts to commit the pending updates to Fuchsia if the resulting updates
+  // would leave the final tree in a valid state.
+  void TryToCommit();
+
+  // Returns a batch that can receive an update or deletion depending on |type|.
+  Batch& GetCurrentUnfilledBatch(Batch::Type type);
+
+  // Invoked whenever Fuchsia responds that a commit was received. This tries to
+  // commit again if there are pending upedates or deletions.
+  void OnCommitComplete();
+
+  // Mark all |child_ids| not reachable from |parent_id|, meaning:
+  // - If |parent_id| was the only parent, the children are now disconnected
+  // from the tree.
+  // - If |parent_id| was an additional parent, now the children are connected
+  // to a single parent in the tree.
+  // - If the children do not exist, remove them from the list of nodes waiting
+  // to be updated.
+  void MarkChildrenAsNotReachable(const std::vector<uint32_t>& child_ids,
+                                  uint32_t parent_id);
+
+  // Mark all |child_ids| reachable from |parent_id|, meaning:
+  // - If |parent_id| is the only parent, the children are now connected to the
+  // tree and are all reachable.
+  // - If |parent_id| is an additional parent, now the children are not
+  // connected to the tree as multiple parents point to them.
+  // - If the children do not exist, the parent waits for the nodes to be
+  // created.
+  void MarkChildrenAsReachable(const std::vector<uint32_t>& child_ids,
+                               uint32_t parent_id);
+
+  // Returns the ID of the parent of this node if it has one. If it does not
+  // have a parent or it has multiple parents, returns absl::nullopt.
+  absl::optional<uint32_t> GetParentForNode(const uint32_t node_id);
+
+  // fuchsia::accessibility::semantics::SemanticListener:
+  void OnAccessibilityActionRequested(
+      uint32_t node_id,
+      fuchsia::accessibility::semantics::Action action,
+      fuchsia::accessibility::semantics::SemanticListener::
+          OnAccessibilityActionRequestedCallback callback) override;
+
+  // fuchsia::accessibility::semantics::SemanticListener:
+  void HitTest(::fuchsia::math::PointF local_point,
+               HitTestCallback callback) override;
+
+  // fuchsia::accessibility::semantics::SemanticListener:
+  void OnSemanticsModeChanged(bool update_enabled,
+                              OnSemanticsModeChangedCallback callback) override;
+
+  fuchsia::ui::views::ViewRef view_ref_;
+  float pixel_scale_;
+  Delegate* const delegate_;
+
+  fidl::Binding<fuchsia::accessibility::semantics::SemanticListener>
+      semantic_listener_binding_;
+
+  fuchsia::accessibility::semantics::SemanticTreePtr semantic_tree_;
+
+  bool semantic_updates_enabled_ = true;
+
+  // Nodes from this tree. If not empty, to be considered a valid tree, there
+  // must be:
+  // - A node which node id is equal to kFuchsiaRootNodeId;
+  // - Each node except the root has only one parent;
+  // - All children pointed by a parent exist in the tree.
+  // Only the node ID and the child IDs of the node are stored here because at
+  // this point we only check to see if the tree is valid.
+  std::map<uint32_t /*node_id*/, NodeInfo> nodes_;
+
+  // Key == the node ID that is not reachable from the root of the tree, value
+  // == 0 or more parents that point to this node. Note that nodes can be listed
+  // here but still be present in |nodes_|. This may happen, for example, if the
+  // parent of the node was deleted and there is no path from the root to it, so
+  // the node waits for a parent to connect to it.
+  std::map<uint32_t, std::set<uint32_t>> not_reachable_;
+
+  // Stores batches of node updates or deletions to be sent to Fuchsia. Note
+  // that a batch contains only updates or deletions, because they are pushed to
+  // Fuchsia differently.
+  std::vector<Batch> batches_;
+
+  bool commit_inflight_;
+};
+
+}  // namespace ui
+#endif  // UI_ACCESSIBILITY_PLATFORM_FUCHSIA_SEMANTIC_PROVIDER_H_
diff --git a/ui/accessibility/platform/fuchsia/semantic_provider_test.cc b/ui/accessibility/platform/fuchsia/semantic_provider_test.cc
new file mode 100644
index 0000000..ba5e469
--- /dev/null
+++ b/ui/accessibility/platform/fuchsia/semantic_provider_test.cc
@@ -0,0 +1,441 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "semantic_provider.h"
+
+#include <fuchsia/accessibility/semantics/cpp/fidl.h>
+#include <lib/ui/scenic/cpp/view_ref_pair.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "base/auto_reset.h"
+#include "base/callback.h"
+#include "base/fuchsia/process_context.h"
+#include "base/fuchsia/scoped_service_binding.h"
+#include "base/fuchsia/test_component_context_for_process.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ui {
+namespace {
+
+using fuchsia::accessibility::semantics::Node;
+
+class AXFuchsiaSemanticProviderDelegate
+    : public AXFuchsiaSemanticProvider::Delegate {
+ public:
+  AXFuchsiaSemanticProviderDelegate() = default;
+  ~AXFuchsiaSemanticProviderDelegate() override = default;
+
+  bool OnSemanticsManagerConnectionClosed() override {
+    on_semantics_manager_connectionClosed_called_ = true;
+    return true;
+  }
+
+  bool OnAccessibilityAction(
+      uint32_t node_id,
+      fuchsia::accessibility::semantics::Action action) override {
+    on_accessibility_action_called_ = true;
+    on_accessibility_action_node_id_ = node_id;
+    on_accessibility_action_action_ = std::move(action);
+    return true;
+  }
+
+  void OnHitTest(fuchsia::math::PointF point,
+                 AXFuchsiaSemanticProvider::HitTestCallback callback) override {
+    on_hit_test_called_ = true;
+    on_hit_test_point_ = std::move(point);
+  }
+
+  void OnSemanticsEnabled(bool enabled) override {
+    on_semantics_enabled_called_ = true;
+  }
+
+  bool on_semantics_manager_connectionClosed_called_;
+  bool on_accessibility_action_called_;
+  uint32_t on_accessibility_action_node_id_ = 10000000;
+  fuchsia::accessibility::semantics::Action on_accessibility_action_action_;
+  bool on_hit_test_called_;
+  fuchsia::math::PointF on_hit_test_point_;
+  bool on_semantics_enabled_called_;
+};
+
+// Returns a semantic tree of the form:
+// (0 (1 2 (3 4 (5))))
+std::vector<Node> TreeNodes() {
+  Node node_0;
+  node_0.set_node_id(0u);
+  node_0.set_child_ids({1u, 2u});
+
+  Node node_1;
+  node_1.set_node_id(1u);
+
+  Node node_2;
+  node_2.set_node_id(2u);
+  node_2.set_child_ids({3u, 4u});
+
+  Node node_3;
+  node_3.set_node_id(3u);
+
+  Node node_4;
+  node_4.set_node_id(4u);
+  node_4.set_child_ids({5u});
+
+  Node node_5;
+  node_5.set_node_id(5u);
+
+  std::vector<Node> update;
+  update.push_back(std::move(node_0));
+  update.push_back(std::move(node_1));
+  update.push_back(std::move(node_2));
+  update.push_back(std::move(node_3));
+  update.push_back(std::move(node_4));
+  update.push_back(std::move(node_5));
+  return update;
+}
+
+class AXFuchsiaSemanticProviderTest
+    : public ::testing::Test,
+      public fuchsia::accessibility::semantics::SemanticsManager,
+      public fuchsia::accessibility::semantics::SemanticTree {
+ public:
+  AXFuchsiaSemanticProviderTest()
+      : semantics_manager_bindings_(test_context_.additional_services(), this),
+        semantic_tree_binding_(this) {}
+  ~AXFuchsiaSemanticProviderTest() override = default;
+  AXFuchsiaSemanticProviderTest(const AXFuchsiaSemanticProviderTest&) = delete;
+  AXFuchsiaSemanticProviderTest& operator=(
+      const AXFuchsiaSemanticProviderTest&) = delete;
+  void SetUp() override {
+    auto view_ref_pair = scenic::ViewRefPair::New();
+    delegate_ = std::make_unique<AXFuchsiaSemanticProviderDelegate>();
+
+    semantic_provider_ = std::make_unique<AXFuchsiaSemanticProvider>(
+        std::move(view_ref_pair.view_ref), 2.0f, delegate_.get());
+  }
+
+ protected:
+  // fuchsia::accessibility::semantics::SemanticsManager implementation.
+  void RegisterViewForSemantics(
+      fuchsia::ui::views::ViewRef view_ref,
+      fidl::InterfaceHandle<fuchsia::accessibility::semantics::SemanticListener>
+          listener,
+      fidl::InterfaceRequest<fuchsia::accessibility::semantics::SemanticTree>
+          semantic_tree_request) final {
+    semantic_listener_ = listener.Bind();
+    semantic_tree_binding_.Bind(std::move(semantic_tree_request));
+  }
+
+  // fuchsia::accessibility::semantics::SemanticTree implementation.
+  void UpdateSemanticNodes(
+      std::vector<fuchsia::accessibility::semantics::Node> nodes) final {
+    num_update_semantic_nodes_called_++;
+  }
+  void DeleteSemanticNodes(std::vector<uint32_t> node_ids) final {
+    num_delete_semantic_nodes_called_++;
+  }
+  void CommitUpdates(CommitUpdatesCallback callback) final { callback(); }
+  void SendSemanticEvent(
+      fuchsia::accessibility::semantics::SemanticEvent semantic_event,
+      SendSemanticEventCallback callback) override {
+    callback();
+  }
+
+  // Required because of |test_context_|.
+  base::test::SingleThreadTaskEnvironment task_environment_{
+      base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
+  base::TestComponentContextForProcess test_context_;
+  // Binding to fake Semantics Manager Fuchsia service, implemented by this test
+  // class.
+  base::ScopedServiceBinding<
+      fuchsia::accessibility::semantics::SemanticsManager>
+      semantics_manager_bindings_;
+
+  uint32_t num_update_semantic_nodes_called_ = 0;
+  uint32_t num_delete_semantic_nodes_called_ = 0;
+
+  base::RepeatingClosure on_commit_;
+
+  fuchsia::accessibility::semantics::SemanticListenerPtr semantic_listener_;
+  fidl::Binding<fuchsia::accessibility::semantics::SemanticTree>
+      semantic_tree_binding_;
+  std::unique_ptr<AXFuchsiaSemanticProviderDelegate> delegate_;
+  std::unique_ptr<AXFuchsiaSemanticProvider> semantic_provider_;
+};
+
+TEST_F(AXFuchsiaSemanticProviderTest, HandlesOnSemanticsConnectionClosed) {
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+  semantic_tree_binding_.Close(ZX_ERR_PEER_CLOSED);
+  loop.RunUntilIdle();
+  EXPECT_TRUE(delegate_->on_semantics_manager_connectionClosed_called_);
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, HandlesOnAccessibilityAction) {
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+  bool action_handled = false;
+  semantic_listener_->OnAccessibilityActionRequested(
+      /*node_id=*/1u, fuchsia::accessibility::semantics::Action::DEFAULT,
+      [&action_handled](bool handled) { action_handled = handled; });
+  loop.RunUntilIdle();
+  EXPECT_TRUE(action_handled);
+  EXPECT_TRUE(delegate_->on_accessibility_action_called_);
+  EXPECT_EQ(delegate_->on_accessibility_action_node_id_, 1u);
+  EXPECT_EQ(delegate_->on_accessibility_action_action_,
+            fuchsia::accessibility::semantics::Action::DEFAULT);
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, HandlesOnHitTest) {
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+
+  // Note that the point is sent here and will be converted according to the
+  // device scale used. Only then it gets sent to the handler, which receives
+  // the value already with the proper scaling.
+  fuchsia::math::PointF point;
+  point.x = 4;
+  point.y = 6;
+  semantic_listener_->HitTest(std::move(point), [](auto...) {});
+  loop.RunUntilIdle();
+  EXPECT_TRUE(delegate_->on_hit_test_called_);
+  EXPECT_EQ(delegate_->on_hit_test_point_.x, 8.0);
+  EXPECT_EQ(delegate_->on_hit_test_point_.y, 12.0);
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, HandlesOnSemanticsEnabled) {
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+  semantic_listener_->OnSemanticsModeChanged(false, [](auto...) {});
+  loop.RunUntilIdle();
+  EXPECT_TRUE(delegate_->on_semantics_enabled_called_);
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, SendsRootOnly) {
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+  Node root;
+  root.set_node_id(0u);
+  EXPECT_TRUE(semantic_provider_->Update(std::move(root)));
+  loop.RunUntilIdle();
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, SendsNodesFromRootToLeaves) {
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+  auto tree_nodes = TreeNodes();
+  for (auto& node : tree_nodes) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+  loop.RunUntilIdle();
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, SendsNodesFromLeavesToRoot) {
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+  auto nodes = TreeNodes();
+  std::reverse(nodes.begin(), nodes.end());
+  for (auto& node : nodes) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+  loop.RunUntilIdle();
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest,
+       SendsNodesOnlyAfterParentNoLongerPointsToDeletedChild) {
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+  auto tree_nodes = TreeNodes();
+  for (auto& node : tree_nodes) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+  loop.RunUntilIdle();
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+
+  // Deletes node 5, which is a child of 4.
+  EXPECT_TRUE(semantic_provider_->Delete(5u));
+  loop.RunUntilIdle();
+
+  // Commit is pending, because the parent still points to the child.
+  EXPECT_TRUE(semantic_provider_->HasPendingUpdates());
+
+  Node node_4;
+  node_4.set_node_id(4u);
+  node_4.set_child_ids({});
+  EXPECT_TRUE(semantic_provider_->Update(std::move(node_4)));
+  loop.RunUntilIdle();
+  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
+  EXPECT_EQ(num_delete_semantic_nodes_called_, 1u);
+
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest,
+       SendsNodesOnlyAfterDanglingChildIsDeleted) {
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+  auto tree_nodes = TreeNodes();
+  for (auto& node : tree_nodes) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+  loop.RunUntilIdle();
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+
+  Node node_4;
+  node_4.set_node_id(4u);
+  node_4.set_child_ids({});  // This removes child 5.
+  EXPECT_TRUE(semantic_provider_->Update(std::move(node_4)));
+  loop.RunUntilIdle();
+  EXPECT_TRUE(semantic_provider_->HasPendingUpdates());
+
+  EXPECT_TRUE(semantic_provider_->Delete(5u));
+  loop.RunUntilIdle();
+  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
+  EXPECT_EQ(num_delete_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, ReparentsNodeWithADeletion) {
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+  auto tree_nodes = TreeNodes();
+  for (auto& node : tree_nodes) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+  loop.RunUntilIdle();
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+
+  // Deletes node 4 to reparent its child (5).
+  EXPECT_TRUE(semantic_provider_->Delete(4u));
+  loop.RunUntilIdle();
+  EXPECT_TRUE(semantic_provider_->HasPendingUpdates());
+
+  // Add child 5 to another node.
+  Node node_1;
+  node_1.set_node_id(1u);
+  node_1.set_child_ids({5u});
+  EXPECT_TRUE(semantic_provider_->Update(std::move(node_1)));
+  loop.RunUntilIdle();
+  EXPECT_TRUE(semantic_provider_->HasPendingUpdates());
+
+  Node node_4;
+  node_4.set_node_id(4u);
+  node_4.set_child_ids({});
+  EXPECT_TRUE(semantic_provider_->Update(std::move(node_4)));
+  loop.RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
+  EXPECT_EQ(num_delete_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, ReparentsNodeWithAnUpdate) {
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+  auto tree_nodes = TreeNodes();
+  for (auto& node : tree_nodes) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+  loop.RunUntilIdle();
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+
+  // Add child 5 to another node. Note that 5 will have two parents, and the
+  // commit must be held until it has only one.
+  Node node_1;
+  node_1.set_node_id(1u);
+  node_1.set_child_ids({5u});
+  EXPECT_TRUE(semantic_provider_->Update(std::move(node_1)));
+  loop.RunUntilIdle();
+  EXPECT_TRUE(semantic_provider_->HasPendingUpdates());
+
+  // Updates node 4 to no longer point to 5.
+  Node node_4;
+  node_4.set_node_id(4u);
+  node_4.set_child_ids({});
+  EXPECT_TRUE(semantic_provider_->Update(std::move(node_4)));
+  loop.RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
+  EXPECT_EQ(num_delete_semantic_nodes_called_, 0u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, ChangesRoot) {
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+  auto tree_nodes = TreeNodes();
+  for (auto& node : tree_nodes) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+  loop.RunUntilIdle();
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+
+  Node new_root;
+  new_root.set_node_id(0u);
+  new_root.set_child_ids({1u, 2u});
+  EXPECT_TRUE(semantic_provider_->Update(std::move(new_root)));
+  loop.RunUntilIdle();
+  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
+  EXPECT_EQ(num_delete_semantic_nodes_called_, 0u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, BatchesUpdates) {
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+  std::vector<Node> updates;
+  for (uint32_t i = 0; i < 30; ++i) {
+    Node node;
+    node.set_node_id(i);
+    node.set_child_ids({i + 1});
+    updates.push_back(std::move(node));
+  }
+  updates.back().clear_child_ids();
+
+  for (auto& node : updates) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+  loop.RunUntilIdle();
+
+  // 30 nodes in batches of 16 (default value of maximum nodes per update call),
+  // should result in two update calls to the semantics API.
+  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, ClearsTree) {
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+  auto tree_nodes = TreeNodes();
+  for (auto& node : tree_nodes) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+  loop.RunUntilIdle();
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+
+  semantic_provider_->Clear();
+  loop.RunUntilIdle();
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_EQ(num_delete_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+}  // namespace
+}  // namespace ui
\ No newline at end of file
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index 6e4e334b..6916cd4 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -139,7 +139,7 @@
     "java/res/color/default_text_color_inverse_list.xml",
     "java/res/color/default_text_color_light_list.xml",
     "java/res/color/default_text_color_list_baseline.xml",
-    "java/res/color/default_text_color_on_accent1_list.xml",
+    "java/res/color/default_text_color_on_accent1_baseline_list.xml",
     "java/res/color/default_text_color_secondary_light_list.xml",
     "java/res/color/default_text_color_secondary_list_baseline.xml",
     "java/res/color/filled_button_bg.xml",
diff --git a/ui/android/delegated_frame_host_android.cc b/ui/android/delegated_frame_host_android.cc
index fe454e1..2d47a4b3 100644
--- a/ui/android/delegated_frame_host_android.cc
+++ b/ui/android/delegated_frame_host_android.cc
@@ -188,6 +188,19 @@
   client_->WasEvicted();
 }
 
+void DelegatedFrameHostAndroid::ClearFallbackSurfaceForCommitPending() {
+  const absl::optional<viz::SurfaceId> fallback_surface_id =
+      content_layer_->oldest_acceptable_fallback();
+
+  // CommitPending without a target for TakeFallbackContentFrom. Since we cannot
+  // guarantee that Navigation will complete, evict our surfaces which are from
+  // a previous Navigation.
+  if (fallback_surface_id && fallback_surface_id->is_valid()) {
+    EvictDelegatedFrame();
+    content_layer_->SetOldestAcceptableFallback(viz::SurfaceId());
+  }
+}
+
 void DelegatedFrameHostAndroid::ResetFallbackToFirstNavigationSurface() {
   // Don't update the fallback if it's already newer than the first id after
   // navigation.
diff --git a/ui/android/delegated_frame_host_android.h b/ui/android/delegated_frame_host_android.h
index 837af08..fafbce2f 100644
--- a/ui/android/delegated_frame_host_android.h
+++ b/ui/android/delegated_frame_host_android.h
@@ -83,6 +83,7 @@
     return TimeDeltaToFrames(ResizeTimeout());
   }
 
+  void ClearFallbackSurfaceForCommitPending();
   // Advances the fallback surface to the first surface after navigation. This
   // ensures that stale surfaces are not presented to the user for an indefinite
   // period of time.
diff --git a/ui/android/java/res/color/default_text_color_on_accent1_list.xml b/ui/android/java/res/color/default_text_color_on_accent1_baseline_list.xml
similarity index 74%
rename from ui/android/java/res/color/default_text_color_on_accent1_list.xml
rename to ui/android/java/res/color/default_text_color_on_accent1_baseline_list.xml
index e126c52..4752ad4 100644
--- a/ui/android/java/res/color/default_text_color_on_accent1_list.xml
+++ b/ui/android/java/res/color/default_text_color_on_accent1_baseline_list.xml
@@ -4,6 +4,6 @@
      found in the LICENSE file.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="@color/default_text_color_on_accent1_disabled" android:state_enabled="false" />
-    <item android:color="@color/default_text_color_on_accent1"/>
+    <item android:color="@color/default_text_color_on_accent1_disabled_baseline" android:state_enabled="false" />
+    <item android:color="@color/default_text_color_on_accent1_baseline"/>
 </selector>
diff --git a/ui/android/java/res/values-night/colors.xml b/ui/android/java/res/values-night/colors.xml
index 4f43bda..518bb081 100644
--- a/ui/android/java/res/values-night/colors.xml
+++ b/ui/android/java/res/values-night/colors.xml
@@ -12,8 +12,8 @@
     <color name="default_text_color_link">@color/default_text_color_link_light</color>
     <color name="default_text_color_link_disabled">@color/default_text_color_link_disabled_light</color>
     <color name="default_text_color_error">@color/default_text_color_error_light</color>
-    <color name="default_text_color_on_accent1">@color/default_text_color_on_accent1_dark</color>
-    <color name="default_text_color_on_accent1_disabled">@color/default_text_color_disabled_light</color>
+    <color name="default_text_color_on_accent1_baseline">@color/default_text_color_on_accent1_dark</color>
+    <color name="default_text_color_on_accent1_disabled_baseline">@color/default_text_color_disabled_light</color>
 
     <!-- Common icon colors for drawables. -->
     <color name="default_icon_color_baseline">@color/default_icon_color_light</color>
diff --git a/ui/android/java/res/values-v17/styles.xml b/ui/android/java/res/values-v17/styles.xml
index 32dc9a0..46440fa 100644
--- a/ui/android/java/res/values-v17/styles.xml
+++ b/ui/android/java/res/values-v17/styles.xml
@@ -242,21 +242,6 @@
         <item name="android:textColor">@color/default_text_color_disabled_dark</item>
     </style>
 
-    <!-- Inverse text styles -->
-    <!-- Text styles used on dark background on light theme, and light background on dark theme. -->
-    <style name="TextAppearance.Headline.Primary.Baseline.Inverse" tools:ignore="UnusedResources">
-        <item name="android:textColor">@color/default_text_color_inverse</item>
-    </style>
-    <style name="TextAppearance.TextMediumThick.Primary.Baseline.Inverse" tools:ignore="UnusedResources">
-        <item name="android:textColor">@color/default_text_color_inverse</item>
-    </style>
-    <style name="TextAppearance.TextMedium.Primary.Baseline.Inverse" tools:ignore="UnusedResources">
-        <item name="android:textColor">@color/default_text_color_inverse</item>
-    </style>
-    <style name="TextAppearance.TextSmall.Primary.Baseline.Inverse" tools:ignore="UnusedResources">
-        <item name="android:textColor">@color/default_text_color_inverse</item>
-    </style>
-
     <!-- Blue And Button Text Styles -->
     <style name="TextAppearance.TextMediumThick.Green">
         <item name="android:textColor">@color/default_green</item>
@@ -274,11 +259,11 @@
         <item name="android:textColor">@color/blue_when_enabled_dark</item>
     </style>
     <style name="TextAppearance.Button.Text.Filled" parent="TextAppearance.TextAccentMediumThick">
-        <item name="android:textColor">@color/default_text_color_on_accent1_list</item>
+        <item name="android:textColor">@color/default_text_color_on_accent1_baseline_list</item>
     </style>
     <style name="TextAppearance.Button.Text.Inverse" parent="TextAppearance.TextAccentMediumThick"
         tools:ignore="UnusedResources">
-        <item name="android:textColor">@color/default_text_color_on_accent1_list</item>
+        <item name="android:textColor">@color/default_text_color_on_accent1_baseline_list</item>
     </style>
     <style name="TextAppearance.MenuChip.Text.Blue" parent="TextAppearance.Button.Text.Blue">
        <item name="android:textSize">@dimen/text_size_small</item>
diff --git a/ui/android/java/res/values/semantic_colors_adaptive.xml b/ui/android/java/res/values/semantic_colors_adaptive.xml
index 4e463815..dfaf9fb 100644
--- a/ui/android/java/res/values/semantic_colors_adaptive.xml
+++ b/ui/android/java/res/values/semantic_colors_adaptive.xml
@@ -17,8 +17,8 @@
     <color name="default_text_color_link">@color/default_text_color_link_dark</color>
     <color name="default_text_color_link_disabled">@color/default_text_color_link_disabled_dark</color>
     <color name="default_text_color_error">@color/default_text_color_error_dark</color>
-    <color name="default_text_color_on_accent1">@color/default_text_color_on_accent1_light</color>
-    <color name="default_text_color_on_accent1_disabled">@color/default_text_color_disabled_dark</color>
+    <color name="default_text_color_on_accent1_baseline">@color/default_text_color_on_accent1_light</color>
+    <color name="default_text_color_on_accent1_disabled_baseline">@color/default_text_color_disabled_dark</color>
     <!-- TODO(https://crbug.com/1248157): This is the same as light mode's
          ?attr/colorOnSecondaryContainer, but it would break in dark mode.
          Figure out a solution for dynamic colors. -->
diff --git a/ui/android/java/src/org/chromium/ui/widget/TextViewWithTightWrapTest.java b/ui/android/java/src/org/chromium/ui/widget/TextViewWithTightWrapTest.java
index 4263f86..ac32944 100644
--- a/ui/android/java/src/org/chromium/ui/widget/TextViewWithTightWrapTest.java
+++ b/ui/android/java/src/org/chromium/ui/widget/TextViewWithTightWrapTest.java
@@ -29,8 +29,8 @@
 */
 @RunWith(BaseJUnit4ClassRunner.class)
 public class TextViewWithTightWrapTest extends DummyUiActivityTestCase {
-    private static final int RENDER_TEST_REVISION = 1;
-    private static final String RENDER_TEST_REVISION_DESCRIPTION = "Fix the background color.";
+    private static final int RENDER_TEST_REVISION = 2;
+    private static final String RENDER_TEST_REVISION_DESCRIPTION = "Update the text style.";
 
     private TextViewWithTightWrap mTextView;
     private View mView;
diff --git a/ui/base/ime/ash/ime_bridge.h b/ui/base/ime/ash/ime_bridge.h
index b46ae9e..8cf444d 100644
--- a/ui/base/ime/ash/ime_bridge.h
+++ b/ui/base/ime/ash/ime_bridge.h
@@ -27,12 +27,18 @@
   ~IMEBridge();
 
   // Allocates the global instance. Must be called before any calls to Get().
+  // TODO(crbug/1279743): This is a stateful global. Make it into true global
+  // singleton first, then use dependency injection instead in the next step.
   static void Initialize();
 
   // Releases the global instance.
+  // TODO(crbug/1279743): This is a stateful global. Make it into true global
+  // singleton first, then use dependency injection instead in the next step.
   static void Shutdown();
 
   // Returns IMEBridge global instance. Initialize() must be called first.
+  // TODO(crbug/1279743): This is a stateful global. Make it into true global
+  // singleton first, then use dependency injection instead in the next step.
   static IMEBridge* Get();
 
   // Returns current InputContextHandler. This function returns nullptr if input
diff --git a/ui/base/ime/ash/input_method_manager.h b/ui/base/ime/ash/input_method_manager.h
index 8ef1c1b..0da90a42 100644
--- a/ui/base/ime/ash/input_method_manager.h
+++ b/ui/base/ime/ash/input_method_manager.h
@@ -277,16 +277,22 @@
 
   // Gets the global instance of InputMethodManager. Initialize() must be called
   // first.
+  // TODO(crbug/1279743): This is a stateful global. Make it into true global
+  // singleton first, then use dependency injection instead in the next step.
   static COMPONENT_EXPORT(UI_BASE_IME_ASH) InputMethodManager* Get();
 
   // Sets the global instance. |instance| will be owned by the internal pointer
   // and deleted by Shutdown().
   // TODO(nona): Instanciate InputMethodManagerImpl inside of this function once
   //             crbug.com/164375 is fixed.
+  // TODO(crbug/1279743): This is a stateful global. Make it into true global
+  // singleton first, then use dependency injection instead in the next step.
   static COMPONENT_EXPORT(UI_BASE_IME_ASH) void Initialize(
       InputMethodManager* instance);
 
   // Destroy the global instance.
+  // TODO(crbug/1279743): This is a stateful global. Make it into true global
+  // singleton first, then use dependency injection instead in the next step.
   static COMPONENT_EXPORT(UI_BASE_IME_ASH) void Shutdown();
 
   // Adds an observer to receive notifications of input method related
diff --git a/weblayer/browser/content_browser_client_impl.cc b/weblayer/browser/content_browser_client_impl.cc
index fc64b37..f7797c1 100644
--- a/weblayer/browser/content_browser_client_impl.cc
+++ b/weblayer/browser/content_browser_client_impl.cc
@@ -1227,4 +1227,9 @@
   return true;
 }
 
+bool ContentBrowserClientImpl::ShouldPreconnectNavigation(
+    content::BrowserContext* browser_context) {
+  return true;
+}
+
 }  // namespace weblayer
diff --git a/weblayer/browser/content_browser_client_impl.h b/weblayer/browser/content_browser_client_impl.h
index 78c6db20..a3d515d 100644
--- a/weblayer/browser/content_browser_client_impl.h
+++ b/weblayer/browser/content_browser_client_impl.h
@@ -226,6 +226,8 @@
   bool HasErrorPage(int http_status_code) override;
   bool IsClipboardPasteAllowed(
       content::RenderFrameHost* render_frame_host) override;
+  bool ShouldPreconnectNavigation(
+      content::BrowserContext* browser_context) override;
 
   void CreateFeatureListAndFieldTrials();