diff --git a/DEPS b/DEPS
index 32456cc..8bcd61d 100644
--- a/DEPS
+++ b/DEPS
@@ -295,11 +295,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': '9dc12704a5bffd1ff42310211eaa421bf1b6f6de',
+  'src_internal_revision': '5c45e13e948d0c028bd62c61ea316ec5d4e16ce1',
   # 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': 'b70fc925125553a4eb8bb2f123e225f057aa35d8',
+  'skia_revision': 'bb255dd0252e256a28542475b22711346c319030',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -307,7 +307,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': '9b16a3e925e7763e0c4b45807abcf65864688762',
+  'angle_revision': 'db71e8fa7c26d18f76d7b9e9474447b20f1c73b3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -331,7 +331,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling googletest
   # and whatever else without interference from each other.
-  'googletest_revision': '9f79a9597ad9b5394e5c620ebf76824f77ffbde4',
+  'googletest_revision': '571930618fa96eabcd05b573285edbee9fc13bae',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling lighttpd
   # and whatever else without interference from each other.
@@ -351,7 +351,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '7172bd11badd468f6a86dba0b1769d624ead885c',
+  'freetype_revision': '8082aba5f2ddc334b6dfe89a10db13a1b120f76e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -375,7 +375,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
-  'crossbench_revision': 'a6913f411c9deff814721268135cba809042a669',
+  'crossbench_revision': '354a6230a3a7bc437dd3f512a6decd3155eb15fc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -391,7 +391,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '9e176519afd505a405a50d9312c926ef97003ec3',
+  'devtools_frontend_revision': '2755713bf6b0ca46d6e60124a0633f45c2d54677',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -415,7 +415,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': 'bebd40b19d96dea39d582b866dfe751dded1c33c',
+  'dawn_revision': 'f1ac40b54eb53282eff2e587a08fae81de9408ea',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1486,7 +1486,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '6dc6dcf374257e18cd794bba4500e3a4ea9fd307',
+    'b98a5841d71a8998413b6811f80897742ea8b9e1',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -2500,7 +2500,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '652bdb7719f30b52b08e506645a7322ff1b2cc6f',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + 'bca4abcec3de975c0c1b9a60334d9fee1b7f43a1',
+    Var('chromium_git') + '/openscreen' + '@' + '4b64bb75ad30c5d9e170caa21c6f5065583cb49c',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + '781f2eab3698d653c804ecbd11e0aed47eaad1c6',
@@ -2894,7 +2894,7 @@
     Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'),
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '691c150f9232901f75368bf741281f5a8f5ddefa',
+    Var('webrtc_git') + '/src.git' + '@' + 'e30e216756cb9bdee35074b8020a78e26c981d94',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -4614,7 +4614,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        '97235e2248772e44aa4ea07928d220def6a00f66',
+        '9793b5313489e7bedaba7b2eb051eb37a6cccc2b',
       'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt b/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt
index 65065a1..e903153 100644
--- a/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt
@@ -5526,31 +5526,6 @@
     method destroy
     method detect
     method measureInputUsage
-interface LanguageModel : EventTarget
-    static method availability
-    static method create
-    static method params
-    attribute @@toStringTag
-    getter inputQuota
-    getter inputUsage
-    getter onquotaoverflow
-    getter temperature
-    getter topK
-    method append
-    method clone
-    method constructor
-    method destroy
-    method measureInputUsage
-    method prompt
-    method promptStreaming
-    setter onquotaoverflow
-interface LanguageModelParams
-    attribute @@toStringTag
-    getter defaultTemperature
-    getter defaultTopK
-    getter maxTemperature
-    getter maxTopK
-    method constructor
 interface LargestContentfulPaint : PerformanceEntry
     attribute @@toStringTag
     getter element
@@ -7783,23 +7758,6 @@
     method formData
     method json
     method text
-interface Rewriter
-    static method availability
-    static method create
-    attribute @@toStringTag
-    getter expectedContextLanguages
-    getter expectedInputLanguages
-    getter format
-    getter inputQuota
-    getter length
-    getter outputLanguage
-    getter sharedContext
-    getter tone
-    method constructor
-    method destroy
-    method measureInputUsage
-    method rewrite
-    method rewriteStreaming
 interface SVGAElement : SVGGraphicsElement
     attribute @@toStringTag
     getter href
@@ -9400,23 +9358,6 @@
     method unwrapKey
     method verify
     method wrapKey
-interface Summarizer
-    static method availability
-    static method create
-    attribute @@toStringTag
-    getter expectedContextLanguages
-    getter expectedInputLanguages
-    getter format
-    getter inputQuota
-    getter length
-    getter outputLanguage
-    getter sharedContext
-    getter type
-    method constructor
-    method destroy
-    method measureInputUsage
-    method summarize
-    method summarizeStreaming
 interface SuppressedError : Error
     attribute message
     attribute name
@@ -9607,6 +9548,7 @@
     attribute @@toStringTag
     getter newState
     getter oldState
+    getter source
     method constructor
 interface Touch
     attribute @@toStringTag
@@ -9674,18 +9616,6 @@
     getter propertyName
     getter pseudoElement
     method constructor
-interface Translator
-    static method availability
-    static method create
-    attribute @@toStringTag
-    getter inputQuota
-    getter sourceLanguage
-    getter targetLanguage
-    method constructor
-    method destroy
-    method measureInputUsage
-    method translate
-    method translateStreaming
 interface TreeWalker
     attribute @@toStringTag
     getter currentNode
@@ -11543,23 +11473,6 @@
     method constructor
     method releaseLock
     method write
-interface Writer
-    static method availability
-    static method create
-    attribute @@toStringTag
-    getter expectedContextLanguages
-    getter expectedInputLanguages
-    getter format
-    getter inputQuota
-    getter length
-    getter outputLanguage
-    getter sharedContext
-    getter tone
-    method constructor
-    method destroy
-    method measureInputUsage
-    method write
-    method writeStreaming
 interface XMLDocument : Document
     attribute @@toStringTag
     method constructor
diff --git a/ash/webui/boca_ui/boca_app_page_handler.cc b/ash/webui/boca_ui/boca_app_page_handler.cc
index b9aab71..84c42f2 100644
--- a/ash/webui/boca_ui/boca_app_page_handler.cc
+++ b/ash/webui/boca_ui/boca_app_page_handler.cc
@@ -12,6 +12,7 @@
 #include "ash/screen_util.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
+#include "ash/webui/boca_ui/mojom/boca.mojom-data-view.h"
 #include "ash/webui/boca_ui/mojom/boca.mojom-forward.h"
 #include "ash/webui/boca_ui/mojom/boca.mojom-shared.h"
 #include "ash/webui/boca_ui/mojom/boca.mojom.h"
@@ -194,6 +195,14 @@
                             .connection_param()
                             .connection_code();
     }
+
+    // If sign in more than one device, flip status to be inactive and
+    if (item.second.devices().size() > 1) {
+      device_active = false;
+      student_status_detail =
+          mojom::StudentStatusDetail::kMultipleDeviceSignedIn;
+    }
+
     auto identity_ptr = mojom::IdentifiedActivity::New(
         item.first, mojom::StudentActivity::New(
                         student_status_detail, device_active, active_tab,
@@ -370,11 +379,16 @@
 }
 
 void BocaAppHandler::EndSession(EndSessionCallback callback) {
+  if (GetSessionManager()->end_session_callback_for_testing()) {
+    CHECK_IS_TEST();
+    std::move(GetSessionManager()->end_session_callback_for_testing()).Run();
+  }
   auto* session = GetSessionManager()->GetCurrentSession();
   if (!session || session->session_state() != ::boca::Session::ACTIVE) {
     std::move(callback).Run(mojom::UpdateSessionError::kInvalid);
     return;
   }
+
   std::unique_ptr<UpdateSessionRequest> request =
       std::make_unique<UpdateSessionRequest>(
           session_client_impl_->sender(), base_url_, user_identity_,
diff --git a/ash/webui/boca_ui/boca_app_page_handler_unittest.cc b/ash/webui/boca_ui/boca_app_page_handler_unittest.cc
index 29248e0..8222f9a 100644
--- a/ash/webui/boca_ui/boca_app_page_handler_unittest.cc
+++ b/ash/webui/boca_ui/boca_app_page_handler_unittest.cc
@@ -444,7 +444,6 @@
         content::WebContents::CreateParams(browser_context_));
     web_ui_ = std::make_unique<content::TestWebUI>();
     web_ui_->set_web_contents(web_contents_.get());
-    CreateBocaAppHandler(/*is_producer=*/true);
   }
 
   void TearDown() override {
@@ -460,6 +459,7 @@
 
  protected:
   void CreateBocaAppHandler(bool is_producer) {
+    is_producer_ = is_producer;
     mojo::PendingReceiver<mojom::Page> page_pending_receiver;
     remote_.reset();
     // `BocaAppClient::GetSessionManager` should be called exactly once on
@@ -538,10 +538,12 @@
   }
 
  private:
+  bool is_producer_;
   base::test::ScopedFeatureList scoped_feature_list_;
   content::BrowserTaskEnvironment task_environment_;
   sync_preferences::TestingPrefServiceSyncable pref_service_;
   TestingPrefServiceSimple local_state_;
+  ::boca::Session session = GetCommonActiveSessionProto();
   session_manager::SessionManager device_session_manager_;
 
   user_manager::TypedScopedUserManager<user_manager::FakeUserManager>
@@ -562,7 +564,23 @@
   raw_ptr<content::BrowserContext> browser_context_;
 };
 
-TEST_F(BocaAppPageHandlerTest, CreateSessionWithFullInput) {
+class BocaAppPageHandlerProducerTest : public BocaAppPageHandlerTest {
+ public:
+  void SetUp() override {
+    BocaAppPageHandlerTest::SetUp();
+    CreateBocaAppHandler(/*is_producer=*/true);
+  }
+};
+
+class BocaAppPageHandlerConsumerTest : public BocaAppPageHandlerTest {
+ public:
+  void SetUp() override {
+    BocaAppPageHandlerTest::SetUp();
+    CreateBocaAppHandler(/*is_producer=*/false);
+  }
+};
+
+TEST_F(BocaAppPageHandlerProducerTest, CreateSessionWithFullInput) {
   auto session_duration = base::Minutes(2);
 
   std::vector<mojom::IdentityPtr> students;
@@ -701,7 +719,7 @@
   EXPECT_FALSE(future_1.Get().has_value());
 }
 
-TEST_F(BocaAppPageHandlerTest, CreateSessionWithCritialInputOnly) {
+TEST_F(BocaAppPageHandlerProducerTest, CreateSessionWithCritialInputOnly) {
   auto session_duration = base::Minutes(2);
 
   // Page handler callback.
@@ -753,7 +771,7 @@
   EXPECT_FALSE(future_1.Get().has_value());
 }
 
-TEST_F(BocaAppPageHandlerTest, CreateSessionFailedWithHttpError) {
+TEST_F(BocaAppPageHandlerProducerTest, CreateSessionFailedWithHttpError) {
   base::HistogramTester histogram_tester;
   auto session_duration = base::Minutes(2);
 
@@ -807,7 +825,7 @@
                                      1);
 }
 
-TEST_F(BocaAppPageHandlerTest, CreateSessionFailedOnNonManagedNetwork) {
+TEST_F(BocaAppPageHandlerProducerTest, CreateSessionFailedOnNonManagedNetwork) {
   // Page handler callback.
   base::test::TestFuture<base::expected<std::unique_ptr<::boca::Session>,
                                         google_apis::ApiErrorCode>>
@@ -844,7 +862,7 @@
             future_1.Get().value());
 }
 
-TEST_F(BocaAppPageHandlerTest, GetSessionWithFullInputTest) {
+TEST_F(BocaAppPageHandlerConsumerTest, GetSessionWithFullInputTest) {
   // Page handler callback.
   base::test::TestFuture<base::expected<std::unique_ptr<::boca::Session>,
                                         google_apis::ApiErrorCode>>
@@ -933,7 +951,6 @@
               UpdateCurrentSession(NotNull(), /*dispatch_event=*/true))
       .Times(1);
 
-  CreateBocaAppHandler(/*is_producer=*/false);
   EXPECT_CALL(*session_manager(), disabled_on_non_managed_network())
       .WillOnce(Return(false));
   boca_app_handler()->GetSession(future_1.GetCallback());
@@ -982,7 +999,7 @@
   EXPECT_EQ("google", activities[0]->activity->active_tab);
 }
 
-TEST_F(BocaAppPageHandlerTest, GetSessionWithPartialInputTest) {
+TEST_F(BocaAppPageHandlerProducerTest, GetSessionWithPartialInputTest) {
   // Page handler callback.
   base::test::TestFuture<base::expected<std::unique_ptr<::boca::Session>,
                                         google_apis::ApiErrorCode>>
@@ -1011,7 +1028,7 @@
   EXPECT_EQ(120, result->session_duration.InSeconds());
 }
 
-TEST_F(BocaAppPageHandlerTest, GetSessionWithHTTPError) {
+TEST_F(BocaAppPageHandlerProducerTest, GetSessionWithHTTPError) {
   base::HistogramTester histogram_tester;
   // Page handler callback.
   base::test::TestFuture<base::expected<std::unique_ptr<::boca::Session>,
@@ -1044,7 +1061,7 @@
       google_apis::ApiErrorCode::HTTP_BAD_REQUEST, 1);
 }
 
-TEST_F(BocaAppPageHandlerTest, GetSessionWithNullPtrInputTest) {
+TEST_F(BocaAppPageHandlerProducerTest, GetSessionWithNullPtrInputTest) {
   // Page handler callback.
   base::test::TestFuture<base::expected<std::unique_ptr<::boca::Session>,
                                         google_apis::ApiErrorCode>>
@@ -1071,7 +1088,7 @@
   EXPECT_EQ(mojom::GetSessionError::kEmpty, result->get_error());
 }
 
-TEST_F(BocaAppPageHandlerTest, GetSessionWithNonActiveSessionTest) {
+TEST_F(BocaAppPageHandlerProducerTest, GetSessionWithNonActiveSessionTest) {
   // Page handler callback.
   base::test::TestFuture<base::expected<std::unique_ptr<::boca::Session>,
                                         google_apis::ApiErrorCode>>
@@ -1097,7 +1114,7 @@
   EXPECT_EQ(mojom::GetSessionError::kEmpty, result->get_error());
 }
 
-TEST_F(BocaAppPageHandlerTest,
+TEST_F(BocaAppPageHandlerProducerTest,
        GetSessionWithEmptySessionConfigShouldNotCrashTest) {
   // Page handler callback.
   base::test::TestFuture<base::expected<std::unique_ptr<::boca::Session>,
@@ -1125,7 +1142,7 @@
   ASSERT_FALSE(result->is_error());
 }
 
-TEST_F(BocaAppPageHandlerTest,
+TEST_F(BocaAppPageHandlerProducerTest,
        GetSessionWithNonManagedNetworkShouldReturnEmpty) {
   // Page handler callback.
   base::test::TestFuture<base::expected<std::unique_ptr<::boca::Session>,
@@ -1149,7 +1166,7 @@
   ASSERT_TRUE(result->is_error());
 }
 
-TEST_F(BocaAppPageHandlerTest, EndSessionSucceed) {
+TEST_F(BocaAppPageHandlerProducerTest, EndSessionSucceed) {
   auto* session_id = "123";
   auto session = std::make_unique<::boca::Session>();
   session->mutable_duration()->set_seconds(120);
@@ -1188,7 +1205,7 @@
   EXPECT_FALSE(future_1.Get().has_value());
 }
 
-TEST_F(BocaAppPageHandlerTest, EndSessionWithHTTPFailure) {
+TEST_F(BocaAppPageHandlerProducerTest, EndSessionWithHTTPFailure) {
   base::HistogramTester histogram_tester;
   auto* session_id = "123";
   auto session = std::make_unique<::boca::Session>();
@@ -1230,7 +1247,7 @@
                                      1);
 }
 
-TEST_F(BocaAppPageHandlerTest, EndSessionWithEmptyResponse) {
+TEST_F(BocaAppPageHandlerProducerTest, EndSessionWithEmptyResponse) {
   EXPECT_CALL(*session_manager(), GetCurrentSession())
       .WillOnce(Return(nullptr));
 
@@ -1242,7 +1259,7 @@
   EXPECT_EQ(mojom::UpdateSessionError::kInvalid, future_1.Get().value());
 }
 
-TEST_F(BocaAppPageHandlerTest, EndSessionWithNonActiveResponse) {
+TEST_F(BocaAppPageHandlerProducerTest, EndSessionWithNonActiveResponse) {
   ::boca::Session session;
 
   EXPECT_CALL(*session_manager(), GetCurrentSession())
@@ -1256,7 +1273,7 @@
   EXPECT_EQ(mojom::UpdateSessionError::kInvalid, future_1.Get().value());
 }
 
-TEST_F(BocaAppPageHandlerTest, ExtendSessionDurationSucceed) {
+TEST_F(BocaAppPageHandlerProducerTest, ExtendSessionDurationSucceed) {
   auto* session_id = "123";
   auto session = std::make_unique<::boca::Session>();
   session->mutable_duration()->set_seconds(120);
@@ -1293,7 +1310,7 @@
   EXPECT_FALSE(future_1.Get().has_value());
 }
 
-TEST_F(BocaAppPageHandlerTest, UpdateOnTaskConfigSucceed) {
+TEST_F(BocaAppPageHandlerProducerTest, UpdateOnTaskConfigSucceed) {
   auto session = GetCommonActiveSessionProto();
   EXPECT_CALL(*session_manager(),
               UpdateCurrentSession(_, /*dispatch_event=*/true))
@@ -1336,7 +1353,7 @@
   EXPECT_FALSE(future_1.Get().has_value());
 }
 
-TEST_F(BocaAppPageHandlerTest, UpdateOnTaskConfigWithEmptySession) {
+TEST_F(BocaAppPageHandlerProducerTest, UpdateOnTaskConfigWithEmptySession) {
   EXPECT_CALL(*session_manager(), GetCurrentSession())
       .WillOnce(Return(nullptr));
 
@@ -1349,7 +1366,7 @@
   EXPECT_EQ(mojom::UpdateSessionError::kInvalid, future_1.Get().value());
 }
 
-TEST_F(BocaAppPageHandlerTest, UpdateOnTaskConfigWithNonActiveSession) {
+TEST_F(BocaAppPageHandlerProducerTest, UpdateOnTaskConfigWithNonActiveSession) {
   ::boca::Session non_active_session;
 
   EXPECT_CALL(*session_manager(), GetCurrentSession())
@@ -1364,7 +1381,7 @@
   EXPECT_EQ(mojom::UpdateSessionError::kInvalid, future_1.Get().value());
 }
 
-TEST_F(BocaAppPageHandlerTest, UpdateOnTaskConfigWithHTTPFailure) {
+TEST_F(BocaAppPageHandlerProducerTest, UpdateOnTaskConfigWithHTTPFailure) {
   base::HistogramTester histogram_tester;
   auto session = GetCommonActiveSessionProto();
   EXPECT_CALL(*session_manager(), GetCurrentSession())
@@ -1401,7 +1418,7 @@
                                      1);
 }
 
-TEST_F(BocaAppPageHandlerTest, UpdateCaptionWithEmptySession) {
+TEST_F(BocaAppPageHandlerProducerTest, UpdateCaptionWithEmptySession) {
   EXPECT_CALL(*session_manager(), GetCurrentSession())
       .WillOnce(Return(nullptr));
   EXPECT_CALL(*session_manager(), NotifyLocalCaptionEvents(_)).Times(1);
@@ -1414,7 +1431,7 @@
   EXPECT_FALSE(future_1.Get().has_value());
 }
 
-TEST_F(BocaAppPageHandlerTest, UpdateCaptionWithNonActiveSession) {
+TEST_F(BocaAppPageHandlerProducerTest, UpdateCaptionWithNonActiveSession) {
   ::boca::Session session;
   EXPECT_CALL(*session_manager(), GetCurrentSession())
       .WillOnce(Return(&session));
@@ -1428,7 +1445,7 @@
   EXPECT_FALSE(future_1.Get().has_value());
 }
 
-TEST_F(BocaAppPageHandlerTest, UpdateCaptionConfigSucceed) {
+TEST_F(BocaAppPageHandlerProducerTest, UpdateCaptionConfigSucceed) {
   auto session = GetCommonActiveSessionProto();
   EXPECT_CALL(*session_manager(),
               UpdateCurrentSession(_, /*dispatch_event=*/true))
@@ -1480,7 +1497,7 @@
       .Times(1);
 }
 
-TEST_F(BocaAppPageHandlerTest, UpdateCaptionInitFailed) {
+TEST_F(BocaAppPageHandlerProducerTest, UpdateCaptionInitFailed) {
   auto session = GetCommonActiveSessionProto();
   EXPECT_CALL(*session_manager(),
               UpdateCurrentSession(_, /*dispatch_event=*/true))
@@ -1510,7 +1527,7 @@
             mojom::UpdateSessionError::kPreconditionFailed);
 }
 
-TEST_F(BocaAppPageHandlerTest,
+TEST_F(BocaAppPageHandlerProducerTest,
        UpdateCaptionConfigWithLocalConfigOnlyShouldNotSendServerRequest) {
   auto session = GetCommonActiveSessionProto();
   EXPECT_CALL(*session_manager(), GetCurrentSession())
@@ -1538,7 +1555,7 @@
   EXPECT_FALSE(future_1.Get().has_value());
 }
 
-TEST_F(BocaAppPageHandlerTest, UpdateCaptionWithHTTPFailure) {
+TEST_F(BocaAppPageHandlerProducerTest, UpdateCaptionWithHTTPFailure) {
   auto session = GetCommonActiveSessionProto();
   EXPECT_CALL(*session_manager(), GetCurrentSession())
       .WillRepeatedly(Return(&session));
@@ -1571,7 +1588,8 @@
   EXPECT_EQ(mojom::UpdateSessionError::kHTTPError, future_1.Get().value());
 }
 
-TEST_F(BocaAppPageHandlerTest, UpdateCaptionOnTaskSessionDurationShouldBlock) {
+TEST_F(BocaAppPageHandlerProducerTest,
+       UpdateCaptionOnTaskSessionDurationShouldBlock) {
   auto session = GetCommonActiveSessionProto();
   EXPECT_CALL(*session_manager(),
               UpdateCurrentSession(_, /*dispatch_event=*/true))
@@ -1611,7 +1629,8 @@
       .Times(1);
 }
 
-TEST_F(BocaAppPageHandlerTest, ShouldNotUsePreviousSessionCaptionConfig) {
+TEST_F(BocaAppPageHandlerProducerTest,
+       ShouldNotUsePreviousSessionCaptionConfig) {
   auto session = GetCommonActiveSessionProto();
   EXPECT_CALL(*session_manager(),
               UpdateCurrentSession(_, /*dispatch_event=*/true))
@@ -1652,7 +1671,8 @@
             first_request->captions_config()->SerializeAsString());
 }
 
-TEST_F(BocaAppPageHandlerTest, ShouldNotUsePreviousSessionOnTaskConfig) {
+TEST_F(BocaAppPageHandlerProducerTest,
+       ShouldNotUsePreviousSessionOnTaskConfig) {
   auto session = GetCommonActiveSessionProto();
   EXPECT_CALL(*session_manager(),
               UpdateCurrentSession(_, /*dispatch_event=*/true))
@@ -1699,7 +1719,7 @@
       .Times(1);
 }
 
-TEST_F(BocaAppPageHandlerTest,
+TEST_F(BocaAppPageHandlerProducerTest,
        UpdateOnTaskConfigWithFailedCaptionConfigShouldUseSessionData) {
   auto session = GetCommonActiveSessionProto();
   EXPECT_CALL(*session_manager(),
@@ -1762,7 +1782,7 @@
   EXPECT_FALSE(future_2.Get().has_value());
 }
 
-TEST_F(BocaAppPageHandlerTest,
+TEST_F(BocaAppPageHandlerProducerTest,
        UpdateCaptionConfigWithFailedOnTaskConfigShouldUseSessionData) {
   auto session = GetCommonActiveSessionProto();
   EXPECT_CALL(*session_manager(),
@@ -1829,7 +1849,7 @@
       .Times(1);
 }
 
-TEST_F(BocaAppPageHandlerTest, UpdateEmptyStudentActivitySucceed) {
+TEST_F(BocaAppPageHandlerProducerTest, UpdateEmptyStudentActivitySucceed) {
   std::map<std::string, ::boca::StudentStatus> activities;
   base::test::TestFuture<std::vector<mojom::IdentifiedActivityPtr>> future;
   fake_page()->SetActivityInterceptorCallback(future.GetCallback());
@@ -1838,7 +1858,7 @@
   ASSERT_TRUE(result.empty());
 }
 
-TEST_F(BocaAppPageHandlerTest, UpdateNonEmptyStudentActivitySucceed) {
+TEST_F(BocaAppPageHandlerProducerTest, UpdateNonEmptyStudentActivitySucceed) {
   std::map<std::string, ::boca::StudentStatus> activities;
   ::boca::StudentStatus status_1;
   status_1.set_state(::boca::StudentStatus::ACTIVE);
@@ -1881,7 +1901,7 @@
   EXPECT_TRUE(result[1]->activity->is_active);
 }
 
-TEST_F(BocaAppPageHandlerTest,
+TEST_F(BocaAppPageHandlerProducerTest,
        UpdateStudentActivityWithEmptyDeviceStateSucceed) {
   std::map<std::string, ::boca::StudentStatus> activities;
   ::boca::StudentStatus status_1;
@@ -1902,7 +1922,34 @@
   EXPECT_EQ("", result[0]->activity->view_screen_session_code);
 }
 
-TEST_F(BocaAppPageHandlerTest, RemoveStudentSucceedAlsoRemoveFromLocalSession) {
+TEST_F(BocaAppPageHandlerProducerTest,
+       UpdateStudentActivityWithMultipleDevicesSignedInSucceed) {
+  std::map<std::string, ::boca::StudentStatus> activities;
+  ::boca::StudentStatus status_1;
+  status_1.set_state(::boca::StudentStatus::ACTIVE);
+  ::boca::StudentDevice device_1;
+  device_1.set_state(::boca::StudentDevice::ACTIVE);
+  (*status_1.mutable_devices())["device1"] = std::move(device_1);
+  ::boca::StudentDevice device_2;
+  device_2.set_state(::boca::StudentDevice::ACTIVE);
+  (*status_1.mutable_devices())["device2"] = std::move(device_2);
+  activities.emplace("1", std::move(status_1));
+
+  base::test::TestFuture<std::vector<mojom::IdentifiedActivityPtr>> future;
+  fake_page()->SetActivityInterceptorCallback(future.GetCallback());
+  boca_app_handler()->OnConsumerActivityUpdated(activities);
+  auto result = future.Take();
+  EXPECT_EQ(1u, result.size());
+  EXPECT_EQ("1", result[0]->id);
+  EXPECT_EQ(mojom::StudentStatusDetail::kMultipleDeviceSignedIn,
+            result[0]->activity->student_status_detail);
+  EXPECT_FALSE(result[0]->activity->is_active);
+  EXPECT_EQ("", result[0]->activity->active_tab);
+  EXPECT_EQ("", result[0]->activity->view_screen_session_code);
+}
+
+TEST_F(BocaAppPageHandlerProducerTest,
+       RemoveStudentSucceedAlsoRemoveFromLocalSession) {
   auto* session_id = "123";
   auto session = std::make_unique<::boca::Session>();
   session->set_session_state(::boca::Session::ACTIVE);
@@ -1948,7 +1995,7 @@
   EXPECT_EQ("5", session->roster().student_groups()[1].students()[0].gaia_id());
 }
 
-TEST_F(BocaAppPageHandlerTest, RemoveStudentWithHTTPFailure) {
+TEST_F(BocaAppPageHandlerProducerTest, RemoveStudentWithHTTPFailure) {
   base::HistogramTester histogram_tester;
   auto* session_id = "123";
   auto session = std::make_unique<::boca::Session>();
@@ -1988,7 +2035,7 @@
                                      1);
 }
 
-TEST_F(BocaAppPageHandlerTest, RemoveStudentWithEmptySession) {
+TEST_F(BocaAppPageHandlerProducerTest, RemoveStudentWithEmptySession) {
   EXPECT_CALL(*session_manager(), GetCurrentSession())
       .WillOnce(Return(nullptr));
 
@@ -2000,7 +2047,7 @@
   EXPECT_EQ(mojom::RemoveStudentError::kInvalid, future_1.Get().value());
 }
 
-TEST_F(BocaAppPageHandlerTest, RemoveStudentWithNonActiveSession) {
+TEST_F(BocaAppPageHandlerProducerTest, RemoveStudentWithNonActiveSession) {
   ::boca::Session session;
   EXPECT_CALL(*session_manager(), GetCurrentSession())
       .WillOnce(Return(&session));
@@ -2013,7 +2060,8 @@
   EXPECT_EQ(mojom::RemoveStudentError::kInvalid, future_1.Get().value());
 }
 
-TEST_F(BocaAppPageHandlerTest, AddStudentsSucceedAlsoTriggerSessionReload) {
+TEST_F(BocaAppPageHandlerProducerTest,
+       AddStudentsSucceedAlsoTriggerSessionReload) {
   const auto* session_id = "123";
   const auto* group_id = "groupId";
   auto session = std::make_unique<::boca::Session>();
@@ -2070,7 +2118,7 @@
   EXPECT_FALSE(future_1.Get().has_value());
 }
 
-TEST_F(BocaAppPageHandlerTest, AddStudentsWithHTTPFailure) {
+TEST_F(BocaAppPageHandlerProducerTest, AddStudentsWithHTTPFailure) {
   base::HistogramTester histogram_tester;
   auto* session_id = "123";
   auto session = std::make_unique<::boca::Session>();
@@ -2109,7 +2157,7 @@
                                      1);
 }
 
-TEST_F(BocaAppPageHandlerTest, OnSessionSessionStartedSucceed) {
+TEST_F(BocaAppPageHandlerProducerTest, OnSessionSessionStartedSucceed) {
   auto session = GetCommonActiveSessionProto();
   EXPECT_CALL(*session_manager(), GetCurrentSession())
       .WillOnce(Return(&session));
@@ -2120,7 +2168,7 @@
   ASSERT_TRUE(result->is_config());
 }
 
-TEST_F(BocaAppPageHandlerTest, OnSessionSessionMetadataUpdatedSucceed) {
+TEST_F(BocaAppPageHandlerProducerTest, OnSessionSessionMetadataUpdatedSucceed) {
   auto session = GetCommonActiveSessionProto();
   EXPECT_CALL(*session_manager(), GetCurrentSession())
       .WillOnce(Return(&session));
@@ -2131,7 +2179,7 @@
   ASSERT_TRUE(result->is_config());
 }
 
-TEST_F(BocaAppPageHandlerTest, OnSessionEndedSucceed) {
+TEST_F(BocaAppPageHandlerProducerTest, OnSessionEndedSucceed) {
   base::test::TestFuture<mojom::ConfigResultPtr> future;
   fake_page()->SetSessionConfigInterceptorCallback(future.GetCallback());
   boca_app_handler()->OnSessionEnded("any");
@@ -2139,7 +2187,7 @@
   ASSERT_TRUE(result->is_error());
 }
 
-TEST_F(BocaAppPageHandlerTest, OnSessionCaptionUpdatedSucceed) {
+TEST_F(BocaAppPageHandlerProducerTest, OnSessionCaptionUpdatedSucceed) {
   auto session = GetCommonActiveSessionProto();
   EXPECT_CALL(*session_manager(), GetCurrentSession())
       .WillOnce(Return(&session));
@@ -2151,7 +2199,7 @@
   ASSERT_TRUE(result->is_config());
 }
 
-TEST_F(BocaAppPageHandlerTest, OnSessionBundleUpdatedSucceed) {
+TEST_F(BocaAppPageHandlerProducerTest, OnSessionBundleUpdatedSucceed) {
   auto session = GetCommonActiveSessionProto();
   EXPECT_CALL(*session_manager(), GetCurrentSession())
       .WillOnce(Return(&session));
@@ -2162,7 +2210,7 @@
   ASSERT_TRUE(result->is_config());
 }
 
-TEST_F(BocaAppPageHandlerTest, OnSessionRosterUpdatedSucceed) {
+TEST_F(BocaAppPageHandlerProducerTest, OnSessionRosterUpdatedSucceed) {
   auto session = GetCommonActiveSessionProto();
   EXPECT_CALL(*session_manager(), GetCurrentSession())
       .WillOnce(Return(&session));
@@ -2173,7 +2221,7 @@
   ASSERT_TRUE(result->is_config());
 }
 
-TEST_F(BocaAppPageHandlerTest, JoinSessionSucceeded) {
+TEST_F(BocaAppPageHandlerProducerTest, JoinSessionSucceeded) {
   EXPECT_CALL(*session_manager(),
               UpdateCurrentSession(_, /*dispatch_event=*/true))
       .Times(1);
@@ -2203,7 +2251,7 @@
   EXPECT_FALSE(future_1.Get().has_value());
 }
 
-TEST_F(BocaAppPageHandlerTest, JoinSessionFailed) {
+TEST_F(BocaAppPageHandlerProducerTest, JoinSessionFailed) {
   base::HistogramTester histogram_tester;
   EXPECT_CALL(*session_manager(),
               UpdateCurrentSession(_, /*dispatch_event=*/true))
@@ -2240,7 +2288,8 @@
       google_apis::ApiErrorCode::HTTP_FORBIDDEN, 1);
 }
 
-TEST_F(BocaAppPageHandlerTest, JoinSessionFailedDueToNonManagedNetwork) {
+TEST_F(BocaAppPageHandlerProducerTest,
+       JoinSessionFailedDueToNonManagedNetwork) {
   EXPECT_CALL(*session_manager(),
               UpdateCurrentSession(_, /*dispatch_event=*/true))
       .Times(0);
@@ -2264,7 +2313,7 @@
             future_1.Get().value());
 }
 
-TEST_F(BocaAppPageHandlerTest, ViewScreenSucceeded) {
+TEST_F(BocaAppPageHandlerProducerTest, ViewScreenSucceeded) {
   const std::string student_id = "123";
   EXPECT_CALL(*spotlight_service(), ViewScreen(student_id, kTestUrlBase, _))
       .WillOnce(WithArg<2>(Invoke(
@@ -2277,7 +2326,7 @@
   EXPECT_FALSE(future.Get().has_value());
 }
 
-TEST_F(BocaAppPageHandlerTest, ViewScreenFailed) {
+TEST_F(BocaAppPageHandlerProducerTest, ViewScreenFailed) {
   base::HistogramTester histogram_tester;
   const std::string student_id = "123";
 
@@ -2300,7 +2349,7 @@
       google_apis::ApiErrorCode::HTTP_FORBIDDEN, 1);
 }
 
-TEST_F(BocaAppPageHandlerTest, AuthenticateWebviewSuccess) {
+TEST_F(BocaAppPageHandlerProducerTest, AuthenticateWebviewSuccess) {
   EXPECT_CALL(*webview_auth_handler(), AuthenticateWebview(testing::_))
       .WillOnce(
           testing::Invoke(base::test::RunOnceCallback<0>(/*is_success=*/true)));
@@ -2313,7 +2362,7 @@
   run_loop.Run();
 }
 
-TEST_F(BocaAppPageHandlerTest, AuthenticateWebviewFailure) {
+TEST_F(BocaAppPageHandlerProducerTest, AuthenticateWebviewFailure) {
   EXPECT_CALL(*webview_auth_handler(), AuthenticateWebview(testing::_))
       .WillOnce(testing::Invoke(
           base::test::RunOnceCallback<0>(/*is_success=*/false)));
@@ -2326,7 +2375,7 @@
   run_loop.Run();
 }
 
-TEST_F(BocaAppPageHandlerTest, TestPrefGetter) {
+TEST_F(BocaAppPageHandlerProducerTest, TestPrefGetter) {
   boca_app_handler()->SetPrefForTesting(pref_service());
   base::test::TestFuture<base::Value> future;
   boca_app_handler()->GetUserPref(
@@ -2334,7 +2383,7 @@
   EXPECT_TRUE(future.Get().is_int());
 }
 
-TEST_F(BocaAppPageHandlerTest, TestPrefGetterAndSetter) {
+TEST_F(BocaAppPageHandlerProducerTest, TestPrefGetterAndSetter) {
   base::Value::Dict nav_map;
   base::Value::Dict nav_occurrence;
   nav_occurrence.Set("navRule", 0);
@@ -2344,7 +2393,7 @@
                /*value=*/base::Value(nav_map.Clone()));
 }
 
-TEST_F(BocaAppPageHandlerTest, EndViewScreenSessionSucceeded) {
+TEST_F(BocaAppPageHandlerProducerTest, EndViewScreenSessionSucceeded) {
   const std::string student_id = "123";
   EXPECT_CALL(
       *spotlight_service(),
@@ -2360,7 +2409,7 @@
   EXPECT_FALSE(future.Get().has_value());
 }
 
-TEST_F(BocaAppPageHandlerTest, EndViewScreenSessionFailed) {
+TEST_F(BocaAppPageHandlerProducerTest, EndViewScreenSessionFailed) {
   base::HistogramTester histogram_tester;
   const std::string student_id = "123";
 
@@ -2386,21 +2435,21 @@
       google_apis::ApiErrorCode::HTTP_FORBIDDEN, 1);
 }
 
-TEST_F(BocaAppPageHandlerTest, OpenFeedbackDialog) {
+TEST_F(BocaAppPageHandlerProducerTest, OpenFeedbackDialog) {
   EXPECT_CALL(*boca_app_client(), OpenFeedbackDialog()).Times(1);
   base::test::TestFuture<void> open_feedback_future;
   boca_app_handler()->OpenFeedbackDialog(open_feedback_future.GetCallback());
   EXPECT_TRUE(open_feedback_future.Wait());
 }
 
-TEST_F(BocaAppPageHandlerTest, RefreshWorkbook) {
+TEST_F(BocaAppPageHandlerProducerTest, RefreshWorkbook) {
   EXPECT_CALL(*session_manager(), NotifyAppReload()).Times(1);
   base::test::TestFuture<void> refresh_workbook_future;
   boca_app_handler()->RefreshWorkbook(refresh_workbook_future.GetCallback());
   EXPECT_TRUE(refresh_workbook_future.Wait());
 }
 
-TEST_F(BocaAppPageHandlerTest, SetViewScreenSessionActiveSucceeded) {
+TEST_F(BocaAppPageHandlerProducerTest, SetViewScreenSessionActiveSucceeded) {
   const std::string student_id = "123";
   EXPECT_CALL(
       *spotlight_service(),
@@ -2417,7 +2466,7 @@
   EXPECT_FALSE(future.Get().has_value());
 }
 
-TEST_F(BocaAppPageHandlerTest, SetViewScreenSessionActiveFailed) {
+TEST_F(BocaAppPageHandlerProducerTest, SetViewScreenSessionActiveFailed) {
   base::HistogramTester histogram_tester;
   const std::string student_id = "123";
 
@@ -2482,14 +2531,15 @@
   EXPECT_FALSE(future.Get());
 }
 
-TEST_F(BocaAppPageHandlerTest, NotifyWhenLocalCaptionClosed) {
+TEST_F(BocaAppPageHandlerProducerTest, NotifyWhenLocalCaptionClosed) {
   base::test::TestFuture<void> future;
   fake_page()->SetLocalCaptionDisabledInterceptorCallback(future.GetCallback());
   boca_app_handler()->OnLocalCaptionClosed();
   EXPECT_TRUE(future.Wait());
 }
 
-TEST_F(BocaAppPageHandlerTest, NotifyWhenSessionCaptionClosedRequestSucceed) {
+TEST_F(BocaAppPageHandlerProducerTest,
+       NotifyWhenSessionCaptionClosedRequestSucceed) {
   ::boca::CaptionsConfig request_captions_config;
   ::boca::CaptionsConfig notify_captions_config;
   ::boca::Session session =
@@ -2521,7 +2571,8 @@
   EXPECT_FALSE(future.Get());
 }
 
-TEST_F(BocaAppPageHandlerTest, NotifyWhenSessionCaptionClosedRequestFailed) {
+TEST_F(BocaAppPageHandlerProducerTest,
+       NotifyWhenSessionCaptionClosedRequestFailed) {
   ::boca::CaptionsConfig request_captions_config;
   ::boca::CaptionsConfig notify_captions_config;
   ::boca::Session session =
@@ -2554,9 +2605,8 @@
   EXPECT_TRUE(future.Get());
 }
 
-TEST_F(BocaAppPageHandlerTest,
+TEST_F(BocaAppPageHandlerConsumerTest,
        DoesNotNotifyWhenSessionCaptionClosedForConsumer) {
-  CreateBocaAppHandler(/*is_producer=*/false);
   ::boca::Session session =
       GetCommonActiveSessionProto({.captions_enabled = true});
   EXPECT_CALL(*session_manager(), GetCurrentSession)
@@ -2572,7 +2622,7 @@
   EXPECT_FALSE(future.IsReady());
 }
 
-TEST_F(BocaAppPageHandlerTest,
+TEST_F(BocaAppPageHandlerProducerTest,
        DoesNotNotifyWhenSessionCaptionClosedIfNullSession) {
   EXPECT_CALL(*session_manager(), GetCurrentSession)
       .WillRepeatedly(Return(nullptr));
@@ -2587,7 +2637,7 @@
   EXPECT_FALSE(future.IsReady());
 }
 
-TEST_F(BocaAppPageHandlerTest,
+TEST_F(BocaAppPageHandlerProducerTest,
        DoesNotNotifyWhenSessionCaptionClosedIfSessionInactive) {
   ::boca::Session session =
       GetCommonActiveSessionProto({.captions_enabled = true});
@@ -2605,7 +2655,8 @@
   EXPECT_FALSE(future.IsReady());
 }
 
-TEST_F(BocaAppPageHandlerTest, ProducerCaptionsOverrideGetSessionCaptions) {
+TEST_F(BocaAppPageHandlerProducerTest,
+       ProducerCaptionsOverrideGetSessionCaptions) {
   ::boca::Session response_session = GetCommonActiveSessionProto(
       {.captions_enabled = true, .translations_enabled = true});
   auto current_session = std::make_unique<::boca::Session>();
@@ -2623,7 +2674,7 @@
                    ->config->caption_config->session_translation_enabled);
 }
 
-TEST_F(BocaAppPageHandlerTest,
+TEST_F(BocaAppPageHandlerProducerTest,
        ProducerCaptionsOverrideGetSessionCaptionsAfterUpdate) {
   ::boca::Session response_session = GetCommonActiveSessionProto();
   auto current_session =
@@ -2645,7 +2696,6 @@
       });
 
   base::test::TestFuture<mojom::SessionResultPtr> future;
-  CreateBocaAppHandler(/*is_producer=*/true);
   SetSessionCaptionInitializer(/*success=*/true);
   boca_app_handler()->UpdateCaptionConfig(
       mojom::CaptionConfig::New(/*session_caption_enabled=*/true,
@@ -2668,7 +2718,7 @@
       .Times(1);
 }
 
-TEST_F(BocaAppPageHandlerTest,
+TEST_F(BocaAppPageHandlerConsumerTest,
        ConsumerCaptionsDoesNotOverrideGetSessionCaptions) {
   ::boca::Session response_session = GetCommonActiveSessionProto(
       {.captions_enabled = true, .translations_enabled = true});
@@ -2676,7 +2726,6 @@
   PrepareGetSession(current_session.get(), response_session);
 
   base::test::TestFuture<mojom::SessionResultPtr> future;
-  CreateBocaAppHandler(/*is_producer=*/false);
   boca_app_handler()->GetSession(future.GetCallback());
   mojom::SessionResultPtr get_session_result = future.Take();
 
@@ -2687,7 +2736,8 @@
                   ->config->caption_config->session_translation_enabled);
 }
 
-TEST_F(BocaAppPageHandlerTest, EnableSessionCaptionRequestErrorWillFail) {
+TEST_F(BocaAppPageHandlerProducerTest,
+       EnableSessionCaptionRequestErrorWillFail) {
   base::HistogramTester histogram_tester;
   ::boca::Session response_session = GetCommonActiveSessionProto();
   auto current_session =
@@ -2704,7 +2754,6 @@
   base::test::TestFuture<mojom::SessionResultPtr> get_future;
   base::test::TestFuture<std::optional<mojom::UpdateSessionError>>
       update_future;
-  CreateBocaAppHandler(/*is_producer=*/true);
   SetSessionCaptionInitializer(/*success=*/true);
   boca_app_handler()->UpdateCaptionConfig(
       mojom::CaptionConfig::New(/*session_caption_enabled=*/true,
@@ -2727,7 +2776,8 @@
       google_apis::ApiErrorCode::HTTP_BAD_REQUEST, 1);
 }
 
-TEST_F(BocaAppPageHandlerTest, IgnoreDisableSessionCaptionRequestError) {
+TEST_F(BocaAppPageHandlerProducerTest,
+       IgnoreDisableSessionCaptionRequestError) {
   ::boca::CaptionsConfig enable_captions_notified;
   ::boca::CaptionsConfig disable_captions_notified;
   ::boca::Session response_session = GetCommonActiveSessionProto(
@@ -2736,7 +2786,6 @@
       std::make_unique<::boca::Session>(GetCommonActiveSessionProto());
   PrepareGetSession(current_session.get(), response_session);
   EXPECT_CALL(*session_manager(), NotifyLocalCaptionEvents).Times(2);
-  CreateBocaAppHandler(/*is_producer=*/true);
 
   // Simulate enable session captions success.
   EXPECT_CALL(*session_manager(), NotifySessionCaptionProducerEvents)
diff --git a/ash/webui/boca_ui/boca_ui.cc b/ash/webui/boca_ui/boca_ui.cc
index 443c25e..5899de1 100644
--- a/ash/webui/boca_ui/boca_ui.cc
+++ b/ash/webui/boca_ui/boca_ui.cc
@@ -108,7 +108,14 @@
 #endif  // !DCHECK_IS_ON()
 }
 
-BocaUI::~BocaUI() = default;
+BocaUI::~BocaUI() {
+  if (is_producer_) {
+    // Not handling response, if update failed, persistent notification will
+    // stay.
+    page_handler_impl_->EndSession(
+        base::BindOnce([](std::optional<mojom::UpdateSessionError>) {}));
+  }
+}
 
 void BocaUI::BindInterface(
     mojo::PendingReceiver<boca::mojom::BocaPageHandlerFactory> factory) {
diff --git a/ash/webui/boca_ui/mojom/boca.mojom b/ash/webui/boca_ui/mojom/boca.mojom
index 86140966..3cd14f4 100644
--- a/ash/webui/boca_ui/mojom/boca.mojom
+++ b/ash/webui/boca_ui/mojom/boca.mojom
@@ -210,6 +210,8 @@
   kNotAddedConfiguredAsTeacher = 7,
   // Student not added due to feature not enabled in policy.
   kNotAddedNotConfigured = 8,
+  // Student signed into multiple devices.
+  kMultipleDeviceSignedIn = 9,
 };
 
 struct StudentActivity {
diff --git a/ash/webui/boca_ui/resources/app/boca_app.ts b/ash/webui/boca_ui/resources/app/boca_app.ts
index 097910e..0244263a 100644
--- a/ash/webui/boca_ui/resources/app/boca_app.ts
+++ b/ash/webui/boca_ui/resources/app/boca_app.ts
@@ -114,6 +114,8 @@
   NOT_ADDED_CONFIGURED_AS_TEACHER = 7,
 
   NOT_ADDED_NOT_CONFIGURED = 8,
+
+  MULTIPLE_DEVICE_SIGNED_IN = 9,
 }
 
 /**
diff --git a/ash/webui/diagnostics_ui/resources/cpu_card.ts b/ash/webui/diagnostics_ui/resources/cpu_card.ts
index 0cee5ac..9a6f82c 100644
--- a/ash/webui/diagnostics_ui/resources/cpu_card.ts
+++ b/ash/webui/diagnostics_ui/resources/cpu_card.ts
@@ -165,7 +165,7 @@
   }
 
   private convertKhzToGhz(num: number): string {
-    return (num / 1000000).toFixed(2);
+    return (num / 1000000).toFixed(3);
   }
 
   protected getCurrentCpuSpeed(): string {
diff --git a/ash/wm/desks/desk_animation_base.cc b/ash/wm/desks/desk_animation_base.cc
index 37aaec3b..cb14636 100644
--- a/ash/wm/desks/desk_animation_base.cc
+++ b/ash/wm/desks/desk_animation_base.cc
@@ -46,6 +46,14 @@
     throughput_tracker_->Start(GetSmoothnessReportCallback());
   }
 
+  // Pause occlusion tracking while taking starting desk screenshot.
+  //
+  // Occlusion tracking should be paused prior to PrepareForActivationAnimation
+  // since it can update the occlusion state of the starting desk windows.
+  // See crbug.com/417088506 for the context.
+  pauser_for_screenshot_ =
+      std::make_unique<aura::WindowOcclusionTracker::ScopedPause>();
+
   // This step makes sure that the containers of the target desk are shown at
   // the beginning of the animation (but not actually visible to the user yet,
   // until the desk is actually activated at a later step of the animation).
@@ -107,6 +115,9 @@
     return;
   }
 
+  // Now each display is covered by starting desk screenshot.
+  pauser_for_screenshot_.reset();
+
   // Extend the compositors' timeouts in order to prevents any repaints until
   // the desks are switched and overview mode exits.
   const auto roots = Shell::GetAllRootWindows();
diff --git a/ash/wm/desks/desk_animation_base.h b/ash/wm/desks/desk_animation_base.h
index 8f4555a5..fe92c32 100644
--- a/ash/wm/desks/desk_animation_base.h
+++ b/ash/wm/desks/desk_animation_base.h
@@ -13,6 +13,7 @@
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/time/time.h"
+#include "ui/aura/window_occlusion_tracker.h"
 #include "ui/compositor/compositor_metrics_tracker.h"
 
 namespace ash {
@@ -155,6 +156,10 @@
   // DeskController are tied together in production code, but may not be in a
   // test scenario.
   bool skip_notify_controller_on_animation_finished_for_testing_ = false;
+
+  // Used to pause occlusion updates while taking starting desk screenshot.
+  std::unique_ptr<aura::WindowOcclusionTracker::ScopedPause>
+      pauser_for_screenshot_;
 };
 
 }  // namespace ash
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index a303abf..8e8cabe 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -2613,6 +2613,89 @@
   NewDesk();
 }
 
+// Verify that the starting desk is not occluded by the ending desk when
+// activation is switching. Regression test for crbug.com/398287318.
+TEST_P(DesksTest, EndingDeskShouldNotOccludeStartingDesk) {
+  UpdateDisplay("600x400");
+
+  auto* root = Shell::GetPrimaryRootWindow();
+  auto* controller = DesksController::Get();
+
+  // Prepare two desks.
+  NewDesk();
+  ASSERT_EQ(2u, controller->desks().size());
+
+  const Desk* desk1 = controller->GetDeskAtIndex(0);
+  const Desk* desk2 = controller->GetDeskAtIndex(1);
+  auto* desk1_container = desk1->GetDeskContainerForRoot(root);
+  auto* desk2_container = desk2->GetDeskContainerForRoot(root);
+
+  // desk1 is active.
+  ASSERT_TRUE(desk1_container->IsVisible());
+  ASSERT_FALSE(desk2_container->IsVisible());
+  ASSERT_EQ(desks_util::GetActiveDeskContainerForRoot(root), desk1_container);
+
+  // Create two windows so that win2 can completely cover win1.
+  const auto win1_bounds = gfx::Rect{20, 20, 100, 100};
+  const auto win2_bounds = gfx::Rect{10, 10, 200, 200};
+  std::unique_ptr<aura::Window> win1 = CreateAppWindow(win1_bounds);
+  std::unique_ptr<aura::Window> win2 = CreateAppWindow(win2_bounds);
+  win1->TrackOcclusionState();
+  win2->TrackOcclusionState();
+  win1->Show();
+  win2->Show();
+  // Check that win1 is occluded when win2 is active.
+  wm::ActivateWindow(win2.get());
+  ASSERT_EQ(win1->GetOcclusionState(), aura::Window::OcclusionState::OCCLUDED);
+  ASSERT_EQ(win2->GetOcclusionState(), aura::Window::OcclusionState::VISIBLE);
+  wm::ActivateWindow(win1.get());
+  ASSERT_EQ(win1->GetOcclusionState(), aura::Window::OcclusionState::VISIBLE);
+  ASSERT_EQ(win2->GetOcclusionState(), aura::Window::OcclusionState::VISIBLE);
+
+  // Move win2 to desk2.
+  controller->SendToDeskAtIndex(win2.get(), 1);
+
+  // Now win1 is only visible.
+  ASSERT_EQ(win1->GetOcclusionState(), aura::Window::OcclusionState::VISIBLE);
+  ASSERT_EQ(win2->GetOcclusionState(), aura::Window::OcclusionState::HIDDEN);
+
+  // Activate desk2.
+  DeskSwitchAnimationWaiter animation_waiter;
+  controller->ActivateDesk(desk2, DesksSwitchSource::kDeskSwitchShortcut);
+
+  // `ActivateDesk` does not activate desk2 immediately.
+  // Although the fix for crbug.com/40100714 makes win2 shown (with opacity 0),
+  // win1 should not be occluded.
+  // Note: Occlusion state update is paused until the starting desk screenshot
+  // is taken.
+  EXPECT_TRUE(desk1_container->IsVisible());
+  EXPECT_TRUE(desk2_container->IsVisible());
+  EXPECT_EQ(win1->GetOcclusionState(), aura::Window::OcclusionState::VISIBLE);
+  EXPECT_EQ(win2->GetOcclusionState(), aura::Window::OcclusionState::HIDDEN);
+
+  // Wait until the starting desk screenshot has been taken.
+  base::RunLoop run_loop;
+  DeskAnimationBase* animation = DesksController::Get()->animation();
+  ASSERT_TRUE(animation);
+  auto* desk_switch_animator =
+      animation->GetDeskSwitchAnimatorAtIndexForTesting(0);
+  ASSERT_TRUE(desk_switch_animator);
+  RootWindowDeskSwitchAnimatorTestApi(desk_switch_animator)
+      .SetOnStartingScreenshotTakenCallback(run_loop.QuitClosure());
+  run_loop.Run();
+
+  // Activation is done while the ending desk screenshot is taken.
+  EXPECT_EQ(desks_util::GetActiveDeskContainerForRoot(root), desk2_container);
+  EXPECT_FALSE(desk1_container->IsVisible());
+  EXPECT_TRUE(desk2_container->IsVisible());
+  // Occlusion tracking is resumed before the ending desk screenshot is
+  // taken, so occlusion stat should be updated.
+  EXPECT_EQ(win1->GetOcclusionState(), aura::Window::OcclusionState::HIDDEN);
+  EXPECT_EQ(win2->GetOcclusionState(), aura::Window::OcclusionState::VISIBLE);
+
+  animation_waiter.Wait();
+}
+
 class DesksWithMultiDisplayOverview : public AshTestBase {
  public:
   DesksWithMultiDisplayOverview() = default;
diff --git a/base/android/java/src/org/chromium/base/BuildInfo.java b/base/android/java/src/org/chromium/base/BuildInfo.java
index 07319e0..1d2b378 100644
--- a/base/android/java/src/org/chromium/base/BuildInfo.java
+++ b/base/android/java/src/org/chromium/base/BuildInfo.java
@@ -92,9 +92,6 @@
     /** Whether we're running on an Android Foldable OS device or not. */
     public final boolean isFoldable;
 
-    /** Whether we're running on an Android Desktop OS device or not. */
-    public final boolean isDesktop;
-
     /**
      * version of the FEATURE_VULKAN_DEQP_LEVEL, if available. Queried only on Android T or above
      */
@@ -190,7 +187,6 @@
         isTV = DeviceInfo.isTV();
         isAutomotive = DeviceInfo.isAutomotive();
         isFoldable = DeviceInfo.isFoldable();
-        isDesktop = DeviceInfo.isDesktop();
         vulkanDeqpLevel = DeviceInfo.getVulkanDeqpLevel();
     }
 
diff --git a/base/metrics/persistent_histogram_allocator_unittest.cc b/base/metrics/persistent_histogram_allocator_unittest.cc
index a04b7e61..daf9e95 100644
--- a/base/metrics/persistent_histogram_allocator_unittest.cc
+++ b/base/metrics/persistent_histogram_allocator_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/metrics/histogram.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/persistent_memory_allocator.h"
+#include "base/metrics/sample_vector.h"
 #include "base/metrics/sparse_histogram.h"
 #include "base/metrics/statistics_recorder.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -38,6 +39,12 @@
   PersistentHistogramAllocatorTest()
       : statistics_recorder_(StatisticsRecorder::CreateTemporaryForTesting()) {
     CreatePersistentHistogramAllocator();
+
+    // Reset the static histogram pointer that PersistentSampleVector uses to
+    // track the result of MountExistingCountsStorage, in case a previous test
+    // has caused it to already be initialized. This ensures a known state for
+    // the current test.
+    PersistentSampleVector::ResetMountExistingCountsStorageResultForTesting();
   }
   ~PersistentHistogramAllocatorTest() override {
     DestroyPersistentHistogramAllocator();
@@ -224,17 +231,22 @@
 }
 
 TEST_F(PersistentHistogramAllocatorTest, StatisticsRecorderMerge) {
-  const char LinearHistogramName[] = "SRTLinearHistogram";
-  const char SparseHistogramName[] = "SRTSparseHistogram";
+  static constexpr char LinearHistogramName[] = "SRTLinearHistogram";
+  static constexpr char SparseHistogramName[] = "SRTSparseHistogram";
+
   const size_t global_sr_initial_histogram_count =
       StatisticsRecorder::GetHistogramCount();
   const size_t global_sr_initial_bucket_ranges_count =
       StatisticsRecorder::GetBucketRanges().size();
 
+  // We will create three histograms in this test,: two explicitly, and one
+  // implicitly when we merge multi-sample histogram data.
+  constexpr size_t kNumHistogramsCreated = 3;
+
   // Create a local StatisticsRecorder in which the newly created histogram
   // will be recorded. The global allocator must be replaced after because the
   // act of releasing will cause the active SR to forget about all histograms
-  // in the relased memory.
+  // in the released memory.
   std::unique_ptr<StatisticsRecorder> local_sr =
       StatisticsRecorder::CreateTemporaryForTesting();
   EXPECT_EQ(0U, StatisticsRecorder::GetHistogramCount());
@@ -242,6 +254,7 @@
       GlobalHistogramAllocator::ReleaseForTesting();
   GlobalHistogramAllocator::CreateWithLocalMemory(kAllocatorMemorySize, 0, "");
   ASSERT_TRUE(GlobalHistogramAllocator::Get());
+  PersistentSampleVector::ResetMountExistingCountsStorageResultForTesting();
 
   // Create a linear histogram for merge testing.
   HistogramBase* histogram1 =
@@ -276,6 +289,7 @@
   EXPECT_EQ(global_sr_initial_bucket_ranges_count,
             StatisticsRecorder::GetBucketRanges().size());
   GlobalHistogramAllocator::Set(old_allocator);
+  PersistentSampleVector::ResetMountExistingCountsStorageResultForTesting();
 
   // Create a "recovery" allocator using the same memory as the local one.
   PersistentHistogramAllocator recovery1(
@@ -299,7 +313,12 @@
         StatisticsRecorder::FindHistogram(recovered->histogram_name());
     EXPECT_NE(recovered.get(), found);
   }
-  EXPECT_EQ(global_sr_initial_histogram_count + 2,
+
+  // Verify that the histograms were merged into the global SR. The test has
+  // created two histograms above, plus another internal histogram tracking
+  // the success/failure of reading non-sparse persistent histogram sample
+  // vector data, for a total of 3 histograms.
+  EXPECT_EQ(global_sr_initial_histogram_count + kNumHistogramsCreated,
             StatisticsRecorder::GetHistogramCount());
 
   // Check the merged histograms for accuracy.
@@ -321,10 +340,20 @@
   EXPECT_EQ(1, snapshot->GetCount(4));
   EXPECT_EQ(1, snapshot->GetCount(6));
 
+  // kMountExistingCountsStorageResult will have exactly one sample for the
+  // linear histogram: One sample for the in-flight, but not logged/snapshotted
+  // sample vector; zero samples for the logged/shapshotted sample vector (which
+  // has no samples written to it yet),
+  found = StatisticsRecorder::FindHistogram(kMountExistingCountsStorageResult);
+  ASSERT_TRUE(found);
+  snapshot = found->SnapshotSamples();
+  EXPECT_EQ(1, snapshot->TotalCount());
+  EXPECT_EQ(1, snapshot->GetCount(0));  // We expect to have logged kSuccess
+
   // Verify that the LinearHistogram's BucketRanges was registered with the
   // global SR since the recovery allocator does not specify a custom
   // RangesManager.
-  ASSERT_EQ(global_sr_initial_bucket_ranges_count + 1,
+  ASSERT_EQ(global_sr_initial_bucket_ranges_count + 2,
             StatisticsRecorder::GetBucketRanges().size());
 
   // Perform additional histogram increments.
@@ -347,7 +376,7 @@
     }
     recovery2.MergeHistogramDeltaToStatisticsRecorder(recovered.get());
   }
-  EXPECT_EQ(global_sr_initial_histogram_count + 2,
+  EXPECT_EQ(global_sr_initial_histogram_count + kNumHistogramsCreated,
             StatisticsRecorder::GetHistogramCount());
 
   // And verify.
@@ -367,6 +396,16 @@
   EXPECT_EQ(1, snapshot->GetCount(4));
   EXPECT_EQ(1, snapshot->GetCount(6));
   EXPECT_EQ(1, snapshot->GetCount(7));
+
+  // kMountExistingCountsStorageResult will have exactly three samples for the
+  // linear histogram: One sample from the first merge; one sample for the
+  // current merge of the in-flight, but not logged/snapshotted sample vector;
+  // and, one samples for the logged/shapshotted sample vector (which was
+  // populated when the snapshot was taken above).
+  found = StatisticsRecorder::FindHistogram(kMountExistingCountsStorageResult);
+  snapshot = found->SnapshotSamples();
+  EXPECT_EQ(3, snapshot->TotalCount());
+  EXPECT_EQ(3, snapshot->GetCount(0));  // kSuccess.
 }
 
 // Verify that when merging histograms from an allocator with the global
@@ -414,6 +453,9 @@
   sparse_histogram3->Add(10);
   sparse_histogram3->SnapshotDelta();
 
+  // No histograms have been recovered from "persistent" memory yet, so there
+  // are no samples for kMountExistingCountsStorageResult. Just the histograms
+  // created above.
   EXPECT_EQ(6U, StatisticsRecorder::GetHistogramCount());
 
   // Destroy the local SR and ensure that we're back to the initial state and
@@ -450,8 +492,20 @@
         StatisticsRecorder::FindHistogram(recovered->histogram_name());
     EXPECT_FALSE(found);
   }
-  EXPECT_EQ(global_sr_initial_histogram_count,
+
+  // As mentioned above, all of the previously written histograms have not
+  // changed since their snapshot, so we load then discard them. However
+  // we record kMountExistingCountsStorageResult for each attempted sample
+  // vector recovery, which happens up to twice per histogram: once for the
+  // in-flight sample vector and once for the logged/snapshotted sample vector.
+  EXPECT_EQ(global_sr_initial_histogram_count + 1,
             StatisticsRecorder::GetHistogramCount());
+  HistogramBase* found =
+      StatisticsRecorder::FindHistogram(kMountExistingCountsStorageResult);
+  ASSERT_TRUE(found);
+  auto snapshot = found->SnapshotSamples();
+  EXPECT_EQ(2, snapshot->TotalCount());  // 2 for SRTLinearHistogram3
+  EXPECT_EQ(2, snapshot->GetCount(0));   // kSuccess.
 
   // Same as above, but with MergeHistogramFinalDeltaToStatisticsRecorder()
   // instead of MergeHistogramDeltaToStatisticsRecorder().
@@ -468,11 +522,10 @@
     }
 
     recovery2.MergeHistogramFinalDeltaToStatisticsRecorder(recovered.get());
-    HistogramBase* found =
-        StatisticsRecorder::FindHistogram(recovered->histogram_name());
+    found = StatisticsRecorder::FindHistogram(recovered->histogram_name());
     EXPECT_FALSE(found);
   }
-  EXPECT_EQ(global_sr_initial_histogram_count,
+  EXPECT_EQ(global_sr_initial_histogram_count + 1,
             StatisticsRecorder::GetHistogramCount());
 }
 
diff --git a/base/metrics/sample_vector.cc b/base/metrics/sample_vector.cc
index 0cf321d0..8f95c4e 100644
--- a/base/metrics/sample_vector.cc
+++ b/base/metrics/sample_vector.cc
@@ -610,15 +610,39 @@
   // move any single-sample to it because sometimes the persistent memory is
   // read-only. Only non-const methods (which assume that memory is read/write)
   // can do that.
-  if (single_sample().IsDisabled() && !MountExistingCountsStorage()) {
-#if !BUILDFLAG(IS_NACL)
+  if (single_sample().IsDisabled()) {
+    const auto result = MountExistingCountsStorageImpl();
+
+    // Record the result of MountExistingCountsStorageImpl() to the
+    // kMountExistingCountsStorageResult histogram so that we can see how often
+    // the call to MountExistingCountsStorageImpl() fails.
+    //
+    // See crbug.com/410544723
+    //
+    // Recording a histogram here is safe for re-entrancy because the act of
+    // recording the histogram will either:
+    // * Create a new histogram with no pre-existing samples, so the above check
+    //   for `single_sample().IsDisabled()` will be `false` when reached; or,
+    // * Find the existing histogram, without calling this constructor.
+    RecordMountExistingCountsStorageResult(result);
+
+    // We're trying to understand how/why the call to MountExistingCountsStorage
+    // sometimes fails. We've seen enough examples of kNothingToRead to suggest
+    // that this specific failure is not strongly associated with any particular
+    // historam being recovered; so, we don't need to collect any further crash
+    // dumps for that outcome. We haven't seen many examples of kCorrupt, so we
+    // continue to collect those.
     // TODO: crbug.com/410544723 - Remove crash keys and DumpWithoutCrashing
     // once investigation is complete.
-    SCOPED_CRASH_KEY_STRING64("PSV", "name", name);
-    SCOPED_CRASH_KEY_NUMBER("PSV", "counts_ref",
-                            persistent_counts_.reference());
+    if (result != MountExistingCountsStorageResult::kSucceeded &&
+        result != MountExistingCountsStorageResult::kNothingToRead) {
+#if !BUILDFLAG(IS_NACL)
+      SCOPED_CRASH_KEY_STRING64("PSV", "name", name);
+      SCOPED_CRASH_KEY_NUMBER("PSV", "counts_ref",
+                              persistent_counts_.reference());
 #endif
-    debug::DumpWithoutCrashing();
+      debug::DumpWithoutCrashing();
+    }
   }
 }
 
@@ -629,7 +653,40 @@
   NOTREACHED();
 }
 
-bool PersistentSampleVector::MountExistingCountsStorage() const {
+// static
+constinit std::atomic_uintptr_t
+    PersistentSampleVector::atomic_histogram_pointer{0};
+
+// static
+void PersistentSampleVector::ResetMountExistingCountsStorageResultForTesting() {
+  atomic_histogram_pointer.store(0, std::memory_order_release);
+}
+
+// static
+void PersistentSampleVector::RecordMountExistingCountsStorageResult(
+    MountExistingCountsStorageResult result) {
+  static constexpr auto boundary = static_cast<base::HistogramBase::Sample32>(
+      MountExistingCountsStorageResult::kMaxValue);
+  // This method is the functional and performance equivalent of:
+  //
+  //   UMA_HISTOGRAM_ENUMERATION(kMountExistingCountsStorageResult, result);
+  //
+  // The UMA_HISTOGRAM_ENUMERATION macro hides the static pointer used to cache
+  // the histogram pointer. We need to be able to reset the cached histogram
+  // pointer for testing so we have extracted the histogram pointer to a static
+  // member and used HISTOGRAM_POINTER_USE macro to complete the implementation,
+  // which is ultimately what UMA_HISTOGRAM_ENUMERATION macro does.
+  HISTOGRAM_POINTER_USE(
+      std::addressof(atomic_histogram_pointer),
+      kMountExistingCountsStorageResult,
+      Add(static_cast<base::HistogramBase::Sample32>(result)),
+      base::LinearHistogram::FactoryGet(
+          kMountExistingCountsStorageResult, 1, boundary, boundary + 1,
+          base::HistogramBase::kUmaTargetedHistogramFlag));
+}
+
+PersistentSampleVector::MountExistingCountsStorageResult
+PersistentSampleVector::MountExistingCountsStorageImpl() const {
   // There is no early exit if counts is not yet mounted because, given that
   // this is a virtual function, it's more efficient to do that at the call-
   // site. There is no danger, however, should this get called anyway (perhaps
@@ -638,7 +695,7 @@
   // with the exact same values.
 
   if (!persistent_counts_.reference()) {
-    return false;  // Nothing to mount.
+    return MountExistingCountsStorageResult::kNothingToRead;
   }
 
   // Mount the counts array in position. This shouldn't fail but can if the
@@ -646,12 +703,17 @@
   span<HistogramBase::AtomicCount> mem =
       persistent_counts_.Get<HistogramBase::AtomicCount>();
   if (mem.empty()) {
-    return false;
+    return MountExistingCountsStorageResult::kCorrupt;
   }
   // Uses a span that only covers the counts the SampleVector should have
   // access to, which can be a subset of the entire persistent allocation.
   set_counts(mem.first(counts_size()));
-  return true;
+  return MountExistingCountsStorageResult::kSucceeded;
+}
+
+bool PersistentSampleVector::MountExistingCountsStorage() const {
+  return MountExistingCountsStorageImpl() ==
+         MountExistingCountsStorageResult::kSucceeded;
 }
 
 span<HistogramBase::AtomicCount>
diff --git a/base/metrics/sample_vector.h b/base/metrics/sample_vector.h
index d30a621..5e1ff45 100644
--- a/base/metrics/sample_vector.h
+++ b/base/metrics/sample_vector.h
@@ -213,7 +213,38 @@
   // HistogramSamples:
   bool IsDefinitelyEmpty() const override;
 
+  // Resets the histogram used to log the result of MountExistingCountsStorage.
+  // We have tests that monitor histogram creation/restoration. These tests need
+  // to be able to initialize the histogram (or more precisely, the static
+  // pointer to the histogram) to a known state.
+  static void ResetMountExistingCountsStorageResultForTesting();
+
  private:
+  // These values are logged to UMA. Entries should not be renumbered and
+  // numeric values should never be reused. Please keep in sync with
+  // "MountExistingCountsStorageResult" in
+  // src/tools/metrics/histograms/metadata/uma/enums.xml.
+  enum class MountExistingCountsStorageResult {
+    kSucceeded = 0,
+    kNothingToRead = 1,
+    kCorrupt = 2,
+    kMaxValue = kCorrupt,
+  };
+
+  // Pointer used to cache the MountExistingCountsStorageResult histogram for
+  // PersistentSampleVector. This is used to avoid creating the histogram on
+  // every MountExistingCountsStorage call. Usually, this would an
+  // implementation detail hidden in the use of the UMA_HISTOGRAM_ENUMERATION
+  // macro, but PersistentSampleVector is a special case where we need to be
+  // able to reset the histogram pointer for testing.
+  static std::atomic_uintptr_t atomic_histogram_pointer;
+
+  static void RecordMountExistingCountsStorageResult(
+      MountExistingCountsStorageResult result);
+
+  // Private implementation of MountExistingCountsStorage
+  MountExistingCountsStorageResult MountExistingCountsStorageImpl() const;
+
   // SampleVectorBase:
   bool MountExistingCountsStorage() const override;
   span<HistogramBase::Count32> CreateCountsStorageWhileLocked() override;
@@ -222,6 +253,11 @@
   DelayedPersistentAllocation persistent_counts_;
 };
 
+// Histogram name used to log the result of MountExistingCountsStorage for
+// PersistentSampleVector. Exposed here for testing.
+inline constexpr std::string_view kMountExistingCountsStorageResult =
+    "UMA.PersistentHistograms.MountExistingCountsStorageResult";
+
 }  // namespace base
 
 #endif  // BASE_METRICS_SAMPLE_VECTOR_H_
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h
index d4c70f2..e149fca 100644
--- a/base/trace_event/builtin_categories.h
+++ b/base/trace_event/builtin_categories.h
@@ -154,7 +154,10 @@
     perfetto::Category("net.stream").SetDescription(
         "Includes events related to creating HTTP streams to serve requests."),
     perfetto::Category("network.scheduler"),
-    perfetto::Category("netlog").SetTags("navigation"),
+    perfetto::Category("netlog").SetTags("navigation").SetDescription(
+      "NetLog events and metadata. Describes the operation of the //net "
+      "network stack, e.g. HTTP requests, TLS, DNS, connections, sockets, "
+      "etc."),
     perfetto::Category("offline_pages"),
     perfetto::Category("omnibox"),
     perfetto::Category("oobe"),
@@ -310,6 +313,12 @@
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("mojom")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("navigation")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("net")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("netlog.sensitive")).SetTags(
+      "navigation", "sensitive").SetDescription(
+      "NetLog events and metadata, including sensitive information such as "
+      "hostnames, URLs, HTTP headers and other identifiable information. "
+      "Describes the operation of the //net network stack, e.g. HTTP requests, "
+      "TLS, DNS, connections, sockets, etc."),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("network")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("paint-worklet")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("power")),
diff --git a/build/android/chromiumide_api.py b/build/android/chromiumide_api.py
index 71b6021..e9cbe1a7 100755
--- a/build/android/chromiumide_api.py
+++ b/build/android/chromiumide_api.py
@@ -19,10 +19,9 @@
 import shutil
 import subprocess
 import sys
-from typing import List, Optional, Set, Tuple
+from typing import Iterator, List, Optional, Set, Tuple
 
-_SRC_ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), '..',
-                                          '..'))
+_SRC_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
 
 sys.path.append(os.path.join(_SRC_ROOT, 'build'))
 import gn_helpers
@@ -227,6 +226,32 @@
       source_jar_set.add(source_jar_path)
 
 
+def _find_params(output_dir: str) -> Iterator[str]:
+  """Finds all .params.json files within the output directory.
+
+  It uses list_java_targets.py to enumerate .params.json files, correctly
+  ignoring stale ones in the output directory.
+
+  Args:
+    output_dir: The path to the build output directory.
+
+  Yields:
+    The paths to the .params.json files.
+  """
+  output = subprocess.check_output(
+      [
+          os.path.join(_SRC_ROOT, 'build', 'android', 'list_java_targets.py'),
+          '--output-directory=' + output_dir,
+          '--print-params-paths',
+      ],
+      cwd=_SRC_ROOT,
+      encoding='utf-8',
+  )
+  for line in output.splitlines():
+    path = line.split(': ', 1)[1]
+    yield os.path.relpath(path, _SRC_ROOT)
+
+
 def _scan_params(output_dir: str) -> Tuple[List[str], List[str], List[str]]:
   """Scans the output directory for .params.json files and processes them.
 
@@ -247,12 +272,9 @@
   class_path_set: Set[str] = set()
   source_jar_set: Set[str] = set()
 
-  for dirpath, _, filenames in os.walk(os.path.join(output_dir, 'gen')):
-    for filename in filenames:
-      params_path = os.path.join(dirpath, filename)
-      if params_path.endswith('.params.json'):
-        _process_params(params_path, output_dir, source_path_set,
-                        class_path_set, source_jar_set)
+  for params_path in _find_params(output_dir):
+    _process_params(params_path, output_dir, source_path_set, class_path_set,
+                    source_jar_set)
 
   return sorted(source_path_set), sorted(class_path_set), sorted(source_jar_set)
 
diff --git a/build/config/siso/clang_exception.star b/build/config/siso/clang_exception.star
index e265bf9..c6452884 100644
--- a/build/config/siso/clang_exception.star
+++ b/build/config/siso/clang_exception.star
@@ -6,10 +6,11 @@
  or DeadlineExceeded.
 """
 
+load("@builtin//lib/gn.star", "gn")
 load("@builtin//runtime.star", "runtime")
 load("@builtin//struct.star", "module")
 
-def __step_config(ctx, step_config):
+def __step_config(ctx, step_config, use_windows_worker = None):
     cxx_exceptions = [
         {
             # TODO: crbug.com/380755128 - Make each compile unit smaller.
@@ -57,6 +58,10 @@
             "timeout": "4m",
         },
     ]
+
+    # Check if the build target is Windows.
+    is_target_windows = runtime.os == "windows" or ("args.gn" in ctx.metadata and gn.args(ctx).get("target_os") == '"win"')
+
     cc_exceptions = []
     new_rules = []
     for rule in step_config["rules"]:
@@ -72,18 +77,22 @@
                 r.update(rule)
                 r["name"] += "/exception/" + ex["name"]
                 outs = ex["action_outs"]
-                if runtime.os == "windows":
+                if is_target_windows:
                     outs = [obj.removesuffix(".o") + ".obj" for obj in outs if obj.startswith("./obj/")]
                 r["action_outs"] = outs
                 if "timeout" in ex:
                     r["timeout"] = ex["timeout"]
                 if "use_large" in ex and ex["use_large"]:
-                    # use `_large` variant of platform if it doesn't use default platform,
-                    # i.e. mac/win case.
-                    if "platform_ref" in r:
-                        r["platform_ref"] = r["platform_ref"] + "_large"
+                    if use_windows_worker:
+                        # Do not run large compiles on default Windows worker.
+                        r["remote"] = False
                     else:
-                        r["platform_ref"] = "large"
+                        # use `_large` variant of platform if it doesn't use default platform,
+                        # i.e. mac/win case.
+                        if "platform_ref" in r:
+                            r["platform_ref"] = r["platform_ref"] + "_large"
+                        else:
+                            r["platform_ref"] = "large"
                 if r.get("handler") == "rewrite_rewrapper":
                     r["handler"] = "rewrite_rewrapper_large"
                 new_rules.append(r)
diff --git a/build/config/siso/clang_linux.star b/build/config/siso/clang_linux.star
index d277f6e..59f01b3 100644
--- a/build/config/siso/clang_linux.star
+++ b/build/config/siso/clang_linux.star
@@ -7,6 +7,7 @@
 load("@builtin//struct.star", "module")
 load("./android.star", "android")
 load("./clang_all.star", "clang_all")
+load("./clang_exception.star", "clang_exception")
 load("./clang_unix.star", "clang_unix")
 load("./fuchsia.star", "fuchsia")
 load("./win_sdk.star", "win_sdk")
@@ -121,6 +122,7 @@
     step_config["rules"].extend(clang_unix.rules(ctx))
     if win_sdk.enabled(ctx):
         win_sdk.step_config(ctx, step_config)
+    step_config = clang_exception.step_config(ctx, step_config)
     return step_config
 
 clang = module(
diff --git a/build/config/siso/clang_mac.star b/build/config/siso/clang_mac.star
index ef6de2f..f0f3d02 100644
--- a/build/config/siso/clang_mac.star
+++ b/build/config/siso/clang_mac.star
@@ -7,6 +7,7 @@
 load("@builtin//lib/gn.star", "gn")
 load("@builtin//struct.star", "module")
 load("./clang_all.star", "clang_all")
+load("./clang_exception.star", "clang_exception")
 load("./clang_unix.star", "clang_unix")
 load("./mac_sdk.star", "mac_sdk")
 load("./rewrapper_cfg.star", "rewrapper_cfg")
@@ -49,6 +50,8 @@
             step_config["rules"].append(rule)
     elif gn.args(ctx).get("use_remoteexec") == "true":
         fail("remoteexec requires rewrapper config")
+
+    step_config = clang_exception.step_config(ctx, step_config)
     return step_config
 
 clang = module(
diff --git a/build/config/siso/clang_windows.star b/build/config/siso/clang_windows.star
index 9727c3ad..9f9ad12 100644
--- a/build/config/siso/clang_windows.star
+++ b/build/config/siso/clang_windows.star
@@ -8,6 +8,7 @@
 load("@builtin//struct.star", "module")
 load("./clang_all.star", "clang_all")
 load("./clang_code_coverage_wrapper.star", "clang_code_coverage_wrapper")
+load("./clang_exception.star", "clang_exception")
 load("./config.star", "config")
 load("./gn_logs.star", "gn_logs")
 load("./reproxy.star", "reproxy")
@@ -40,10 +41,10 @@
             largePlatform[k] = v
 
         # no "action_large" Windows worker pool
-        windowsWorker = True
+        use_windows_worker = True
         if reproxy_config["platform"]["OSFamily"] != "Windows":
             largePlatform["label:action_large"] = "1"
-            windowsWorker = False
+            use_windows_worker = False
         step_config["platforms"].update({
             "clang-cl": reproxy_config["platform"],
             "clang-cl_large": largePlatform,
@@ -79,7 +80,7 @@
         canonicalize_dir = not input_root_absolute_path
 
         timeout = "2m"
-        if (not reproxy.enabled(ctx)) and windowsWorker:
+        if (not reproxy.enabled(ctx)) and use_windows_worker:
             # use longer timeout for siso native
             # it takes long time for input fetch (many files in sysroot etc)
             timeout = "4m"
@@ -212,6 +213,7 @@
                 "timeout": "4m",
             },
         ])
+        step_config = clang_exception.step_config(ctx, step_config, use_windows_worker)
     elif gn.args(ctx).get("use_remoteexec") == "true":
         fail("remoteexec requires rewrapper config")
     return step_config
diff --git a/build/config/siso/main.star b/build/config/siso/main.star
index df73461..698c1fd 100644
--- a/build/config/siso/main.star
+++ b/build/config/siso/main.star
@@ -10,7 +10,6 @@
 load("@builtin//struct.star", "module")
 load("./backend_config/backend.star", "backend")
 load("./blink_all.star", "blink_all")
-load("./clang_exception.star", "clang_exception")
 load("./gn_logs.star", "gn_logs")
 load("./linux.star", chromium_linux = "chromium")
 load("./mac.star", chromium_mac = "chromium")
@@ -79,7 +78,6 @@
             arg0 = arg0.removesuffix(".exe")
         rule["remote_command"] = arg0
 
-    step_config = clang_exception.step_config(ctx, step_config)
     step_config = __disable_remote(ctx, step_config)
 
     filegroups = {}
diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn
index a71603f..aaeadc3 100644
--- a/build/config/win/BUILD.gn
+++ b/build/config/win/BUILD.gn
@@ -427,6 +427,7 @@
     "/DELAYLOAD:hid.dll",
     "/DELAYLOAD:imagehlp.dll",
     "/DELAYLOAD:imm32.dll",
+    "/DELAYLOAD:mmdevapi.dll",
     "/DELAYLOAD:msi.dll",
     "/DELAYLOAD:netapi32.dll",
     "/DELAYLOAD:ncrypt.dll",
diff --git a/cc/layers/texture_layer.cc b/cc/layers/texture_layer.cc
index 6cf28e3..21c8897 100644
--- a/cc/layers/texture_layer.cc
+++ b/cc/layers/texture_layer.cc
@@ -30,7 +30,6 @@
 TextureLayer::TextureLayer(TextureLayerClient* client)
     : client_(client),
       uv_bottom_right_(1.f, 1.f),
-      premultiplied_alpha_(true),
       blend_background_color_(false),
       force_texture_to_opaque_(false),
       needs_set_resource_(false) {}
@@ -62,13 +61,6 @@
   SetNeedsCommit();
 }
 
-void TextureLayer::SetPremultipliedAlpha(bool premultiplied_alpha) {
-  if (premultiplied_alpha_.Read(*this) == premultiplied_alpha)
-    return;
-  premultiplied_alpha_.Write(*this) = premultiplied_alpha;
-  SetNeedsCommit();
-}
-
 void TextureLayer::SetBlendBackgroundColor(bool blend) {
   if (blend_background_color_.Read(*this) == blend)
     return;
@@ -206,7 +198,6 @@
     TextureLayerImpl* texture_layer = static_cast<TextureLayerImpl*>(layer);
     texture_layer->SetUVTopLeft(uv_top_left_.Read(*this));
     texture_layer->SetUVBottomRight(uv_bottom_right_.Read(*this));
-    texture_layer->SetPremultipliedAlpha(premultiplied_alpha_.Read(*this));
     texture_layer->SetBlendBackgroundColor(blend_background_color_.Read(*this));
     texture_layer->SetForceTextureToOpaque(
         force_texture_to_opaque_.Read(*this));
diff --git a/cc/layers/texture_layer.h b/cc/layers/texture_layer.h
index cc803d9..cc1146fc 100644
--- a/cc/layers/texture_layer.h
+++ b/cc/layers/texture_layer.h
@@ -97,10 +97,6 @@
   // Sets a UV transform to be used at draw time. Defaults to (0, 0) and (1, 1).
   void SetUV(const gfx::PointF& top_left, const gfx::PointF& bottom_right);
 
-  // Sets whether the alpha channel is premultiplied or unpremultiplied.
-  // Defaults to true.
-  void SetPremultipliedAlpha(bool premultiplied_alpha);
-
   // Sets whether the texture should be blended with the background color
   // at draw time. Defaults to false.
   void SetBlendBackgroundColor(bool blend);
@@ -153,7 +149,6 @@
   ProtectedSequenceReadable<gfx::PointF> uv_top_left_;
   ProtectedSequenceReadable<gfx::PointF> uv_bottom_right_;
   // [bottom left, top left, top right, bottom right]
-  ProtectedSequenceReadable<bool> premultiplied_alpha_;
   ProtectedSequenceReadable<bool> blend_background_color_;
   ProtectedSequenceReadable<bool> force_texture_to_opaque_;
 
diff --git a/cc/layers/texture_layer_impl.cc b/cc/layers/texture_layer_impl.cc
index 682ccdb2..d342a87 100644
--- a/cc/layers/texture_layer_impl.cc
+++ b/cc/layers/texture_layer_impl.cc
@@ -53,7 +53,6 @@
   TextureLayerImpl* texture_layer = static_cast<TextureLayerImpl*>(layer);
   texture_layer->SetUVTopLeft(uv_top_left_);
   texture_layer->SetUVBottomRight(uv_bottom_right_);
-  texture_layer->SetPremultipliedAlpha(premultiplied_alpha_);
   texture_layer->SetBlendBackgroundColor(blend_background_color_);
   texture_layer->SetForceTextureToOpaque(force_texture_to_opaque_);
   if (needs_set_resource_push_) {
@@ -101,11 +100,6 @@
             transferable_resource_.resource_source));
       }
 
-      // NOTE: We must perform any updates to `transferable_resource_` *before*
-      // exporting it to the resource provider, as that copies the resource into
-      // a new instance that is what is transferred across processes.
-      transferable_resource_.alpha_type =
-          premultiplied_alpha_ ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
       resource_id_ = resource_provider->ImportResource(
           transferable_resource_,
           /* impl_thread_release_callback= */ viz::ReleaseCallback(),
@@ -194,10 +188,6 @@
   return transferable_resource_.color_space.GetContentColorUsage();
 }
 
-void TextureLayerImpl::SetPremultipliedAlpha(bool premultiplied_alpha) {
-  premultiplied_alpha_ = premultiplied_alpha;
-}
-
 void TextureLayerImpl::SetBlendBackgroundColor(bool blend) {
   blend_background_color_ = blend;
 }
diff --git a/cc/layers/texture_layer_impl.h b/cc/layers/texture_layer_impl.h
index 171eea86..683a8ea6 100644
--- a/cc/layers/texture_layer_impl.h
+++ b/cc/layers/texture_layer_impl.h
@@ -52,7 +52,6 @@
   // must explicitly invalidate if they intend to cause a visible change in the
   // layer's output.
   void SetTextureId(unsigned id);
-  void SetPremultipliedAlpha(bool premultiplied_alpha);
   void SetBlendBackgroundColor(bool blend);
   void SetForceTextureToOpaque(bool opaque);
   void SetUVTopLeft(const gfx::PointF& top_left);
@@ -70,7 +69,6 @@
   static bool MayEvictResourceInBackground(
       viz::TransferableResource::ResourceSource source);
 
-  bool premultiplied_alpha() const { return premultiplied_alpha_; }
   bool blend_background_color() const { return blend_background_color_; }
   bool force_texture_to_opaque() const { return force_texture_to_opaque_; }
   bool needs_set_resource_push() const { return needs_set_resource_push_; }
@@ -89,7 +87,6 @@
   void FreeTransferableResource();
   void OnResourceEvicted();
 
-  bool premultiplied_alpha_ = true;
   bool blend_background_color_ = false;
   bool force_texture_to_opaque_ = false;
 
diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc
index 653a4af..eebd347 100644
--- a/cc/layers/texture_layer_unittest.cc
+++ b/cc/layers/texture_layer_unittest.cc
@@ -259,7 +259,6 @@
                                      ));
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetUV(gfx::PointF(0.25f, 0.25f),
                                                gfx::PointF(0.75f, 0.75f)));
-  EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetPremultipliedAlpha(false));
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetBlendBackgroundColor(true));
 }
 
diff --git a/cc/mojo_embedder/viz_layer_context.cc b/cc/mojo_embedder/viz_layer_context.cc
index 256dc72..6f6692fe 100644
--- a/cc/mojo_embedder/viz_layer_context.cc
+++ b/cc/mojo_embedder/viz_layer_context.cc
@@ -544,7 +544,6 @@
                                 viz::mojom::TextureLayerExtraPtr& extra,
                                 viz::ClientResourceProvider& resource_provider,
                                 viz::RasterContextProvider& context_provider) {
-  extra->premultiplied_alpha = layer.premultiplied_alpha();
   extra->blend_background_color = layer.blend_background_color();
   extra->force_texture_to_opaque = layer.force_texture_to_opaque();
   extra->uv_top_left = layer.uv_top_left();
diff --git a/chrome/VERSION b/chrome/VERSION
index 6a26e223..6c52b71b 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=138
 MINOR=0
-BUILD=7182
+BUILD=7183
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 74ae438..a2b7759a 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -513,6 +513,7 @@
       "//chrome/browser/ui/android/edge_to_edge:factory_java",
       "//chrome/browser/ui/android/edge_to_edge:java",
       "//chrome/browser/ui/android/ephemeraltab:java",
+      "//chrome/browser/ui/android/extensions:java",
       "//chrome/browser/ui/android/fast_checkout:java",
       "//chrome/browser/ui/android/favicon:java",
       "//chrome/browser/ui/android/google_bottom_bar:java",
@@ -3247,6 +3248,10 @@
       "//chrome/browser/partnerbookmarks:jni_headers",
       "//chrome/browser/webid:jni_headers",
     ]
+
+    if (enable_extensions_core) {
+      public_deps += [ "//chrome/browser/ui/android/extensions:jni_headers" ]
+    }
   }
 
   source_set("chrome_test_util") {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 232b60a..fb7d82b3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -267,6 +267,7 @@
 import org.chromium.chrome.browser.ui.appmenu.AppMenuPropertiesDelegate;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeUtils;
+import org.chromium.chrome.browser.ui.extensions.ExtensionKeybindingRegistry;
 import org.chromium.chrome.browser.ui.searchactivityutils.SearchActivityClient;
 import org.chromium.chrome.browser.ui.searchactivityutils.SearchActivityExtras.IntentOrigin;
 import org.chromium.chrome.browser.ui.signin.BottomSheetSigninAndHistorySyncConfig;
@@ -555,6 +556,8 @@
     private SuggestionEventObserver mSuggestionEventObserver;
     private GroupSuggestionsPromotionCoordinator mGroupSuggestionsPromotionCoordinator;
 
+    @Nullable private ExtensionKeybindingRegistry mExtensionKeybindingRegistry;
+
     /** Constructs a ChromeTabbedActivity. */
     public ChromeTabbedActivity() {
         mIntentHandlingTimeMs = SystemClock.uptimeMillis();
@@ -1343,6 +1346,10 @@
                                             .getTabGroupModelFilter(false));
                 }
             }
+
+            mExtensionKeybindingRegistry =
+                    ExtensionKeybindingRegistry.maybeCreate(
+                            getProfileProviderSupplier().get().getOriginalProfile());
         }
     }
 
@@ -3871,6 +3878,11 @@
             mGroupSuggestionsPromotionCoordinator = null;
         }
 
+        if (mExtensionKeybindingRegistry != null) {
+            mExtensionKeybindingRegistry.destroy();
+            mExtensionKeybindingRegistry = null;
+        }
+
         super.onDestroyInternal();
     }
 
@@ -3898,6 +3910,15 @@
                         getFullscreenManager(),
                         /* menuOrKeyboardActionController= */ this,
                         this);
+
+        if (Boolean.TRUE.equals(result)) return result;
+
+        if (mExtensionKeybindingRegistry != null) {
+            if (mExtensionKeybindingRegistry.handleKeyEvent(event)) {
+                result = true;
+            }
+        }
+
         return result != null ? result : super.dispatchKeyEvent(event);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenToast.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenToast.java
index c03f5b3c..d0fa45f1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenToast.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenToast.java
@@ -8,6 +8,7 @@
 import android.view.Gravity;
 
 import org.chromium.base.BuildInfo;
+import org.chromium.base.DeviceInfo;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.ui.UiUtils;
@@ -92,11 +93,10 @@
                     UiUtils.isGestureNavigationMode(mActivity.getWindow())
                             ? R.string.immersive_fullscreen_gesture_navigation_mode_api_notification
                             : R.string.immersive_fullscreen_api_notification;
-            if (BuildInfo.getInstance().isDesktop) {
-                if (ChromeFeatureList.isEnabled(
-                        ChromeFeatureList.DISPLAY_EDGE_TO_EDGE_FULLSCREEN)) {
-                    toastTextId = R.string.immersive_fullscreen_api_notification_desktop;
-                }
+            if (DeviceInfo.isDesktop()
+                    && ChromeFeatureList.isEnabled(
+                            ChromeFeatureList.DISPLAY_EDGE_TO_EDGE_FULLSCREEN)) {
+                toastTextId = R.string.immersive_fullscreen_api_notification_desktop;
             }
             if (BuildInfo.getInstance().isAutomotive) {
                 toastTextId = R.string.immersive_fullscreen_automotive_toolbar_improvements;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitTaskRunner.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitTaskRunner.java
index 56c7d50..74894b9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitTaskRunner.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitTaskRunner.java
@@ -6,9 +6,9 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import org.chromium.base.BuildInfo;
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
+import org.chromium.base.DeviceInfo;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryPrefetcher;
@@ -80,7 +80,7 @@
             }
             // TODO(crbug.com/389565104): Remove this if block when ready to move desktop to stable
             // builds.
-            if (VersionInfo.isStableBuild() && BuildInfo.getInstance().isDesktop) {
+            if (VersionInfo.isStableBuild() && DeviceInfo.isDesktop()) {
                 return "dev";
             }
             if (VersionInfo.isBetaBuild()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/CustomLinkOperations.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/CustomLinkOperations.java
index c9c5e6dc8..8fcd6ad7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/CustomLinkOperations.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/CustomLinkOperations.java
@@ -17,9 +17,10 @@
      *
      * @param name The name of the added tile.
      * @param url The URL of the added tile.
+     * @param pos The position to add tile, with null indicating add to end.
      * @return Whether the operation successfully ran.
      */
-    boolean addCustomLink(String name, @Nullable GURL url);
+    boolean addCustomLink(String name, @Nullable GURL url, @Nullable Integer pos);
 
     /**
      * Assigns a link identified by {@param keyUrl} to a custom link specified with {@param name}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java
index e0c94f7..ce5d62c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java
@@ -41,8 +41,13 @@
 
     // CustomLinkOperations -> MostVisitedSites implementation.
     @Override
-    public boolean addCustomLink(String name, @Nullable GURL url) {
+    public boolean addCustomLink(String name, @Nullable GURL url, @Nullable Integer pos) {
         if (mNativeMostVisitedSitesBridge == 0 || GURL.isEmptyOrInvalid(url)) return false;
+        if (pos != null) {
+            return MostVisitedSitesBridgeJni.get()
+                    .addCustomLinkTo(mNativeMostVisitedSitesBridge, name, url, pos.intValue());
+        }
+
         return MostVisitedSitesBridgeJni.get()
                 .addCustomLink(mNativeMostVisitedSitesBridge, name, url);
     }
@@ -193,6 +198,12 @@
                 @JniType("Profile*") Profile profile,
                 boolean enableCustomLinks);
 
+        boolean addCustomLinkTo(
+                long nativeMostVisitedSitesBridge,
+                @JniType("std::u16string") String caller,
+                @JniType("GURL") GURL url,
+                int pos);
+
         boolean addCustomLink(
                 long nativeMostVisitedSitesBridge,
                 @JniType("std::u16string") String caller,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
index 0bba819..652f518 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
@@ -114,6 +114,12 @@
         CustomTileEditCoordinator createCustomTileEditCoordinator(@Nullable Tile originalTile);
 
         /**
+         * Displays the snackbar to inform the user that a tile was unpinned and to offer the
+         * opportunity to undo the operation.
+         */
+        void showTileUnpinSnackbar(Runnable undoHandler);
+
+        /**
          * To be called before this instance is abandoned to the garbage collector so it can do any
          * necessary cleanups. This instance must not be used after this method is called.
          */
@@ -671,7 +677,10 @@
             CustomTileEditCoordinator customTileEditCoordinator =
                     mTileGroupDelegate.createCustomTileEditCoordinator(/* originalTile= */ null);
             customTileEditCoordinator.show(
-                    this::addCustomLinkAndUpdateOnSuccess, mTileGroupDelegate::hasCustomLink);
+                    (String name, GURL url) -> {
+                        return addCustomLinkAndUpdateOnSuccess(name, url, /* pos= */ null);
+                    },
+                    mTileGroupDelegate::hasCustomLink);
         }
 
         @Override
@@ -689,7 +698,7 @@
             @Nullable Tile tile = findTile(suggestion);
             if (tile == null) return;
 
-            deleteCustomLinkAndUpdateOnSuccess(tile.getUrl());
+            deleteCustomLinkAndUpdateOnSuccess(tile);
         }
 
         @Override
@@ -720,14 +729,15 @@
             return mCustomTileCountIsUnderLimit;
         }
 
-        private boolean addCustomLinkAndUpdateOnSuccess(String name, GURL url) {
+        private boolean addCustomLinkAndUpdateOnSuccess(
+                String name, GURL url, @Nullable Integer pos) {
             if (!TileUtils.isValidCustomTileName(name) || !TileUtils.isValidCustomTileUrl(url)) {
                 return false;
             }
 
             // On success, onSiteSuggestionsAvailable() triggers.
             mPendingChanges.customTilesIndicator = true;
-            boolean success = mTileGroupDelegate.addCustomLink(name, url);
+            boolean success = mTileGroupDelegate.addCustomLink(name, url, pos);
             if (!success) {
                 mPendingChanges.customTilesIndicator = false;
             }
@@ -750,10 +760,18 @@
             return success;
         }
 
-        private void deleteCustomLinkAndUpdateOnSuccess(GURL url) {
+        private void deleteCustomLinkAndUpdateOnSuccess(Tile tile) {
             // On success, onSiteSuggestionsAvailable() triggers.
             mPendingChanges.customTilesIndicator = true;
-            if (!mTileGroupDelegate.deleteCustomLink(url)) {
+
+            if (mTileGroupDelegate.deleteCustomLink(tile.getUrl())) {
+                mTileGroupDelegate.showTileUnpinSnackbar(
+                        () -> {
+                            // Perform undo by adding the tile back at its original index.
+                            addCustomLinkAndUpdateOnSuccess(
+                                    tile.getTitle(), tile.getUrl(), tile.getIndex());
+                        });
+            } else {
                 mPendingChanges.customTilesIndicator = false;
             }
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java
index cb195f7e..69ff64c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java
@@ -57,6 +57,7 @@
 
     private boolean mIsDestroyed;
     private SnackbarController mTileRemovedSnackbarController;
+    private SnackbarController mTileUnpinnedSnackbarController;
 
     public TileGroupDelegateImpl(
             Context context,
@@ -73,20 +74,23 @@
 
     // CustomLinkOperations -> TileGroup.Delegate implementation.
     @Override
-    public boolean addCustomLink(String name, @Nullable GURL url) {
+    public boolean addCustomLink(String name, @Nullable GURL url, @Nullable Integer pos) {
         assert !mIsDestroyed;
-        return mMostVisitedSites.addCustomLink(name, url);
+        dismissAllSnackbars();
+        return mMostVisitedSites.addCustomLink(name, url, pos);
     }
 
     @Override
     public boolean assignCustomLink(GURL keyUrl, String name, @Nullable GURL url) {
         assert !mIsDestroyed;
+        dismissAllSnackbars();
         return mMostVisitedSites.assignCustomLink(keyUrl, name, url);
     }
 
     @Override
     public boolean deleteCustomLink(GURL keyUrl) {
         assert !mIsDestroyed;
+        dismissAllSnackbars();
         return mMostVisitedSites.deleteCustomLink(keyUrl);
     }
 
@@ -99,6 +103,7 @@
     @Override
     public boolean reorderCustomLink(GURL keyUrl, int newPos) {
         assert !mIsDestroyed;
+        dismissAllSnackbars();
         return mMostVisitedSites.reorderCustomLink(keyUrl, newPos);
     }
 
@@ -197,14 +202,48 @@
     }
 
     @Override
+    public void showTileUnpinSnackbar(Runnable undoHandler) {
+        if (mTileUnpinnedSnackbarController == null) {
+            mTileUnpinnedSnackbarController =
+                    new SnackbarController() {
+                        @Override
+                        public void onDismissNoAction(Object actionData) {}
+
+                        /** Undoes the tile removal. */
+                        @Override
+                        public void onAction(Object actionData) {
+                            if (mIsDestroyed) return;
+                            Runnable undoHandlerFromData = (Runnable) actionData;
+                            undoHandlerFromData.run();
+                        }
+                    };
+        }
+        Snackbar snackbar =
+                Snackbar.make(
+                                mContext.getString(R.string.most_visited_item_removed),
+                                mTileUnpinnedSnackbarController,
+                                Snackbar.TYPE_ACTION,
+                                Snackbar.UMA_NTP_MOST_VISITED_UNPIN_UNDO)
+                        .setAction(mContext.getString(R.string.undo), undoHandler);
+        mSnackbarManager.showSnackbar(snackbar);
+    }
+
+    @Override
     public void destroy() {
         assert !mIsDestroyed;
         mIsDestroyed = true;
 
+        dismissAllSnackbars();
+        mMostVisitedSites.destroy();
+    }
+
+    private void dismissAllSnackbars() {
+        if (mTileUnpinnedSnackbarController != null) {
+            mSnackbarManager.dismissSnackbars(mTileUnpinnedSnackbarController);
+        }
         if (mTileRemovedSnackbarController != null) {
             mSnackbarManager.dismissSnackbars(mTileRemovedSnackbarController);
         }
-        mMostVisitedSites.destroy();
     }
 
     private void showTileRemovedSnackbar(GURL url) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 016ae17..b6071b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -1665,9 +1665,11 @@
                 mLayoutStateProvider != null
                         ? mLayoutStateProvider.getActiveLayoutType() == LayoutType.TAB_SWITCHER
                         : false);
-        ObservableSupplier<Integer> keyboardAccessoryHeightSupplier =
-                new KeyboardAccessoryHeightSupplier(
+        KeyboardAccessoryStateSupplier keyboardAccessoryHeightSupplier =
+                new KeyboardAccessoryStateSupplier(
                         ManualFillingComponentSupplier.from(mWindowAndroid));
+        ObservableSupplierImpl<Integer> controlContainerTranslationSupplier =
+                new ObservableSupplierImpl<>(0);
         new ToolbarPositionController(
                 mBrowserControlsSizer,
                 ContextUtils.getAppSharedPreferences(),
@@ -1682,6 +1684,7 @@
                 mBottomControlsStacker,
                 mBottomToolbarControlsOffsetSupplier,
                 mProgressBarContainer,
+                controlContainerTranslationSupplier,
                 mActivity);
         if (ChromeFeatureList.sMiniOriginBar.isEnabled()) {
             mMiniOriginBarController =
@@ -1692,7 +1695,12 @@
                             mActivity,
                             mControlContainer,
                             mSuppressToolbarSceneLayerSupplier,
-                            mBrowserControlsSizer);
+                            mBrowserControlsSizer,
+                            mWindowAndroid.getInsetObserver(),
+                            controlContainerTranslationSupplier,
+                            () ->
+                                    keyboardAccessoryHeightSupplier.isSheetShowing(
+                                            mControlContainer.getView()));
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index 817ab37..1c55454 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -19,7 +19,6 @@
 
 import androidx.annotation.CallSuper;
 import androidx.annotation.ColorInt;
-import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
@@ -32,7 +31,6 @@
 import org.chromium.base.CallbackController;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.lifetime.Destroyable;
-import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
@@ -150,6 +148,7 @@
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeControllerFactory;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeUtils;
+import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeUtils.MissingNavbarInsetsReason;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.ui.native_page.NativePage;
 import org.chromium.chrome.browser.ui.system.StatusBarColorController;
@@ -212,21 +211,6 @@
                 AppMenuBlocker,
                 ContextualSearchTabPromotionDelegate,
                 WindowFocusChangedObserver {
-    private static final String MISSING_NAVBAR_INSETS_HISTOGRAM =
-            "Android.EdgeToEdge.MissingNavbarInsets";
-
-    @IntDef({
-        MissingNavbarInsetsReason.OTHER,
-        MissingNavbarInsetsReason.IN_MULTI_WINDOW,
-        MissingNavbarInsetsReason.IN_DESKTOP_WINDOW,
-        MissingNavbarInsetsReason.NUM_ENTRIES
-    })
-    @interface MissingNavbarInsetsReason {
-        int OTHER = 0;
-        int IN_MULTI_WINDOW = 1;
-        int IN_DESKTOP_WINDOW = 2;
-        int NUM_ENTRIES = 3;
-    }
 
     protected final UnownedUserDataSupplier<TabObscuringHandler> mTabObscuringHandlerSupplier =
             new TabObscuringHandlerSupplier();
@@ -1862,27 +1846,36 @@
     private void recordIfMissingNavigationBar() {
         var rootInsets = mActivity.getWindow().getDecorView().getRootWindowInsets();
         assert rootInsets != null;
-        Insets navigationBarInsets =
-                WindowInsetsCompat.toWindowInsetsCompat(rootInsets)
-                        .getInsets(WindowInsetsCompat.Type.navigationBars());
-        if (!navigationBarInsets.equals(Insets.NONE)) return;
 
-        if (AppHeaderUtils.isAppInDesktopWindow(getDesktopWindowStateManager())) {
-            RecordHistogram.recordEnumeratedHistogram(
-                    MISSING_NAVBAR_INSETS_HISTOGRAM,
-                    MissingNavbarInsetsReason.IN_DESKTOP_WINDOW,
-                    MissingNavbarInsetsReason.NUM_ENTRIES);
-        } else if (MultiWindowUtils.getInstance().isInMultiWindowMode(mActivity)) {
-            RecordHistogram.recordEnumeratedHistogram(
-                    MISSING_NAVBAR_INSETS_HISTOGRAM,
-                    MissingNavbarInsetsReason.IN_MULTI_WINDOW,
-                    MissingNavbarInsetsReason.NUM_ENTRIES);
-        } else {
-            RecordHistogram.recordEnumeratedHistogram(
-                    MISSING_NAVBAR_INSETS_HISTOGRAM,
-                    MissingNavbarInsetsReason.OTHER,
-                    MissingNavbarInsetsReason.NUM_ENTRIES);
+        var rootInsetsCompat = WindowInsetsCompat.toWindowInsetsCompat(rootInsets);
+        Insets navigationBarInsets =
+                rootInsetsCompat.getInsets(WindowInsetsCompat.Type.navigationBars());
+        if (!navigationBarInsets.equals(Insets.NONE)) {
+            return;
         }
+
+        @MissingNavbarInsetsReason int reason;
+        if (AppHeaderUtils.isAppInDesktopWindow(getDesktopWindowStateManager())) {
+            reason = MissingNavbarInsetsReason.IN_DESKTOP_WINDOW;
+        } else if (MultiWindowUtils.getInstance().isInMultiWindowMode(mActivity)) {
+            reason = MissingNavbarInsetsReason.IN_MULTI_WINDOW;
+        } else if (mFullscreenManager.getPersistentFullscreenMode()) {
+            // Fullscreen mode can lead to empty system bar insets. Being in fullscreen mode during
+            // activity recreation should be rare.
+            reason = MissingNavbarInsetsReason.IN_FULLSCREEN;
+        } else if (!MultiWindowUtils.isActivityVisible(mActivity)) {
+            // When activity is not visible (during theme recreation) we could get empty system bar
+            // insets.
+            reason = MissingNavbarInsetsReason.ACTIVITY_NOT_VISIBLE;
+        } else if (rootInsetsCompat
+                .getInsets(WindowInsetsCompat.Type.systemBars())
+                .equals(Insets.NONE)) {
+            reason = MissingNavbarInsetsReason.SYSTEM_BAR_INSETS_EMPTY;
+        } else {
+            reason = MissingNavbarInsetsReason.OTHER;
+        }
+
+        EdgeToEdgeUtils.recordIfMissingNavigationBar(reason);
     }
 
     /** Create a bottom chin for Edge-to-Edge. */
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt
index e339a6c..d902cd7 100644
--- a/chrome/android/profiles/arm.newest.txt
+++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-138.0.7171.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-arm-138.0.7180.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index ebbf4391..e590445 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-138.0.7171.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-138.0.7180.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 198b38cd..30962af 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1857,7 +1857,7 @@
 
     # TODO(crbug.com/364667551): includes //c/b/about_flags.h and other sources
     # from //chrome/browser/ui/webui, which are not modularized yet.
-    "//chrome/browser/ui/webui/about",
+    "//chrome/browser/ui/webui/about:impl",
   ]
 
   public_deps = [
@@ -2060,6 +2060,7 @@
     "//chrome/browser/ui/webui",
     "//chrome/browser/ui/webui:configs",
     "//chrome/browser/ui/webui/about",
+    "//chrome/browser/ui/webui/about:impl",
     "//chrome/browser/ui/webui/bluetooth_internals",
     "//chrome/browser/ui/webui/bluetooth_internals:mojo_bindings",
     "//chrome/browser/ui/webui/internal_debug_pages_disabled",
@@ -5507,7 +5508,6 @@
       "//chrome/browser/ui/webui/ash/emoji",
       "//chrome/browser/ui/webui/ash/enterprise_reporting",
       "//chrome/browser/ui/webui/ash/extended_updates",
-      "//chrome/browser/ui/webui/ash/floating_workspace",
       "//chrome/browser/ui/webui/ash/internet",
       "//chrome/browser/ui/webui/ash/kerberos",
       "//chrome/browser/ui/webui/ash/launcher_internals",
@@ -5999,7 +5999,6 @@
       "//chrome/browser/ui/webui/ash/emoji",
       "//chrome/browser/ui/webui/ash/enterprise_reporting",
       "//chrome/browser/ui/webui/ash/extended_updates",
-      "//chrome/browser/ui/webui/ash/floating_workspace",
       "//chrome/browser/ui/webui/ash/kerberos",
       "//chrome/browser/ui/webui/ash/lobster",
       "//chrome/browser/ui/webui/ash/lock_screen_reauth",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 3c66073..8abfa53 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -9909,6 +9909,11 @@
      flag_descriptions::kTabStripContextMenuAndroidDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kTabStripContextMenuAndroid)},
 
+    {"tab-strip-density-change-android",
+     flag_descriptions::kTabStripDensityChangeAndroidName,
+     flag_descriptions::kTabStripDensityChangeAndroidDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kTabStripDensityChangeAndroid)},
+
     {"tab-strip-group-drag-drop-android",
      flag_descriptions::kTabStripGroupDragDropAndroidName,
      flag_descriptions::kTabStripGroupDragDropAndroidDescription, kOsAndroid,
diff --git a/chrome/browser/android/ntp/most_visited_sites_bridge.cc b/chrome/browser/android/ntp/most_visited_sites_bridge.cc
index 66c37ab..afd72b9 100644
--- a/chrome/browser/android/ntp/most_visited_sites_bridge.cc
+++ b/chrome/browser/android/ntp/most_visited_sites_bridge.cc
@@ -209,6 +209,13 @@
   most_visited_->AddMostVisitedURLsObserver(java_observer_.get(), num_sites);
 }
 
+jboolean MostVisitedSitesBridge::AddCustomLinkTo(JNIEnv* env,
+                                                 const std::u16string& name,
+                                                 const GURL& url,
+                                                 jint pos) {
+  return most_visited_->AddCustomLinkTo(url, name, pos);
+}
+
 jboolean MostVisitedSitesBridge::AddCustomLink(JNIEnv* env,
                                                const std::u16string& name,
                                                const GURL& url) {
diff --git a/chrome/browser/android/ntp/most_visited_sites_bridge.h b/chrome/browser/android/ntp/most_visited_sites_bridge.h
index c30ec0ac..4cf5950 100644
--- a/chrome/browser/android/ntp/most_visited_sites_bridge.h
+++ b/chrome/browser/android/ntp/most_visited_sites_bridge.h
@@ -42,6 +42,11 @@
                          const base::android::JavaParamRef<jobject>& obj,
                          const base::android::JavaParamRef<jobject>& j_client);
 
+  jboolean AddCustomLinkTo(JNIEnv* env,
+                           const std::u16string& name,
+                           const GURL& url,
+                           jint pos);
+
   jboolean AddCustomLink(JNIEnv* env,
                          const std::u16string& name,
                          const GURL& url);
diff --git a/chrome/browser/apps/app_discovery_service/almanac_api/BUILD.gn b/chrome/browser/apps/app_discovery_service/almanac_api/BUILD.gn
index 29175df..166fcd5c 100644
--- a/chrome/browser/apps/app_discovery_service/almanac_api/BUILD.gn
+++ b/chrome/browser/apps/app_discovery_service/almanac_api/BUILD.gn
@@ -22,5 +22,5 @@
   generate_cc = false
   generate_python = false
   generate_descriptor = "launcher_app.descriptor"
-  proto_deps = [ "//chrome/browser/apps/almanac_api_client/proto" ]
+  deps = [ "//chrome/browser/apps/almanac_api_client/proto" ]
 }
diff --git a/chrome/browser/ash/audio/OWNERS b/chrome/browser/ash/audio/OWNERS
index 52736fa..506e41a 100644
--- a/chrome/browser/ash/audio/OWNERS
+++ b/chrome/browser/ash/audio/OWNERS
@@ -1,5 +1,4 @@
 aaronyu@google.com
 enshuo@chromium.org
 hychao@chromium.org
-whalechang@google.com
 yuhsuan@chromium.org
diff --git a/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle.cc b/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle.cc
index 0cf35611..391f18a3 100644
--- a/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle.cc
+++ b/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle.cc
@@ -76,8 +76,8 @@
 }  // namespace
 
 OnTaskLockedSessionNavigationThrottle::OnTaskLockedSessionNavigationThrottle(
-    content::NavigationHandle* navigation_handle)
-    : content::NavigationThrottle(navigation_handle) {}
+    content::NavigationThrottleRegistry& registry)
+    : content::NavigationThrottle(registry) {}
 
 OnTaskLockedSessionNavigationThrottle::
     ~OnTaskLockedSessionNavigationThrottle() = default;
@@ -87,18 +87,18 @@
 }
 
 // static
-std::unique_ptr<content::NavigationThrottle>
-OnTaskLockedSessionNavigationThrottle::MaybeCreateThrottleFor(
-    content::NavigationHandle* handle) {
+void OnTaskLockedSessionNavigationThrottle::MaybeCreateAndAdd(
+    content::NavigationThrottleRegistry& registry) {
+  content::NavigationHandle& handle = registry.GetNavigationHandle();
   if (!ash::boca_util::IsEnabled(
           ash::BrowserContextHelper::Get()->GetUserByBrowserContext(
-              handle->GetWebContents()->GetBrowserContext()))) {
-    return nullptr;
+              handle.GetWebContents()->GetBrowserContext()))) {
+    return;
   }
 
   LockedSessionWindowTracker* const window_tracker =
       LockedSessionWindowTrackerFactory::GetForBrowserContext(
-          handle->GetWebContents()->GetBrowserContext());
+          handle.GetWebContents()->GetBrowserContext());
   // We do not need to create the throttle when we are not currently observing a
   // window that needs to be in locked mode, or if the navigation throttle is
   // not ready to start (where we are adding new tabs), or if the navigation is
@@ -107,27 +107,28 @@
   // we are not navigating to a new page).
   if (!window_tracker || !window_tracker->browser() ||
       !window_tracker->can_start_navigation_throttle()) {
-    return nullptr;
+    return;
   }
 
-  if (!handle->IsInOutermostMainFrame()) {
-    return nullptr;
+  if (!handle.IsInOutermostMainFrame()) {
+    return;
   }
 
-  if (handle->IsSameDocument()) {
-    return nullptr;
+  if (handle.IsSameDocument()) {
+    return;
   }
 
   Browser* const content_browser =
-      LockedSessionWindowTracker::GetBrowserWithTab(handle->GetWebContents());
+      LockedSessionWindowTracker::GetBrowserWithTab(handle.GetWebContents());
 
   // Ensure we only apply the nav throttle on OnTask SWA navigations.
   if (content_browser && (content_browser != window_tracker->browser() &&
                           !content_browser->is_type_app_popup())) {
-    return nullptr;
+    return;
   }
-  window_tracker->ObserveWebContents(handle->GetWebContents());
-  return base::WrapUnique(new OnTaskLockedSessionNavigationThrottle(handle));
+  window_tracker->ObserveWebContents(handle.GetWebContents());
+  registry.AddThrottle(
+      base::WrapUnique(new OnTaskLockedSessionNavigationThrottle(registry)));
 }
 
 void OnTaskLockedSessionNavigationThrottle::MaybeShowBlockedURLToast() {
diff --git a/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle.h b/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle.h
index be77646b..e93f1b0 100644
--- a/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle.h
+++ b/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle.h
@@ -19,8 +19,7 @@
  public:
   // Returns a navigation throttle when the navigation is happening inside
   // a tabbed web app and the tabbed web app has a pinned home tab.
-  static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
-      content::NavigationHandle* handle);
+  static void MaybeCreateAndAdd(content::NavigationThrottleRegistry& registry);
 
   ~OnTaskLockedSessionNavigationThrottle() override;
 
@@ -32,7 +31,7 @@
 
  private:
   explicit OnTaskLockedSessionNavigationThrottle(
-      content::NavigationHandle* handle);
+      content::NavigationThrottleRegistry& registry);
 
   // Checks to see if the url we are currently navigating should be blocked
   // regardless of restriction levels. This includes special chrome urls, files,
diff --git a/chrome/browser/ash/floating_workspace/BUILD.gn b/chrome/browser/ash/floating_workspace/BUILD.gn
index 87d9c990..a31dcf0 100644
--- a/chrome/browser/ash/floating_workspace/BUILD.gn
+++ b/chrome/browser/ash/floating_workspace/BUILD.gn
@@ -94,7 +94,6 @@
     "//chrome/browser/ui/ash/multi_user",
     "//chrome/browser/ui/ash/session",
     "//chrome/browser/ui/ash/session:test_support",
-    "//chrome/browser/ui/webui/ash/floating_workspace:floating_workspace",
     "//chrome/test:test_support",
     "//chromeos/ash/components/login/session",
     "//chromeos/ash/components/network",
diff --git a/chrome/browser/ash/floating_workspace/DEPS b/chrome/browser/ash/floating_workspace/DEPS
index 5b61cf03..ad24539b 100644
--- a/chrome/browser/ash/floating_workspace/DEPS
+++ b/chrome/browser/ash/floating_workspace/DEPS
@@ -28,7 +28,6 @@
   "+chrome/browser/ui/ash/multi_user",
   "+chrome/browser/ui/ash/session",
   "+chrome/browser/ui/settings_window_manager_chromeos.h",
-  "+chrome/browser/ui/webui/ash/floating_workspace",
   "+chrome/common/pref_names.h",
   "+chrome/grit",
   "+chrome/test/base",
diff --git a/chrome/browser/ash/floating_workspace/floating_workspace_service.cc b/chrome/browser/ash/floating_workspace/floating_workspace_service.cc
index 2b7cc34..23608d2 100644
--- a/chrome/browser/ash/floating_workspace/floating_workspace_service.cc
+++ b/chrome/browser/ash/floating_workspace/floating_workspace_service.cc
@@ -11,6 +11,7 @@
 
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/desk_template.h"
+#include "ash/public/cpp/notification_utils.h"
 #include "ash/public/cpp/session/session_controller.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
@@ -32,6 +33,8 @@
 #include "chrome/browser/ash/login/session/user_session_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/notifications/notification_display_service.h"
+#include "chrome/browser/notifications/notification_display_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sessions/session_restore.h"
 #include "chrome/browser/sync/desk_sync_service_factory.h"
@@ -41,7 +44,6 @@
 #include "chrome/browser/ui/ash/desks/desks_client.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
-#include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
@@ -61,6 +63,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/user_activity/user_activity_detector.h"
 #include "ui/chromeos/devicetype_utils.h"
+#include "ui/message_center/public/cpp/notification.h"
 
 namespace {
 
@@ -79,12 +82,38 @@
 
 namespace ash {
 
+constexpr char kNotificationForNoNetworkConnection[] =
+    "notification_no_network_connection";
+constexpr char kNotificationForSyncErrorOrTimeOut[] =
+    "notification_sync_error_or_timeout";
+constexpr char kNotificationForProgressStatus[] =
+    "notification_progress_status";
+constexpr char kSafeMode[] = "notification_safe_mode";
 // Default time without activity after which a floating workspace template is
 // considered stale and becomes a candidate for garbage collection.
 constexpr base::TimeDelta kStaleFWSThreshold = base::Days(30);
 // Minimum time to wait before we decide to show the progress status if no
 // floating workspace templates have been downloaded yet.
 constexpr base::TimeDelta kMinTimeToWait = base::Seconds(2);
+// Time interval between progress bar update.
+constexpr base::TimeDelta kProgressTimeUpdateDelay = base::Seconds(1);
+
+FloatingWorkspaceServiceNotificationType GetNotificationTypeById(
+    const std::string& id) {
+  if (id == kNotificationForNoNetworkConnection) {
+    return FloatingWorkspaceServiceNotificationType::kNoNetworkConnection;
+  }
+  if (id == kNotificationForSyncErrorOrTimeOut) {
+    return FloatingWorkspaceServiceNotificationType::kSyncErrorOrTimeOut;
+  }
+  if (id == kNotificationForProgressStatus) {
+    return FloatingWorkspaceServiceNotificationType::kProgressStatus;
+  }
+  if (id == kSafeMode) {
+    return FloatingWorkspaceServiceNotificationType::kSafeMode;
+  }
+  return FloatingWorkspaceServiceNotificationType::kUnknown;
+}
 
 FloatingWorkspaceService::FloatingWorkspaceService(
     Profile* profile,
@@ -128,9 +157,7 @@
 
 void FloatingWorkspaceService::InitiateSigninTask() {
   if (!floating_workspace_util::IsInternetConnected()) {
-    if (should_run_restore_) {
-      FloatingWorkspaceDialog::ShowNetworkScreen();
-    }
+    SendNotification(kNotificationForNoNetworkConnection);
   } else {
     syncer::LocalDeviceInfoProviderImpl* local_device_info_provider =
         static_cast<syncer::LocalDeviceInfoProviderImpl*>(
@@ -141,10 +168,14 @@
             base::BindRepeating(
                 &FloatingWorkspaceService::OnLocalDeviceInfoProviderReady,
                 weak_pointer_factory_.GetWeakPtr()));
-    if (should_run_restore_) {
-      FloatingWorkspaceDialog::ShowDefaultScreen();
-    }
   }
+
+    base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(
+            &FloatingWorkspaceService::MaybeStartProgressBarNotification,
+            weak_pointer_factory_.GetWeakPtr()),
+        kMinTimeToWait);
 }
 
 // TODO(b/309137462): Clean up params to not need to be passed in.
@@ -263,7 +294,7 @@
     // This state indicates that we are not permitted to upload user's workspace
     // data (see the comment above syncer::UploadState::NOT_ACTIVE for details).
     // We should treat this as if the feature is fully disabled.
-    StopRestoringSession();
+    should_run_restore_ = false;
     return;
   }
   syncer::SyncService::DataTypeDownloadStatus workspace_download_status =
@@ -305,13 +336,13 @@
         // Don'h handle the error repeatedly.
         break;
       }
-      // If Sync is not expected to deliver the data, do nothing.
+      // Sync is not expected to deliver the data, let user decide.
+      // TODO: send notification to user asking if restore local.
       if (!should_run_restore_) {
         break;
       }
-      // There is nothing in particular the user can do to resolve a Sync error
-      // - show a generic error UI.
-      FloatingWorkspaceDialog::ShowErrorScreen();
+      StopProgressBarNotification();
+      HandleSyncError();
       break;
     }
   }
@@ -375,23 +406,65 @@
 }
 
 void FloatingWorkspaceService::OnNetworkStateOrSyncServiceStateChanged() {
-  // If the restore should not run, then there's no need to show any UI and it
-  // is expected to be closed elsewhere.
-  if (!should_run_restore_) {
+  if (!floating_workspace_util::IsInternetConnected() ||
+      (sync_service_ && !sync_service_->IsSyncFeatureActive())) {
+    // Only send notification if there's no notification currently or the
+    // current notification is the same one that we want to display. If the
+    // restore should not run, then there's no need to display the notification
+    // either.
+    if (should_run_restore_ &&
+        (notification_ == nullptr ||
+         notification_->id() != kNotificationForNoNetworkConnection)) {
+      StopProgressBarNotification();
+      SendNotification(kNotificationForNoNetworkConnection);
+    }
+  } else {
+    if (notification_ != nullptr &&
+        notification_->id() == kNotificationForNoNetworkConnection) {
+      MaybeCloseNotification();
+    }
+  }
+}
+void FloatingWorkspaceService::Click(
+    const std::optional<int>& button_index,
+    const std::optional<std::u16string>& reply) {
+  DCHECK(notification_);
+
+  switch (GetNotificationTypeById(notification_->id())) {
+    case FloatingWorkspaceServiceNotificationType::kUnknown:
+      // For unknown type of notification id, do nothing and run close logic.
+    case FloatingWorkspaceServiceNotificationType::kSyncErrorOrTimeOut:
+    case FloatingWorkspaceServiceNotificationType::kProgressStatus:
+    case FloatingWorkspaceServiceNotificationType::kSafeMode:
+      break;
+    case FloatingWorkspaceServiceNotificationType::kNoNetworkConnection:
+      if (button_index.has_value()) {
+        // Show network settings if the user clicks the show network settings
+        // button.
+        chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
+            profile_, chromeos::settings::mojom::kNetworkSectionPath);
+      }
+      break;
+  }
+  MaybeCloseNotification();
+}
+
+void FloatingWorkspaceService::MaybeCloseNotification() {
+  if (notification_ == nullptr) {
     return;
   }
-  if (!floating_workspace_util::IsInternetConnected()) {
-    // No need to double check if UI is already shown - the
-    // dialog is smart enough to recognize when there is no change.
-    FloatingWorkspaceDialog::ShowNetworkScreen();
-  } else if (sync_service_ && !sync_service_->IsSyncFeatureActive()) {
-    // If Sync is not active, show a generic error UI.
-    // TODO(crbug.com/411121762): write a test for this code path.
-    FloatingWorkspaceDialog::ShowErrorScreen();
-  } else {
-    // We are online and Sync is active: show the default UI state.
-    FloatingWorkspaceDialog::ShowDefaultScreen();
+  // If it's a progress bar notification and we're still waiting for chrome sync
+  // to finish downloading, don't need to close notification.
+  if (notification_->type() == message_center::NOTIFICATION_TYPE_PROGRESS &&
+      !progress_notification_id_.empty() &&
+      progress_notification_id_ == notification_->id()) {
+    return;
   }
+  auto* notification_display_service =
+      NotificationDisplayServiceFactory::GetForProfile(profile_);
+  notification_display_service->Close(NotificationHandler::Type::TRANSIENT,
+                                      notification_->id());
+  notification_ = nullptr;
 }
 
 void FloatingWorkspaceService::SuspendImminent(
@@ -421,8 +494,7 @@
   // Disable floating workspace action in safe mode.
   if (floating_workspace_util::IsSafeMode()) {
     LOG(WARNING) << "Floating workspace disabled in safe mode.";
-    // TODO(crbug.com/411121762): decide if we want to display something to the
-    // user in this case with our new startup UI.
+    SendNotification(kSafeMode);
     return;
   }
   floating_workspace_metrics_util::
@@ -501,6 +573,26 @@
   }
 }
 
+void FloatingWorkspaceService::MaybeStartProgressBarNotification() {
+  if (!should_run_restore_) {
+    return;
+  }
+  progress_timer_.Start(FROM_HERE, kProgressTimeUpdateDelay, this,
+                        &FloatingWorkspaceService::HandleProgressBarStatus);
+}
+
+void FloatingWorkspaceService::StopProgressBarNotification() {
+  progress_notification_id_ = std::string();
+  if (progress_timer_.IsRunning()) {
+    progress_timer_.Stop();
+  }
+  MaybeCloseNotification();
+}
+
+void FloatingWorkspaceService::HandleProgressBarStatus() {
+  SendNotification(kNotificationForProgressStatus);
+}
+
 bool FloatingWorkspaceService::ShouldExcludeTemplate(
     const DeskTemplate* floating_workspace_template) {
   if (!floating_workspace_template) {
@@ -584,7 +676,7 @@
 }
 
 void FloatingWorkspaceService::StopProgressBarAndRestoreFloatingWorkspace() {
-  FloatingWorkspaceDialog::Close();
+  StopProgressBarNotification();
   RestoreFloatingWorkspaceTemplate(GetLatestFloatingWorkspaceTemplate());
   StartCaptureAndUploadActiveDesk();
 }
@@ -867,6 +959,94 @@
   return std::nullopt;
 }
 
+void FloatingWorkspaceService::HandleSyncError() {
+  SendNotification(kNotificationForSyncErrorOrTimeOut);
+}
+
+void FloatingWorkspaceService::SendNotification(const std::string& id) {
+  // If there is a previous notification for floating workspace, close it.
+  MaybeCloseNotification();
+
+  message_center::RichNotificationData notification_data;
+  std::u16string title, message;
+  message_center::SystemNotificationWarningLevel warning_level;
+  const base::TimeDelta time_difference =
+      base::TimeTicks::Now() - initialization_timeticks_;
+  bool is_progress_bar = false;
+  switch (GetNotificationTypeById(id)) {
+    case FloatingWorkspaceServiceNotificationType::kNoNetworkConnection:
+      title =
+          l10n_util::GetStringUTF16(IDS_FLOATING_WORKSPACE_NO_NETWORK_TITLE);
+      message =
+          l10n_util::GetStringUTF16(IDS_FLOATING_WORKSPACE_NO_NETWORK_MESSAGE);
+      warning_level =
+          message_center::SystemNotificationWarningLevel::CRITICAL_WARNING;
+      notification_data.buttons.emplace_back(
+          l10n_util::GetStringUTF16(IDS_FLOATING_WORKSPACE_NO_NETWORK_BUTTON));
+      break;
+    case FloatingWorkspaceServiceNotificationType::kSyncErrorOrTimeOut:
+      title = l10n_util::GetStringUTF16(IDS_FLOATING_WORKSPACE_ERROR_TITLE);
+      message = l10n_util::GetStringUTF16(IDS_FLOATING_WORKSPACE_ERROR_MESSAGE);
+      warning_level =
+          message_center::SystemNotificationWarningLevel::CRITICAL_WARNING;
+      break;
+    case FloatingWorkspaceServiceNotificationType::kProgressStatus:
+      title =
+          l10n_util::GetStringUTF16(IDS_FLOATING_WORKSPACE_PROGRESS_BAR_TITLE);
+      notification_data.progress_status = l10n_util::GetStringUTF16(
+          IDS_FLOATING_WORKSPACE_PROGRESS_BAR_MESSAGE);
+      warning_level = message_center::SystemNotificationWarningLevel::NORMAL;
+      notification_data.progress = std::min(
+          100.0,
+          (time_difference * 100.0) /
+              ash::features::
+                  kFloatingWorkspaceV2MaxTimeAvailableForRestoreAfterLogin
+                      .Get());
+      is_progress_bar = true;
+      break;
+    case FloatingWorkspaceServiceNotificationType::kSafeMode:
+      title = l10n_util::GetStringUTF16(IDS_FLOATING_WORKSPACE_SAFE_MODE_TITLE);
+      message =
+          l10n_util::GetStringUTF16(IDS_FLOATING_WORKSPACE_SAFE_MODE_MESSAGE);
+      warning_level = message_center::SystemNotificationWarningLevel::WARNING;
+      break;
+    case FloatingWorkspaceServiceNotificationType::kUnknown:
+      VLOG(2) << "Unknown notification type for floating workspace, skip "
+                 "sending notification";
+      return;
+  }
+  // Update the current notification with progress status if we are still
+  // showing progress status. Otherwise, make a new notification.
+  if (is_progress_bar && notification_ != nullptr &&
+      !progress_notification_id_.empty() &&
+      notification_->id() == progress_notification_id_) {
+    notification_->set_progress(notification_data.progress);
+  } else {
+    notification_ = CreateSystemNotificationPtr(
+        is_progress_bar ? message_center::NOTIFICATION_TYPE_PROGRESS
+                        : message_center::NOTIFICATION_TYPE_SIMPLE,
+        id, title, message,
+        l10n_util::GetStringUTF16(IDS_FLOATING_WORKSPACE_DISPLAY_SOURCE),
+        GURL(),
+        message_center::NotifierId(
+            message_center::NotifierType::SYSTEM_COMPONENT, id,
+            NotificationCatalogName::kFloatingWorkspace),
+        notification_data,
+        base::MakeRefCounted<message_center::ThunkNotificationDelegate>(
+            weak_pointer_factory_.GetWeakPtr()),
+        kFloatingWorkspaceNotificationIcon, warning_level);
+    notification_->set_priority(message_center::SYSTEM_PRIORITY);
+    if (is_progress_bar) {
+      progress_notification_id_ = notification_->id();
+    }
+  }
+  auto* notification_display_service =
+      NotificationDisplayServiceFactory::GetForProfile(profile_);
+  notification_display_service->Display(NotificationHandler::Type::TRANSIENT,
+                                        *notification_,
+                                        /*metadata=*/nullptr);
+}
+
 void FloatingWorkspaceService::DoGarbageCollection(
     const DeskTemplate* exclude_template) {
   // Do not delete any floating workspace templates if we have less than 2
diff --git a/chrome/browser/ash/floating_workspace/floating_workspace_service.h b/chrome/browser/ash/floating_workspace/floating_workspace_service.h
index b54cb2e..547dace0 100644
--- a/chrome/browser/ash/floating_workspace/floating_workspace_service.h
+++ b/chrome/browser/ash/floating_workspace/floating_workspace_service.h
@@ -34,6 +34,7 @@
 #include "components/sync/service/sync_service_observer.h"
 #include "components/sync_device_info/device_info_sync_service.h"
 #include "components/sync_device_info/device_info_tracker.h"
+#include "ui/message_center/public/cpp/notification.h"
 
 class Profile;
 
@@ -45,11 +46,31 @@
 
 namespace ash {
 
+extern const char kNotificationForNoNetworkConnection[];
+extern const char kNotificationForSyncErrorOrTimeOut[];
+extern const char kNotificationForProgressStatus[];
+
+// The restore from error notification button index.
+enum class RestoreFromErrorNotificationButtonIndex {
+  kRestore = 0,
+  kCancel,
+};
+
+// The notification type for floating workspace service.
+enum class FloatingWorkspaceServiceNotificationType {
+  kUnknown = 0,
+  kNoNetworkConnection,
+  kSyncErrorOrTimeOut,
+  kProgressStatus,
+  kSafeMode
+};
+
 // A keyed service to support floating workspace. Note that a periodical
 // task `CaptureAndUploadActiveDesk` will be dispatched during service
 // initialization.
 class FloatingWorkspaceService
     : public KeyedService,
+      public message_center::NotificationObserver,
       public syncer::SyncServiceObserver,
       public apps::AppRegistryCache::Observer,
       public apps::AppRegistryCacheWrapper::Observer,
@@ -90,6 +111,10 @@
   void OnStateChanged(syncer::SyncService* sync) override;
   void OnSyncShutdown(syncer::SyncService* sync) override;
 
+  // message_center::NotificationObserver overrides:
+  void Click(const std::optional<int>& button_index,
+             const std::optional<std::u16string>& reply) override;
+
   // ash::SessionObserver overrides:
   void OnActiveUserSessionChanged(const AccountId& account_id) override;
   void OnLockStateChanged(bool locked) override;
@@ -114,6 +139,8 @@
   void OnDeviceInfoChange() override;
   void OnDeviceInfoShutdown() override;
 
+  void MaybeCloseNotification();
+
   std::vector<const ash::DeskTemplate*> GetFloatingWorkspaceTemplateEntries();
 
   // Setups the convenience pointers to the dependent services and observers.
@@ -172,6 +199,13 @@
   void StartCaptureAndUploadActiveDesk();
   void StopCaptureAndUploadActiveDesk();
 
+  // Start and stop the progress bar notification.
+  void MaybeStartProgressBarNotification();
+  void StopProgressBarNotification();
+
+  // Handles the updating of progress bar notification.
+  void HandleProgressBarStatus();
+
   // Stops the progress bar and resumes the latest floating workspace. This is
   // called when the app cache is ready and sync data is available.
   void StopProgressBarAndRestoreFloatingWorkspace();
@@ -222,6 +256,12 @@
   // an std::nullopt if there is no floating workspace uuid that is associated
   // with the current device.
   std::optional<base::Uuid> GetFloatingWorkspaceUuidForCurrentDevice();
+  // When sync passes an error status to floating workspace service,
+  // floating workspace service should send notification to user asking
+  // whether to restore the most recent FWS desk from local storage.
+  void HandleSyncError();
+
+  void SendNotification(const std::string& id);
 
   // Performs garbage collection of stale floating workspace templates. A
   // floating workspace template is considered stale if it's older than 30
@@ -242,11 +282,13 @@
   bool AreRequiredAppTypesInitialized();
 
   // Once network state or sync feature active state changes have been detected,
-  // handle the startup UI appropriately based on connection.
+  // handle the internet connectivity notification appropriately based on
+  // connection.
   void OnNetworkStateOrSyncServiceStateChanged();
 
-  // Initial task start. This includes checking the network connectivity upon
-  // login and setting the appropriate state for startup UI.
+  // Initial task start. This involves checking the network connectivity upon
+  // log in and sending a notification if no network is connected or start
+  // posting a task for waiting for sync server downloads to complete.
   void InitiateSigninTask();
 
   // Returns true if we should exclude the `floating_workspace_template` from
@@ -354,6 +396,8 @@
   // populated when we first capture a floating workspace template.
   std::optional<base::Uuid> floating_workspace_uuid_;
 
+  std::unique_ptr<message_center::Notification> notification_;
+  std::string progress_notification_id_;
 
   // The in memory cache of the floating workspace that should be restored
   // after downloading latest updates. Saved in case the user delays resuming
diff --git a/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc b/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc
index ee3c61d..07cc6aa 100644
--- a/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc
+++ b/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc
@@ -20,21 +20,19 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
-#include "base/test/run_until.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "chrome/browser/ash/floating_workspace/floating_workspace_metrics_util.h"
 #include "chrome/browser/ash/floating_workspace/floating_workspace_service_factory.h"
 #include "chrome/browser/ash/floating_workspace/floating_workspace_util.h"
 #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
+#include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/profiles/profile_keyed_service_factory.h"
 #include "chrome/browser/ui/ash/desks/desks_client.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/ash/session/session_controller_client_impl.h"
 #include "chrome/browser/ui/ash/session/test_session_controller.h"
-#include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.h"
-#include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_ui.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
@@ -67,13 +65,12 @@
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/test_helper.h"
 #include "content/public/test/browser_task_environment.h"
-#include "content/public/test/test_web_contents_factory.h"
-#include "content/public/test/test_web_ui.h"
 #include "google_apis/gaia/gaia_id.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
 #include "ui/base/user_activity/user_activity_detector.h"
+#include "ui/message_center/public/cpp/notification.h"
 
 namespace ash::floating_workspace {
 
@@ -340,6 +337,10 @@
     return scoped_feature_list_;
   }
 
+  NotificationDisplayServiceTester* display_service() {
+    return display_service_.get();
+  }
+
   syncer::TestSyncService* test_sync_service() { return &test_sync_service_; }
 
   ui::UserActivityDetector* user_activity_detector() {
@@ -366,6 +367,12 @@
     return chromeos::FakePowerManagerClient::Get();
   }
 
+  bool HasNotificationFor(const std::string& id) {
+    std::optional<message_center::Notification> notification =
+        display_service()->GetNotification(id);
+    return notification.has_value();
+  }
+
   void AddTestNetworkDevice() {
     network_handler_test_helper_->AddDefaultProfiles();
   }
@@ -430,19 +437,6 @@
     task_environment_.RunUntilIdle();
   }
 
-  bool WaitForStartupDialogToClose() {
-    return base::test::RunUntil(
-        []() { return !FloatingWorkspaceDialog::IsShown(); });
-  }
-
-  void CloseStartupDialogIfNeeded() {
-    if (!FloatingWorkspaceDialog::IsShown()) {
-      return;
-    }
-    FloatingWorkspaceDialog::Close();
-    EXPECT_TRUE(WaitForStartupDialogToClose());
-  }
-
   void SetUp() override {
     chromeos::PowerManagerClient::InitializeFake();
     cros_settings_test_helper_ =
@@ -479,6 +473,8 @@
     fake_desk_sync_service_ =
         std::make_unique<desks_storage::FakeDeskSyncService>(
             /*skip_engine_connection=*/true);
+    display_service_ =
+        std::make_unique<NotificationDisplayServiceTester>(profile_.get());
     network_handler_test_helper_ = std::make_unique<NetworkHandlerTestHelper>();
     fake_device_info_sync_service_ =
         std::make_unique<syncer::FakeDeviceInfoSyncService>(
@@ -493,19 +489,9 @@
     apps::AppRegistryCacheWrapper::Get().AddAppRegistryCache(account_id_,
                                                              cache_.get());
     mock_desks_client_ = std::make_unique<MockDesksClient>();
-
-    web_contents_factory_ = std::make_unique<content::TestWebContentsFactory>();
-    test_web_ui_ = std::make_unique<content::TestWebUI>();
-    test_web_ui_->set_web_contents(
-        web_contents_factory_->CreateWebContents(profile_));
-    auto ui = std::make_unique<FloatingWorkspaceUI>(test_web_ui_.get());
-    test_web_ui_->SetController(std::move(ui));
   }
 
   void TearDown() override {
-    CloseStartupDialogIfNeeded();
-    test_web_ui_.reset();
-    web_contents_factory_.reset();
     auto* floating_workspace_service =
         FloatingWorkspaceServiceFactory::GetForProfile(profile());
     if (floating_workspace_service) {
@@ -526,6 +512,7 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   syncer::TestSyncService test_sync_service_;
   std::unique_ptr<desks_storage::FakeDeskSyncService> fake_desk_sync_service_;
+  std::unique_ptr<NotificationDisplayServiceTester> display_service_;
   std::unique_ptr<syncer::FakeDeviceInfoSyncService>
       fake_device_info_sync_service_;
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -539,8 +526,6 @@
   user_manager::TypedScopedUserManager<user_manager::FakeUserManager>
       fake_user_manager_;
   ash::SessionTerminationManager session_termination_manager_;
-  std::unique_ptr<content::TestWebUI> test_web_ui_;
-  std::unique_ptr<content::TestWebContentsFactory> web_contents_factory_;
 
   raw_ptr<TestingProfile> profile_ = nullptr;
 };
@@ -830,7 +815,6 @@
 }
 
 TEST_F(FloatingWorkspaceServiceV2Test, NoNetworkForFloatingWorkspaceTemplate) {
-  SkipOnFirstSyncCallback();
   PopulateAppsCache();
   CleanUpTestNetworkDevices();
   CreateFloatingWorkspaceServiceForTesting(profile());
@@ -841,12 +825,10 @@
                                    fake_device_info_sync_service());
 
   task_environment().RunUntilIdle();
-  EXPECT_EQ(FloatingWorkspaceDialog::State::kNetwork,
-            FloatingWorkspaceDialog::IsShown());
+  EXPECT_TRUE(HasNotificationFor(kNotificationForNoNetworkConnection));
 }
 
 TEST_F(FloatingWorkspaceServiceV2Test, NetworkConnectedButOffline) {
-  SkipOnFirstSyncCallback();
   PopulateAppsCache();
   CleanUpTestNetworkDevices();
   // Connect to wifi, but set it to the ready state instead of online.
@@ -866,16 +848,14 @@
                                    fake_device_info_sync_service());
 
   task_environment().RunUntilIdle();
-  EXPECT_EQ(FloatingWorkspaceDialog::State::kNetwork,
-            FloatingWorkspaceDialog::IsShown());
+  EXPECT_TRUE(HasNotificationFor(kNotificationForNoNetworkConnection));
 
   // Switch wifi to online and check that Floating Workspace service
-  // detects it and switches the startup UI back to default.
+  // detects it and hide the notification.
   network_handler_test_helper()->SetServiceProperty(
       path, shill::kStateProperty, base::Value(shill::kStateOnline));
   task_environment().RunUntilIdle();
-  EXPECT_EQ(FloatingWorkspaceDialog::State::kDefault,
-            FloatingWorkspaceDialog::IsShown());
+  EXPECT_FALSE(HasNotificationFor(kNotificationForNoNetworkConnection));
 }
 
 TEST_F(FloatingWorkspaceServiceV2Test,
@@ -890,17 +870,14 @@
                                    fake_device_info_sync_service());
   task_environment().RunUntilIdle();
 
-  EXPECT_EQ(FloatingWorkspaceDialog::State::kDefault,
-            FloatingWorkspaceDialog::IsShown());
-
+  EXPECT_FALSE(HasNotificationFor(kNotificationForNoNetworkConnection));
   task_environment().FastForwardBy(
       ash::features::kFloatingWorkspaceV2MaxTimeAvailableForRestoreAfterLogin
           .Get() -
       base::Milliseconds(1));
   CleanUpTestNetworkDevices();
   task_environment().RunUntilIdle();
-  EXPECT_EQ(FloatingWorkspaceDialog::State::kNetwork,
-            FloatingWorkspaceDialog::IsShown());
+  EXPECT_TRUE(HasNotificationFor(kNotificationForNoNetworkConnection));
 }
 
 TEST_F(FloatingWorkspaceServiceV2Test, ConnectAfterNotHavingNetworkInitially) {
@@ -926,8 +903,7 @@
                                    fake_desk_sync_service(),
                                    fake_device_info_sync_service());
   task_environment().RunUntilIdle();
-  EXPECT_EQ(FloatingWorkspaceDialog::State::kNetwork,
-            FloatingWorkspaceDialog::IsShown());
+  EXPECT_TRUE(HasNotificationFor(kNotificationForNoNetworkConnection));
 
   // While offline, Sync might report that download status is up to date, while
   // upload state indicates we are not active yet. Check that we are not
@@ -951,8 +927,7 @@
   task_environment().RunUntilIdle();
   floating_workspace_service->DefaultNetworkChanged(
       NetworkHandler::Get()->network_state_handler()->DefaultNetwork());
-  EXPECT_EQ(FloatingWorkspaceDialog::State::kDefault,
-            FloatingWorkspaceDialog::IsShown());
+  EXPECT_FALSE(HasNotificationFor(kNotificationForNoNetworkConnection));
 
   // Just going online is not enough - wait for a sync cycle to complete.
   test_sync_service()->FireStateChanged();
@@ -996,12 +971,12 @@
   ASSERT_TRUE(mock_desks_client()->restored_desk_template());
   EXPECT_EQ(mock_desks_client()->restored_desk_template()->template_name(),
             base::UTF8ToUTF16(template_name));
-  // Disconnect from internet. Make sure no UI is shown since restore
+  // Disconnect from internet. Make sure no notification is sent since restore
   // happened already.
   CleanUpTestNetworkDevices();
   task_environment().RunUntilIdle();
-  EXPECT_FALSE(FloatingWorkspaceDialog::IsShown());
-  // Add network back and make sure there is still no UI.
+  EXPECT_FALSE(HasNotificationFor(kNotificationForNoNetworkConnection));
+  // Sanity check. Add network back and make sure notification is still gone.
   AddTestNetworkDevice();
   network_handler_test_helper()->ResetDevicesAndServices();
   network_handler_test_helper()->ConfigureService(
@@ -1010,11 +985,11 @@
             false})");
   floating_workspace_service->DefaultNetworkChanged(
       NetworkHandler::Get()->network_state_handler()->DefaultNetwork());
-  EXPECT_FALSE(FloatingWorkspaceDialog::IsShown());
+  EXPECT_FALSE(HasNotificationFor(kNotificationForNoNetworkConnection));
 }
 
 TEST_F(FloatingWorkspaceServiceV2Test,
-       NoNetworkUiLogicWhenSyncIsInactiveAndOnceSyncIsActiveAgain) {
+       NoNetworkNotificationLogicWhenSyncIsInactiveAndOnceSyncIsActiveAgain) {
   SkipOnFirstSyncCallback();
   PopulateAppsCache();
   CreateFloatingWorkspaceServiceForTesting(profile());
@@ -1026,12 +1001,11 @@
                                    fake_desk_sync_service(),
                                    fake_device_info_sync_service());
   test_sync_service()->FireStateChanged();
-  EXPECT_EQ(FloatingWorkspaceDialog::State::kError,
-            FloatingWorkspaceDialog::IsShown());
+  EXPECT_TRUE(HasNotificationFor(kNotificationForNoNetworkConnection));
   test_sync_service()->SetAllowedByEnterprisePolicy(true);
   ASSERT_TRUE(test_sync_service()->IsSyncFeatureEnabled());
   test_sync_service()->FireStateChanged();
-  EXPECT_TRUE(WaitForStartupDialogToClose());
+  EXPECT_FALSE(HasNotificationFor(kNotificationForNoNetworkConnection));
 }
 
 TEST_F(FloatingWorkspaceServiceV2Test, CanRecordTemplateLoadMetric) {
@@ -1414,7 +1388,8 @@
       1ul, fake_desk_sync_service()->GetDeskModel()->GetAllEntryUuids().size());
 }
 
-TEST_F(FloatingWorkspaceServiceV2Test, FloatingWorkspaceShowsStartupUi) {
+TEST_F(FloatingWorkspaceServiceV2Test,
+       FloatingWorkspaceTemplateHasProgressStatus) {
   SkipOnFirstSyncCallback();
   PopulateAppsCache();
   CreateFloatingWorkspaceServiceForTesting(profile());
@@ -1425,19 +1400,18 @@
                                    fake_device_info_sync_service());
 
   task_environment().FastForwardBy(base::Seconds(5));
-  EXPECT_EQ(FloatingWorkspaceDialog::State::kDefault,
-            FloatingWorkspaceDialog::IsShown());
+  EXPECT_TRUE(HasNotificationFor(kNotificationForProgressStatus));
 
-  // Wait for download to complete and check that the UI is gone.
+  // Wait for download to complete and check that the progress bar is gone.
   test_sync_service()->SetDownloadStatusFor(
       {syncer::DataType::WORKSPACE_DESK},
       syncer::SyncService::DataTypeDownloadStatus::kUpToDate);
   test_sync_service()->FireStateChanged();
-  EXPECT_TRUE(WaitForStartupDialogToClose());
+  EXPECT_FALSE(HasNotificationFor(kNotificationForProgressStatus));
 }
 
 TEST_F(FloatingWorkspaceServiceV2Test,
-       FloatingWorkspaceTemplateUiSwitchOnSyncError) {
+       FloatingWorkspaceTemplateProgressStatusGoneAfterSyncError) {
   SkipOnFirstSyncCallback();
   PopulateAppsCache();
   CreateFloatingWorkspaceServiceForTesting(profile());
@@ -1448,15 +1422,14 @@
                                    fake_device_info_sync_service());
 
   task_environment().FastForwardBy(base::Seconds(5));
-  EXPECT_EQ(FloatingWorkspaceDialog::State::kDefault,
-            FloatingWorkspaceDialog::IsShown());
+  EXPECT_TRUE(HasNotificationFor(kNotificationForProgressStatus));
   // Send sync error to service.
   test_sync_service()->SetDownloadStatusFor(
       {syncer::DataType::WORKSPACE_DESK},
       syncer::SyncService::DataTypeDownloadStatus::kError);
   test_sync_service()->FireStateChanged();
-  EXPECT_EQ(FloatingWorkspaceDialog::State::kError,
-            FloatingWorkspaceDialog::IsShown());
+  EXPECT_FALSE(HasNotificationFor(kNotificationForProgressStatus));
+  EXPECT_TRUE(HasNotificationFor(kNotificationForSyncErrorOrTimeOut));
 }
 
 TEST_F(FloatingWorkspaceServiceV2Test,
@@ -2084,6 +2057,8 @@
             /*skip_engine_connection=*/true);
     test_sync_service2_ = std::make_unique<syncer::TestSyncService>();
 
+    display_service2_ =
+        std::make_unique<NotificationDisplayServiceTester>(profile2_.get());
     cache2_ = std::make_unique<apps::AppRegistryCache>();
     fake_device_info_sync_service2_ =
         std::make_unique<syncer::FakeDeviceInfoSyncService>(
@@ -2107,6 +2082,7 @@
   std::unique_ptr<desks_storage::FakeDeskSyncService> fake_desk_sync_service2_;
   base::ScopedTempDir temp_dir2_;
   AccountId account_id2_;
+  std::unique_ptr<NotificationDisplayServiceTester> display_service2_;
   std::unique_ptr<apps::AppRegistryCache> cache2_;
   std::unique_ptr<syncer::FakeDeviceInfoSyncService>
       fake_device_info_sync_service2_;
diff --git a/chrome/browser/ash/system_web_apps/apps/boca_app_integration_browsertest.cc b/chrome/browser/ash/system_web_apps/apps/boca_app_integration_browsertest.cc
index 345399b..efa134f 100644
--- a/chrome/browser/ash/system_web_apps/apps/boca_app_integration_browsertest.cc
+++ b/chrome/browser/ash/system_web_apps/apps/boca_app_integration_browsertest.cc
@@ -12,6 +12,7 @@
 #include "ash/wm/window_state.h"
 #include "base/files/file_path.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/test_future.h"
 #include "chrome/browser/ash/boca/boca_manager.h"
 #include "chrome/browser/ash/boca/boca_manager_factory.h"
 #include "chrome/browser/ash/system_web_apps/apps/boca_web_app_info.h"
@@ -26,6 +27,7 @@
 #include "chromeos/ash/components/boca/boca_session_manager.h"
 #include "chromeos/ash/components/boca/proto/roster.pb.h"
 #include "chromeos/ash/components/boca/proto/session.pb.h"
+#include "chromeos/ash/components/boca/session_api/constants.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
@@ -100,6 +102,18 @@
     return &session_observer_;
   }
 
+  std::unique_ptr<::boca::Session> GetActiveSession() {
+    ::boca::SessionConfig session_config;
+    session_config.mutable_captions_config()->set_captions_enabled(true);
+    std::unique_ptr<::boca::Session> session =
+        std::make_unique<::boca::Session>();
+    session->mutable_student_group_configs()->insert(
+        {ash::boca::kMainStudentGroupName, session_config});
+    session->set_session_id("session-id");
+    session->set_session_state(::boca::Session::ACTIVE);
+    return session;
+  }
+
   ash::boca::BocaSessionManager* boca_session_manager() {
     return ash::BocaManagerFactory::GetInstance()
         ->GetForProfile(profile())
@@ -130,6 +144,18 @@
 }
 
 IN_PROC_BROWSER_TEST_P(BocaAppProviderIntegrationTest,
+                       ShouldEndSessionWhenAppClose) {
+  LaunchAndWait();
+  base::test::TestFuture<void> future;
+  boca_session_manager()->set_end_session_callback_for_testing(
+      future.GetCallback());
+  Browser* const boca_app_browser =
+      ash::FindSystemWebAppBrowser(profile(), ash::SystemWebAppType::BOCA);
+  boca_app_browser->window()->Close();
+  EXPECT_TRUE(future.Wait());
+}
+
+IN_PROC_BROWSER_TEST_P(BocaAppProviderIntegrationTest,
                        ShouldLaunchIntoFloatMode) {
   LaunchAndWait();
   auto* window =
@@ -196,6 +222,19 @@
 }
 
 IN_PROC_BROWSER_TEST_P(BocaAppConsumerIntegrationTest,
+                       ShouldNotEndSessionWhenAppClose) {
+  LaunchAndWait();
+  base::test::TestFuture<void> future;
+  boca_session_manager()->set_end_session_callback_for_testing(
+      future.GetCallback());
+  Browser* const boca_app_browser =
+      ash::FindSystemWebAppBrowser(profile(), ash::SystemWebAppType::BOCA);
+  boca_app_browser->window()->Close();
+  // Callback never executed.
+  EXPECT_TRUE(boca_session_manager()->end_session_callback_for_testing());
+}
+
+IN_PROC_BROWSER_TEST_P(BocaAppConsumerIntegrationTest,
                        ShouldShowExtensionsContainerInToolbar) {
   LaunchAndWait();
   const Browser* const boca_app_browser =
diff --git a/chrome/browser/autofill/autofill_ai_model_executor_factory.cc b/chrome/browser/autofill/autofill_ai_model_executor_factory.cc
index e2b48c90..2079e7f 100644
--- a/chrome/browser/autofill/autofill_ai_model_executor_factory.cc
+++ b/chrome/browser/autofill/autofill_ai_model_executor_factory.cc
@@ -58,8 +58,9 @@
   if (!model_cache || !optimization_guide) {
     return nullptr;
   }
-  return std::make_unique<AutofillAiModelExecutorImpl>(model_cache,
-                                                       optimization_guide);
+  return std::make_unique<AutofillAiModelExecutorImpl>(
+      model_cache, optimization_guide,
+      optimization_guide->GetModelQualityLogsUploaderService());
 }
 
 bool AutofillAiModelExecutorFactory::ServiceIsCreatedWithBrowserContext()
diff --git a/chrome/browser/chrome_browser_interface_binders_webui.cc b/chrome/browser/chrome_browser_interface_binders_webui.cc
index d12f445f0..b8cc6d1 100644
--- a/chrome/browser/chrome_browser_interface_binders_webui.cc
+++ b/chrome/browser/chrome_browser_interface_binders_webui.cc
@@ -256,7 +256,6 @@
 #include "chrome/browser/ui/webui/ash/enterprise_reporting/enterprise_reporting_ui.h"
 #include "chrome/browser/ui/webui/ash/extended_updates/extended_updates.mojom.h"
 #include "chrome/browser/ui/webui/ash/extended_updates/extended_updates_ui.h"
-#include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_ui.h"
 #include "chrome/browser/ui/webui/ash/internet/internet_config_dialog.h"
 #include "chrome/browser/ui/webui/ash/internet/internet_detail_dialog.h"
 #include "chrome/browser/ui/webui/ash/launcher_internals/launcher_internals.mojom.h"
@@ -871,7 +870,7 @@
       chromeos::network_config::mojom::CrosNetworkConfig,
       ash::InternetConfigDialogUI, ash::InternetDetailDialogUI, ash::NetworkUI,
       ash::OobeUI, ash::settings::OSSettingsUI, ash::LockScreenNetworkUI,
-      ash::FloatingWorkspaceUI, ash::ShimlessRMADialogUI>(map);
+      ash::ShimlessRMADialogUI>(map);
 
   RegisterWebUIControllerInterfaceBinder<
       chromeos::connectivity::mojom::PasspointService,
diff --git a/chrome/browser/chrome_content_browser_client_navigation_throttles.cc b/chrome/browser/chrome_content_browser_client_navigation_throttles.cc
index 682d297..a70f9f7 100644
--- a/chrome/browser/chrome_content_browser_client_navigation_throttles.cc
+++ b/chrome/browser/chrome_content_browser_client_navigation_throttles.cc
@@ -448,23 +448,19 @@
   web_app::WebUIWebAppNavigationThrottle::MaybeCreateAndAdd(registry);
 #endif  // !BUILDFLAG(IS_ANDROID)
 
-  // TODO(https://crbug.com/412524375): Needs a NavigationThrottle ctor
-  // migration follow-up of https://crrev.com/c/6510776 below.
-
 #if BUILDFLAG(SAFE_BROWSING_AVAILABLE)
   // g_browser_process->safe_browsing_service() may be null in unittests.
   safe_browsing::SafeBrowsingUIManager* ui_manager =
       g_browser_process->safe_browsing_service()
           ? g_browser_process->safe_browsing_service()->ui_manager().get()
           : nullptr;
-  registry.MaybeAddThrottle(
-      safe_browsing::SafeBrowsingNavigationThrottle::MaybeCreateThrottleFor(
-          &handle, ui_manager));
+  safe_browsing::SafeBrowsingNavigationThrottle::MaybeCreateAndAdd(registry,
+                                                                   ui_manager);
 
   if (base::FeatureList::IsEnabled(safe_browsing::kDelayedWarnings)) {
     registry.AddThrottle(
         std::make_unique<safe_browsing::DelayedWarningNavigationThrottle>(
-            &handle));
+            registry));
   }
 #endif  // BUILDFLAG(SAFE_BROWSING_AVAILABLE)
 
@@ -474,43 +470,35 @@
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
 #if BUILDFLAG(IS_CHROMEOS)
-  registry.MaybeAddThrottle(
-      chromeos::KioskSettingsNavigationThrottle::MaybeCreateThrottleFor(
-          &handle));
+  chromeos::KioskSettingsNavigationThrottle::MaybeCreateAndAdd(registry);
 
-  registry.MaybeAddThrottle(
-      ash::OnTaskLockedSessionNavigationThrottle::MaybeCreateThrottleFor(
-          &handle));
+  ash::OnTaskLockedSessionNavigationThrottle::MaybeCreateAndAdd(registry);
 #endif
 
 #if BUILDFLAG(IS_MAC)
-  registry.MaybeAddThrottle(MaybeCreateAuthSessionThrottleFor(&handle));
+  MaybeCreateAndAddAuthSessionNavigationThrottle(registry);
 #endif
 
   if (profile && profile->GetPrefs()) {
-    registry.MaybeAddThrottle(
-        security_interstitials::InsecureFormNavigationThrottle::
-            MaybeCreateNavigationThrottle(
-                &handle, std::make_unique<ChromeSecurityBlockingPageFactory>(),
-                profile->GetPrefs()));
+    security_interstitials::InsecureFormNavigationThrottle::MaybeCreateAndAdd(
+        registry, std::make_unique<ChromeSecurityBlockingPageFactory>(),
+        profile->GetPrefs());
   }
 
   if (IsErrorPageAutoReloadEnabled()) {
-    registry.MaybeAddThrottle(
-        error_page::NetErrorAutoReloader::MaybeCreateThrottleFor(&handle));
+    error_page::NetErrorAutoReloader::MaybeCreateAndAddNavigationThrottle(
+        registry);
   }
 
-  registry.MaybeAddThrottle(
-      payments::PaymentHandlerNavigationThrottle::MaybeCreateThrottleFor(
-          &handle));
+  payments::PaymentHandlerNavigationThrottle::MaybeCreateAndAdd(registry);
 
-  registry.MaybeAddThrottle(
-      prerender::NoStatePrefetchNavigationThrottle::MaybeCreateThrottleFor(
-          &handle));
+  prerender::NoStatePrefetchNavigationThrottle::MaybeCreateAndAdd(registry);
 
 #if !BUILDFLAG(IS_ANDROID)
-  registry.MaybeAddThrottle(
-      ReadAnythingSidePanelNavigationThrottle::CreateFor(&handle));
+  ReadAnythingSidePanelNavigationThrottle::CreateAndAdd(registry);
+
+  // TODO(https://crbug.com/412524375): Needs a NavigationThrottle ctor
+  // migration follow-up of https://crrev.com/c/6510776 below.
 
   if (lens::features::IsLensOverlayEnabled()) {
     if (profile) {
diff --git a/chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.cc b/chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.cc
index 71a475df..36f50c5 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.cc
@@ -79,34 +79,35 @@
 }
 
 // static
-std::unique_ptr<content::NavigationThrottle>
-KioskSettingsNavigationThrottle::MaybeCreateThrottleFor(
-    content::NavigationHandle* handle) {
+void KioskSettingsNavigationThrottle::MaybeCreateAndAdd(
+    content::NavigationThrottleRegistry& registry) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   // Kiosk check.
   if (!IsRunningInForcedAppMode()) {
-    return nullptr;
+    return;
   }
   // If the web contents were previously marked as restricted, attach a throttle
   // to it.
-  if (handle->GetWebContents()->GetUserData(kRestrictedSettingsWindowKey)) {
-    return std::make_unique<KioskSettingsNavigationThrottle>(handle);
+  content::NavigationHandle& handle = registry.GetNavigationHandle();
+  if (handle.GetWebContents()->GetUserData(kRestrictedSettingsWindowKey)) {
+    registry.AddThrottle(
+        std::make_unique<KioskSettingsNavigationThrottle>(registry));
   }
   // Otherwise, check whether the navigated to url is a settings page, and if
   // so, mark it.
-  if (IsSettingsPage(handle->GetURL().spec())) {
-    handle->GetWebContents()->SetUserData(
+  if (IsSettingsPage(handle.GetURL().spec())) {
+    handle.GetWebContents()->SetUserData(
         kRestrictedSettingsWindowKey,
         std::make_unique<content::WebContents::Data>());
-    return std::make_unique<KioskSettingsNavigationThrottle>(handle);
+    registry.AddThrottle(
+        std::make_unique<KioskSettingsNavigationThrottle>(registry));
   }
-  return nullptr;
 }
 
 KioskSettingsNavigationThrottle::KioskSettingsNavigationThrottle(
-    content::NavigationHandle* handle)
-    : content::NavigationThrottle(handle) {}
+    content::NavigationThrottleRegistry& registry)
+    : content::NavigationThrottle(registry) {}
 
 KioskSettingsNavigationThrottle::ThrottleCheckResult
 KioskSettingsNavigationThrottle::WillStartRequest() {
diff --git a/chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.h b/chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.h
index 2c2ef2f2..7067f10 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.h
+++ b/chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.h
@@ -34,9 +34,9 @@
   // Replaces the list of allowed settings plages with the provided one.
   static void SetSettingPagesForTesting(const std::vector<SettingsPage>* pages);
 
-  static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
-      content::NavigationHandle* handle);
-  explicit KioskSettingsNavigationThrottle(content::NavigationHandle* handle);
+  static void MaybeCreateAndAdd(content::NavigationThrottleRegistry& registry);
+  explicit KioskSettingsNavigationThrottle(
+      content::NavigationThrottleRegistry& registry);
 
   // content::NavigationThrottle:
   ThrottleCheckResult WillStartRequest() override;
diff --git a/chrome/browser/extensions/api/declarative/rules_registry_service_unittest.cc b/chrome/browser/extensions/api/declarative/rules_registry_service_unittest.cc
index 9a0e7e0..3c0ad73 100644
--- a/chrome/browser/extensions/api/declarative/rules_registry_service_unittest.cc
+++ b/chrome/browser/extensions/api/declarative/rules_registry_service_unittest.cc
@@ -22,6 +22,7 @@
 #include "extensions/browser/api/declarative/test_rules_registry.h"
 #include "extensions/browser/api/declarative_webrequest/webrequest_constants.h"
 #include "extensions/browser/rules_registry_ids.h"
+#include "extensions/buildflags/buildflags.h"
 #include "extensions/common/api/declarative/declarative_constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
@@ -30,6 +31,8 @@
 #include "extensions/common/features/feature_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 namespace {
 const char kExtensionId[] = "foo";
 
@@ -113,6 +116,11 @@
   base::RunLoop().RunUntilIdle();
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+// This test relies on declarativeWebRequest, which is deprecated and will not
+// be supported on desktop Android. We can't just replace it with
+// declarativeNetRequest because the test relies on the API being unavailable
+// on stable channel.
 TEST_F(RulesRegistryServiceTest, DefaultRulesRegistryRegistered) {
   struct {
     version_info::Channel channel;
@@ -157,5 +165,6 @@
         kWebViewRulesRegistryID, declarative_webrequest_constants::kOnRequest));
   }
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc b/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc
index 48ec705..d24b655 100644
--- a/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc
+++ b/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc
@@ -11,7 +11,6 @@
 #include <memory>
 
 #include "base/command_line.h"
-#include "chrome/browser/extensions/scoped_test_mv2_enabler.h"
 #include "chrome/browser/extensions/test_extension_environment.h"
 #include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/common/extensions/extension_test_util.h"
@@ -27,12 +26,19 @@
 #include "extensions/browser/extension_registrar.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/rules_registry_ids.h"
+#include "extensions/buildflags/buildflags.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/features/feature_channel.h"
 #include "extensions/common/manifest_constants.h"
 #include "extensions/common/permissions/permissions_data.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if !BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/extensions/scoped_test_mv2_enabler.h"
+#endif
+
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 using extension_test_util::LoadManifestUnchecked;
 
 namespace {
@@ -344,6 +350,9 @@
   EXPECT_TRUE(cache_delegate2->GetDeclarativeRulesStored(extension1_->id()));
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+// This test relies on declarativeWebRequest, which is deprecated and will not
+// be supported on desktop Android.
 TEST_F(RulesRegistryWithCacheTest, RulesPreservedAcrossRestart) {
   // This test makes sure that rules are restored from the rule store
   // on registry (in particular, browser) restart.
@@ -393,6 +402,7 @@
   content::RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetNumberOfRules(extension1_->id(), registry.get()));
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 TEST_F(RulesRegistryWithCacheTest, ConcurrentStoringOfRules) {
   // When an extension updates its rules, the new set of rules is stored to disk
diff --git a/chrome/browser/extensions/service_worker_tracking_browsertest.cc b/chrome/browser/extensions/service_worker_tracking_browsertest.cc
index 44a950c0..27f37c2 100644
--- a/chrome/browser/extensions/service_worker_tracking_browsertest.cc
+++ b/chrome/browser/extensions/service_worker_tracking_browsertest.cc
@@ -826,8 +826,13 @@
       base::ScopedAllowBlockingForTesting allow_blocking;
       base::CreateDirectory(test_extension_dir()->UnpackedPath().Append(
           FILE_PATH_LITERAL("subscope")));
+      // NOTE: `setInterval` is used to keep the service worker alive
+      // for the duration of the test, preventing it from being stopped
+      // prematurely, which could lead to test flakiness.
+      // See crbug.com/417430921.
       test_extension_dir()->WriteFile(FILE_PATH_LITERAL("subscope/sw.js"), R"(
           console.log("subscope service worker");
+          setInterval(() => { console.log("keepalive"); }, 1000);
       )");
     }
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 8c725cfd..b10e17a9 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -7137,13 +7137,13 @@
   },
   {
     "name": "omnibox-search-client-prefetch",
-    "owners": ["ryansturm@chromium.org", "chrome-brapp-loading@google.com"],
-    "expiry_milestone": 120
+    "owners": ["nhiroki@chromium.org", "chrome-prerendering@google.com"],
+    "expiry_milestone": 145
   },
   {
     "name": "omnibox-search-prefetch",
-    "owners": ["ryansturm@chromium.org", "chrome-brapp-loading@google.com"],
-    "expiry_milestone": 120
+    "owners": ["nhiroki@chromium.org", "chrome-prerendering@google.com"],
+    "expiry_milestone": 145
   },
   {
     "name": "omnibox-shortcut-boost",
@@ -9173,6 +9173,11 @@
     "expiry_milestone": 150
   },
   {
+    "name": "tab-strip-density-change-android",
+    "owners": [ "zheliooo@google.com", "nemco@google.com", "skavuluru@google.com", "clank-large-form-factors@google.com"],
+    "expiry_milestone": 150
+  },
+  {
     "name": "tab-strip-group-drag-drop-android",
     "owners": [ "zheliooo@google.com", "nemco@google.com", "skavuluru@google.com", "clank-large-form-factors@google.com"],
     "expiry_milestone": 145
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 701f3df5..8c892a85 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -5188,6 +5188,12 @@
 const char kTabStripContextMenuAndroidDescription[] =
     "Enables context menus upon long-pressing on a tab on the tab strip.";
 
+const char kTabStripDensityChangeAndroidName[] = "Tab Strip Density Change";
+const char kTabStripDensityChangeAndroidDescription[] =
+    "Enables tab UI to switch to a denser layout when a peripheral(keyboard, "
+    "mouse, touchpad, etc.) is connected, including reducing minimum tab "
+    "width and button touch target to better support click-first interactions.";
+
 const char kTabStripGroupDragDropAndroidName[] =
     "Tab Strip Group Drag Drop Android";
 const char kTabStripGroupDragDropAndroidDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index f8eda5f..ed5a062 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -3059,6 +3059,9 @@
 extern const char kTabStripContextMenuAndroidName[];
 extern const char kTabStripContextMenuAndroidDescription[];
 
+extern const char kTabStripDensityChangeAndroidName[];
+extern const char kTabStripDensityChangeAndroidDescription[];
+
 extern const char kTabStripGroupDragDropAndroidName[];
 extern const char kTabStripGroupDragDropAndroidDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 96d38c9..84f212d 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -365,6 +365,7 @@
     &kToolbarPhoneCleanup,
     &kTabStateFlatBuffer,
     &kTabStripContextMenuAndroid,
+    &kTabStripDensityChangeAndroid,
     &kTabStripGroupDragDropAndroid,
     &kTabStripGroupReorderAndroid,
     &kTabStripIncognitoMigration,
@@ -1217,6 +1218,10 @@
              "TabStripContextMenuAndroid",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kTabStripDensityChangeAndroid,
+             "TabStripDensityChangeAndroid",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kTabStripGroupDragDropAndroid,
              "TabStripGroupDragDropAndroid",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index 2476e9b..1aea728 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -211,6 +211,7 @@
 BASE_DECLARE_FEATURE(kToolbarPhoneCleanup);
 BASE_DECLARE_FEATURE(kTabStateFlatBuffer);
 BASE_DECLARE_FEATURE(kTabStripContextMenuAndroid);
+BASE_DECLARE_FEATURE(kTabStripDensityChangeAndroid);
 BASE_DECLARE_FEATURE(kTabStripGroupDragDropAndroid);
 BASE_DECLARE_FEATURE(kTabStripGroupReorderAndroid);
 BASE_DECLARE_FEATURE(kTabStripIncognitoMigration);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index c3cb7a81..af883c7 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -577,6 +577,7 @@
     public static final String TAB_RESUMPTION_MODULE_ANDROID = "TabResumptionModuleAndroid";
     public static final String TAB_STATE_FLAT_BUFFER = "TabStateFlatBuffer";
     public static final String TAB_STRIP_CONTEXT_MENU = "TabStripContextMenuAndroid";
+    public static final String TAB_STRIP_DENSITY_CHANGE_ANDROID = "TabStripDensityChangeAndroid";
     public static final String TAB_STRIP_GROUP_DRAG_DROP_ANDROID = "TabStripGroupDragDropAndroid";
     public static final String TAB_STRIP_GROUP_REORDER = "TabStripGroupReorderAndroid";
     public static final String TAB_STRIP_INCOGNITO_MIGRATION = "TabStripIncognitoMigration";
@@ -850,6 +851,8 @@
                     TAB_STATE_FLAT_BUFFER,
                     /* defaultValue= */ true,
                     /* defaultValueInTests= */ true);
+    public static final CachedFlag sTabStripDensityChangeAndroid =
+            newCachedFlag(TAB_STRIP_DENSITY_CHANGE_ANDROID, false);
     public static final CachedFlag sTabStripIncognitoMigration =
             newCachedFlag(
                     TAB_STRIP_INCOGNITO_MIGRATION,
@@ -982,6 +985,7 @@
                     sTabClosureMethodRefactor,
                     sTabletTabStripAnimation,
                     sTabStateFlatBuffer,
+                    sTabStripDensityChangeAndroid,
                     sTabStripIncognitoMigration,
                     sTabStripLayoutOptimization,
                     sTabWindowManagerReportIndicesMismatch,
diff --git a/chrome/browser/headless/headless_mode_browsertest.cc b/chrome/browser/headless/headless_mode_browsertest.cc
index 248baa5..ef8f810 100644
--- a/chrome/browser/headless/headless_mode_browsertest.cc
+++ b/chrome/browser/headless/headless_mode_browsertest.cc
@@ -130,23 +130,6 @@
   }
 }
 
-void HeadlessModeBrowserTestWithWindowSize::SetUpCommandLine(
-    base::CommandLine* command_line) {
-  HeadlessModeBrowserTest::SetUpCommandLine(command_line);
-  command_line->AppendSwitchASCII(
-      ::switches::kWindowSize,
-      base::StringPrintf("%u,%u", kWindowSize.width(), kWindowSize.height()));
-}
-
-void HeadlessModeBrowserTestWithWindowSizeAndScale::SetUpCommandLine(
-    base::CommandLine* command_line) {
-  HeadlessModeBrowserTest::SetUpCommandLine(command_line);
-  command_line->AppendSwitchASCII(
-      ::switches::kWindowSize,
-      base::StringPrintf("%u,%u", kWindowSize.width(), kWindowSize.height()));
-  command_line->AppendSwitchASCII(::switches::kForceDeviceScaleFactor, "1.5");
-}
-
 namespace {
 
 // Miscellaneous tests -------------------------------------------------------
diff --git a/chrome/browser/headless/headless_mode_browsertest.h b/chrome/browser/headless/headless_mode_browsertest.h
index 9b7e921..ae7f927 100644
--- a/chrome/browser/headless/headless_mode_browsertest.h
+++ b/chrome/browser/headless/headless_mode_browsertest.h
@@ -65,27 +65,6 @@
 // Toggles browser fullscreen mode synchronously.
 void ToggleFullscreenModeSync(Browser* browser);
 
-class HeadlessModeBrowserTestWithWindowSize : public HeadlessModeBrowserTest {
- public:
-  static constexpr gfx::Size kWindowSize = {4096, 2160};
-
-  HeadlessModeBrowserTestWithWindowSize() = default;
-  ~HeadlessModeBrowserTestWithWindowSize() override = default;
-
-  void SetUpCommandLine(base::CommandLine* command_line) override;
-};
-
-class HeadlessModeBrowserTestWithWindowSizeAndScale
-    : public HeadlessModeBrowserTest {
- public:
-  static constexpr gfx::Size kWindowSize = {800, 600};
-
-  HeadlessModeBrowserTestWithWindowSizeAndScale() = default;
-  ~HeadlessModeBrowserTestWithWindowSizeAndScale() override = default;
-
-  void SetUpCommandLine(base::CommandLine* command_line) override;
-};
-
 }  // namespace headless
 
 #endif  // CHROME_BROWSER_HEADLESS_HEADLESS_MODE_BROWSERTEST_H_
diff --git a/chrome/browser/headless/headless_mode_browsertest_linux.cc b/chrome/browser/headless/headless_mode_browsertest_linux.cc
index b23a6b5..24ddacc 100644
--- a/chrome/browser/headless/headless_mode_browsertest_linux.cc
+++ b/chrome/browser/headless/headless_mode_browsertest_linux.cc
@@ -90,44 +90,6 @@
   EXPECT_TRUE(browser()->window()->IsVisible());
 }
 
-IN_PROC_BROWSER_TEST_F(HeadlessModeBrowserTestWithWindowSize, LargeWindowSize) {
-  ui::PlatformWindow* platform_window = GetPlatformWindow(browser());
-
-  // Expect the platform window size to be the same as the requested window
-  // size because Ozone/headless is not clamping the platform window dimensions
-  // to the screen size which in this case is default 800x600 pixels.
-  gfx::Rect pixel_bounds = platform_window->GetBoundsInPixels();
-  EXPECT_EQ(pixel_bounds.width(), kWindowSize.width());
-  EXPECT_EQ(pixel_bounds.height(), kWindowSize.height());
-
-  // Expect the reported browser window size to be the same as the requested
-  // window size.
-  gfx::Rect bounds = browser()->window()->GetBounds();
-  EXPECT_EQ(bounds.size(), kWindowSize);
-}
-
-IN_PROC_BROWSER_TEST_F(HeadlessModeBrowserTestWithWindowSizeAndScale,
-                       WindowSizeWithScale) {
-  ui::PlatformWindow* platform_window = GetPlatformWindow(browser());
-
-  // Expect the platform window size in pixels to be larger than the requested
-  // window size due to scaling.
-  gfx::Rect pixel_bounds = platform_window->GetBoundsInPixels();
-  EXPECT_GT(pixel_bounds.width(), kWindowSize.width());
-  EXPECT_GT(pixel_bounds.height(), kWindowSize.height());
-
-  // Expect the platform window size in DIPs to be the same as the requested
-  // window size despite the scaling.
-  gfx::Rect dip_bounds = platform_window->GetBoundsInDIP();
-  EXPECT_EQ(dip_bounds.width(), kWindowSize.width());
-  EXPECT_EQ(dip_bounds.height(), kWindowSize.height());
-
-  // Expect the reported browser window size to be the same as the requested
-  // window size despite the scaling.
-  gfx::Rect bounds = browser()->window()->GetBounds();
-  EXPECT_EQ(bounds.size(), kWindowSize);
-}
-
 }  // namespace
 
 }  // namespace headless
diff --git a/chrome/browser/headless/headless_mode_browsertest_mac.mm b/chrome/browser/headless/headless_mode_browsertest_mac.mm
index 0ee443c..ca65e2e 100644
--- a/chrome/browser/headless/headless_mode_browsertest_mac.mm
+++ b/chrome/browser/headless/headless_mode_browsertest_mac.mm
@@ -137,40 +137,6 @@
   EXPECT_FALSE(ns_window.visible);
 }
 
-IN_PROC_BROWSER_TEST_F(HeadlessModeBrowserTestWithWindowSize, LargeWindowSize) {
-  gfx::NativeWindow native_window = browser()->window()->GetNativeWindow();
-  NSWindow* ns_window = native_window.GetNativeNSWindow();
-
-  // Expect the platform window size to be the same as the requested window
-  // size.
-  NSRect frame_rect = [ns_window frame];
-  EXPECT_EQ(NSWidth(frame_rect), kWindowSize.width());
-  EXPECT_EQ(NSHeight(frame_rect), kWindowSize.height());
-
-  // Expect the reported browser window size to be the same as the requested
-  // window size.
-  gfx::Rect bounds = browser()->window()->GetBounds();
-  EXPECT_EQ(bounds.size(), kWindowSize);
-}
-
-IN_PROC_BROWSER_TEST_F(HeadlessModeBrowserTestWithWindowSizeAndScale,
-                       WindowSizeWithScale) {
-  gfx::NativeWindow native_window = browser()->window()->GetNativeWindow();
-  NSWindow* ns_window = native_window.GetNativeNSWindow();
-
-  // Expect the platform window size to be the same as the requested window size
-  // due to scaling because Mac does not appear to support device scaling at
-  // this time.
-  NSRect frame_rect = [ns_window frame];
-  EXPECT_EQ(NSWidth(frame_rect), kWindowSize.width());
-  EXPECT_EQ(NSHeight(frame_rect), kWindowSize.height());
-
-  // Expect the reported browser window size to be the same as the requested
-  // window size.
-  gfx::Rect bounds = browser()->window()->GetBounds();
-  EXPECT_EQ(bounds.size(), kWindowSize);
-}
-
 }  // namespace
 
 }  // namespace headless
diff --git a/chrome/browser/headless/headless_mode_browsertest_win.cc b/chrome/browser/headless/headless_mode_browsertest_win.cc
index 6406b75..fd67384c 100644
--- a/chrome/browser/headless/headless_mode_browsertest_win.cc
+++ b/chrome/browser/headless/headless_mode_browsertest_win.cc
@@ -188,46 +188,6 @@
   EXPECT_FALSE(::IsWindowVisible(desktop_window_hwnd));
 }
 
-IN_PROC_BROWSER_TEST_F(HeadlessModeBrowserTestWithWindowSize, LargeWindowSize) {
-  DesktopWindowTreeHostWinWrapper* desktop_window_tree_host =
-      static_cast<DesktopWindowTreeHostWinWrapper*>(
-          browser()->window()->GetNativeWindow()->GetHost());
-  HWND desktop_window_hwnd = desktop_window_tree_host->GetHWND();
-
-  // Expect the platform window size to be smaller than the requested window
-  // size due to Windows clamping the window dimensions to the monitor work
-  // area.
-  RECT platform_window_rect;
-  CHECK(::GetWindowRect(desktop_window_hwnd, &platform_window_rect));
-  EXPECT_LT(gfx::Rect(platform_window_rect).width(), kWindowSize.width());
-  EXPECT_LT(gfx::Rect(platform_window_rect).height(), kWindowSize.height());
-
-  // Expect the reported browser window size to be the same as the requested
-  // window size.
-  gfx::Rect bounds = browser()->window()->GetBounds();
-  EXPECT_EQ(bounds.size(), kWindowSize);
-}
-
-IN_PROC_BROWSER_TEST_F(HeadlessModeBrowserTestWithWindowSizeAndScale,
-                       WindowSizeWithScale) {
-  DesktopWindowTreeHostWinWrapper* desktop_window_tree_host =
-      static_cast<DesktopWindowTreeHostWinWrapper*>(
-          browser()->window()->GetNativeWindow()->GetHost());
-  HWND desktop_window_hwnd = desktop_window_tree_host->GetHWND();
-
-  // Expect the platform window size to be larger than the requested window size
-  // due to scaling.
-  RECT platform_window_rect;
-  CHECK(::GetWindowRect(desktop_window_hwnd, &platform_window_rect));
-  EXPECT_GT(gfx::Rect(platform_window_rect).width(), kWindowSize.width());
-  EXPECT_GT(gfx::Rect(platform_window_rect).height(), kWindowSize.height());
-
-  // Expect the reported browser window size to be the same as the requested
-  // window size.
-  gfx::Rect bounds = browser()->window()->GetBounds();
-  EXPECT_EQ(bounds.size(), kWindowSize);
-}
-
 // display::win::ScreenWinHeadless tests -------------------------------------
 
 class HeadlessModeBrowserTestWithScreenInfo : public HeadlessModeBrowserTest {
diff --git a/chrome/browser/mac/auth_session_request.h b/chrome/browser/mac/auth_session_request.h
index b729442..1b7f61b 100644
--- a/chrome/browser/mac/auth_session_request.h
+++ b/chrome/browser/mac/auth_session_request.h
@@ -39,8 +39,8 @@
   static std::optional<std::string> CanonicalizeScheme(std::string scheme);
 
   // Create a throttle for the ongoing authentication session.
-  std::unique_ptr<content::NavigationThrottle> CreateThrottle(
-      content::NavigationHandle* handle);
+  void CreateAndAddNavigationThrottle(
+      content::NavigationThrottleRegistry& registry);
 
  private:
   friend class content::WebContentsUserData<AuthSessionRequest>;
@@ -100,7 +100,7 @@
 
 // If there is an authentication session in progress for the given navigation
 // handle, install a throttle.
-std::unique_ptr<content::NavigationThrottle> MaybeCreateAuthSessionThrottleFor(
-    content::NavigationHandle* handle);
+void MaybeCreateAndAddAuthSessionNavigationThrottle(
+    content::NavigationThrottleRegistry& registry);
 
 #endif  // CHROME_BROWSER_MAC_AUTH_SESSION_REQUEST_H_
diff --git a/chrome/browser/mac/auth_session_request.mm b/chrome/browser/mac/auth_session_request.mm
index 7cb84e87..1ef98fc 100644
--- a/chrome/browser/mac/auth_session_request.mm
+++ b/chrome/browser/mac/auth_session_request.mm
@@ -40,10 +40,10 @@
  public:
   using SchemeURLFoundCallback = base::OnceCallback<void(const GURL&)>;
 
-  AuthNavigationThrottle(content::NavigationHandle* handle,
+  AuthNavigationThrottle(content::NavigationThrottleRegistry& registry,
                          const std::string& scheme,
                          SchemeURLFoundCallback scheme_found)
-      : content::NavigationThrottle(handle),
+      : content::NavigationThrottle(registry),
         scheme_(scheme),
         scheme_found_(std::move(scheme_found)) {
     DCHECK(!scheme_found_.is_null());
@@ -65,14 +65,16 @@
     }
 
     GURL url = navigation_handle()->GetURL();
-    if (!url.SchemeIs(scheme_))
+    if (!url.SchemeIs(scheme_)) {
       return PROCEED;
+    }
 
     // Paranoia; if the callback was already fired, ignore all further
     // navigations that somehow get through before the WebContents deletion
     // happens.
-    if (scheme_found_.is_null())
+    if (scheme_found_.is_null()) {
       return CANCEL_AND_IGNORE;
+    }
 
     // Post the callback; triggering the deletion of the WebContents that owns
     // the navigation that is in the middle of being throttled would likely not
@@ -96,8 +98,9 @@
   std::string uuid = base::SysNSStringToUTF8(request_.UUID.UUIDString);
 
   auto iter = GetMap().find(uuid);
-  if (iter == GetMap().end())
+  if (iter == GetMap().end()) {
     return;
+  }
 
   GetMap().erase(iter);
 }
@@ -166,8 +169,9 @@
   std::string uuid = base::SysNSStringToUTF8(request.UUID.UUIDString);
 
   auto iter = GetMap().find(uuid);
-  if (iter == GetMap().end())
+  if (iter == GetMap().end()) {
     return;
+  }
 
   iter->second->CancelAuthSession();
 }
@@ -178,21 +182,22 @@
   url::RawCanonOutputT<char> canon_output;
   url::Component component;
   bool result = url::CanonicalizeScheme(scheme, &canon_output, &component);
-  if (!result)
+  if (!result) {
     return std::nullopt;
+  }
 
   return std::string(canon_output.data() + component.begin, component.len);
 }
 
-std::unique_ptr<content::NavigationThrottle> AuthSessionRequest::CreateThrottle(
-    content::NavigationHandle* handle) {
+void AuthSessionRequest::CreateAndAddNavigationThrottle(
+    content::NavigationThrottleRegistry& registry) {
   // Only attach a throttle to outermost main frames. Note non-primary main
   // frames will cancel the navigation in the throttle.
-  switch (handle->GetNavigatingFrameType()) {
+  switch (registry.GetNavigationHandle().GetNavigatingFrameType()) {
     case content::FrameType::kSubframe:
     case content::FrameType::kFencedFrameRoot:
     case content::FrameType::kGuestMainFrame:
-      return nil;
+      return;
     case content::FrameType::kPrimaryMainFrame:
     case content::FrameType::kPrerenderMainFrame:
       break;
@@ -201,8 +206,8 @@
   auto scheme_found = base::BindOnce(&AuthSessionRequest::SchemeWasNavigatedTo,
                                      weak_factory_.GetWeakPtr());
 
-  return std::make_unique<AuthNavigationThrottle>(handle, scheme_,
-                                                  std::move(scheme_found));
+  registry.AddThrottle(std::make_unique<AuthNavigationThrottle>(
+      registry, scheme_, std::move(scheme_found)));
 }
 
 AuthSessionRequest::AuthSessionRequest(
@@ -223,8 +228,9 @@
 Browser* AuthSessionRequest::CreateBrowser(
     ASWebAuthenticationSessionRequest* request,
     Profile* profile) {
-  if (!profile)
+  if (!profile) {
     return nullptr;
+  }
 
   bool ephemeral_sessions_allowed_by_policy =
       IncognitoModePrefs::GetAvailability(profile->GetPrefs()) !=
@@ -242,8 +248,9 @@
       ephemeral_sessions_allowed_by_policy) {
     profile = profile->GetPrimaryOTRProfile(/*create_if_needed=*/true);
   }
-  if (!profile)
+  if (!profile) {
     return nullptr;
+  }
 
   // Note that this creates a popup-style window to do the signin. This is a
   // specific choice motivated by security concerns, and must *not* be changed
@@ -347,12 +354,13 @@
 
 WEB_CONTENTS_USER_DATA_KEY_IMPL(AuthSessionRequest);
 
-std::unique_ptr<content::NavigationThrottle> MaybeCreateAuthSessionThrottleFor(
-    content::NavigationHandle* handle) {
-  AuthSessionRequest* request =
-      AuthSessionRequest::FromWebContents(handle->GetWebContents());
-  if (!request)
-    return nullptr;
+void MaybeCreateAndAddAuthSessionNavigationThrottle(
+    content::NavigationThrottleRegistry& registry) {
+  AuthSessionRequest* request = AuthSessionRequest::FromWebContents(
+      registry.GetNavigationHandle().GetWebContents());
+  if (!request) {
+    return;
+  }
 
-  return request->CreateThrottle(handle);
+  return request->CreateAndAddNavigationThrottle(registry);
 }
diff --git a/chrome/browser/performance_manager/metrics/metrics_provider_desktop.cc b/chrome/browser/performance_manager/metrics/metrics_provider_desktop.cc
index 3335d91..5c128365 100644
--- a/chrome/browser/performance_manager/metrics/metrics_provider_desktop.cc
+++ b/chrome/browser/performance_manager/metrics/metrics_provider_desktop.cc
@@ -22,6 +22,10 @@
 #include "ui/accessibility/ax_mode.h"
 #include "ui/accessibility/platform/ax_platform_node.h"
 
+#if BUILDFLAG(IS_WIN)
+#include "base/win/registry.h"
+#endif
+
 using performance_manager::user_tuning::prefs::kMemorySaverModeState;
 using performance_manager::user_tuning::prefs::MemorySaverModeState;
 
@@ -200,6 +204,55 @@
 }
 #endif  // SHOULD_COLLECT_CPU_FREQUENCY_METRICS()
 
+#if BUILDFLAG(IS_WIN)
+// Reports histograms describing the value of the HKEY_LOCAL_MACHINE ->
+// Software\Microsoft\Windows NT\CurrentVersion\Image File ->
+// FrontEndHeapDebugOptions registry key. We observed locally that the 0x10 bit
+// activates stack collection on heap allocation, which results in unacceptable
+// performance. We want to be sure that this isn't used widely in the field.
+void RecordFrontEndHeapDebugOptionsHistogram() {
+  // Outcome of reading the registry key. These values are persisted to logs.
+  // Entries should not be renumbered and numeric values should never be reused.
+  // LINT.IfChange(FrontEndHeapDebugOptionsOutcome)
+  enum class FrontEndHeapDebugOptionsOutcome {
+    kCannotOpenKey = 0,
+    kCannotReadValue = 1,
+    kSuccess = 2,
+    kMaxValue = kSuccess,
+  };
+  // LINT.ThenChange(//tools/metrics/histograms/metadata/performance_manager/enums.xml:FrontEndHeapDebugOptionsOutcome)
+
+  std::optional<FrontEndHeapDebugOptionsOutcome> outcome;
+  base::win::RegKey key;
+  if (key.Open(HKEY_LOCAL_MACHINE,
+               L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File "
+               L"Execution Options\\chrome.exe",
+               KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS) {
+    DWORD value = 0;
+    if (key.ReadValueDW(L"FrontEndHeapDebugOptions", &value) == ERROR_SUCCESS) {
+      base::UmaHistogramSparse(
+          "PerformanceManager.RegistryStats.FrontEndHeapDebugOptionsValue",
+          // Limit the number of distinct values recorded to this histogram, as
+          // recommended by `base::UmaHistogramSparse()` documentation. The
+          // highest bit observed being set in practice is 0x10 (for stack
+          // collection on heap allocation). We set the maximum a little bit
+          // above that, to be aware if higher bits are used in the field.
+          std::clamp(base::saturated_cast<int>(value), 0, 0xff));
+      outcome = FrontEndHeapDebugOptionsOutcome::kSuccess;
+    } else {
+      outcome = FrontEndHeapDebugOptionsOutcome::kCannotReadValue;
+    }
+  } else {
+    outcome = FrontEndHeapDebugOptionsOutcome::kCannotOpenKey;
+  }
+
+  CHECK(outcome.has_value());
+  base::UmaHistogramEnumeration(
+      "PerformanceManager.RegistryStats.FrontEndHeapDebugOptionsOutcome",
+      outcome.value());
+}
+#endif  // BUILDFLAG(IS_WIN)
+
 }  // namespace
 
 // Tracks the proportion of time a specific mode was enabled during this
@@ -342,6 +395,10 @@
 
   RecordDiskMetrics();
 
+#if BUILDFLAG(IS_WIN)
+  RecordFrontEndHeapDebugOptionsHistogram();
+#endif  // BUILDFLAG(IS_WIN)
+
   // Request a disk measurement so it's ready for the next interval
   PostDiskMetricsTask();
 }
diff --git a/chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_navigation_throttle.cc b/chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_navigation_throttle.cc
index 8e48eda..78ccdf1 100644
--- a/chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_navigation_throttle.cc
+++ b/chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_navigation_throttle.cc
@@ -14,8 +14,8 @@
 namespace prerender {
 
 NoStatePrefetchNavigationThrottle::NoStatePrefetchNavigationThrottle(
-    content::NavigationHandle* navigation_handle)
-    : content::NavigationThrottle(navigation_handle) {}
+    content::NavigationThrottleRegistry& registry)
+    : content::NavigationThrottle(registry) {}
 
 NoStatePrefetchNavigationThrottle::~NoStatePrefetchNavigationThrottle() =
     default;
@@ -49,23 +49,22 @@
 }
 
 // static
-std::unique_ptr<content::NavigationThrottle>
-NoStatePrefetchNavigationThrottle::MaybeCreateThrottleFor(
-    content::NavigationHandle* navigation_handle) {
+void NoStatePrefetchNavigationThrottle::MaybeCreateAndAdd(
+    content::NavigationThrottleRegistry& registry) {
+  content::NavigationHandle& navigation_handle = registry.GetNavigationHandle();
   prerender::NoStatePrefetchManager* no_state_prefetch_manager =
       prerender::NoStatePrefetchManagerFactory::GetForBrowserContext(
-          navigation_handle->GetWebContents()->GetBrowserContext());
+          navigation_handle.GetWebContents()->GetBrowserContext());
   if (no_state_prefetch_manager) {
     auto* no_state_prefetch_contents =
         no_state_prefetch_manager->GetNoStatePrefetchContents(
-            navigation_handle->GetWebContents());
+            navigation_handle.GetWebContents());
     if (no_state_prefetch_contents && no_state_prefetch_contents->origin() ==
                                           ORIGIN_SAME_ORIGIN_SPECULATION) {
-      return std::make_unique<NoStatePrefetchNavigationThrottle>(
-          navigation_handle);
+      registry.AddThrottle(
+          std::make_unique<NoStatePrefetchNavigationThrottle>(registry));
     }
   }
-  return nullptr;
 }
 
 }  // namespace prerender
diff --git a/chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_navigation_throttle.h b/chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_navigation_throttle.h
index 54679329..c216805 100644
--- a/chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_navigation_throttle.h
+++ b/chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_navigation_throttle.h
@@ -14,7 +14,7 @@
 class NoStatePrefetchNavigationThrottle : public content::NavigationThrottle {
  public:
   explicit NoStatePrefetchNavigationThrottle(
-      content::NavigationHandle* navigation_handle);
+      content::NavigationThrottleRegistry& registry);
   ~NoStatePrefetchNavigationThrottle() override;
 
   NoStatePrefetchNavigationThrottle(const NoStatePrefetchNavigationThrottle&) =
@@ -30,8 +30,8 @@
 
   // Creates a navigation throttle when NoState Prefetch contents are limited to
   // same origin.
-  static std::unique_ptr<NavigationThrottle> MaybeCreateThrottleFor(
-      content::NavigationHandle* navigation_handle);
+  static void MaybeCreateAndAdd(
+      content::NavigationThrottleRegistry& registry);
 
  private:
   // Called by |WillRedirectRequest()| and |WillStartRequest()|. Cancels
diff --git a/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_dictionary_expand.html b/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_dictionary_expand.html
index 46637d3..1538bc1e 100644
--- a/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_dictionary_expand.html
+++ b/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_dictionary_expand.html
@@ -54,7 +54,7 @@
 <iron-collapse class="" opened="[[expanded_]]">
   <div class="cr-row continuation dict-info-row">
     <cr-input value="[[dict.name]]"
-    label="Dictionary name"
+    label="$i18n{japaneseDictionaryName}"
     type="text"
     on-change="saveName_"></cr-input>
     <div class="button-container">
@@ -96,4 +96,4 @@
         <cr-button class="action-button" on-click="deleteDictionary_">$i18n{japaneseDeleteDictionaryButton}</cr-button>
     </div>
   </cr-dialog>
-</template>
\ No newline at end of file
+</template>
diff --git a/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_dictionary_expand.ts b/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_dictionary_expand.ts
index 99268baa..f0569a5 100644
--- a/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_dictionary_expand.ts
+++ b/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_dictionary_expand.ts
@@ -13,7 +13,7 @@
 import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
 import type {BigBuffer} from 'chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-webui.js';
 import type {BigString} from 'chrome://resources/mojo/mojo/public/mojom/base/big_string.mojom-webui.js';
-import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {afterNextRender, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import type {JapaneseDictionary} from '../mojom-webui/user_data_japanese_dictionary.mojom-webui.js';
 import {JpPosType} from '../mojom-webui/user_data_japanese_dictionary.mojom-webui.js';
@@ -71,6 +71,12 @@
     this.push(
         'dict.entries',
         {key: '', value: '', pos: JpPosType.kNoPos, comment: ''});
+    afterNextRender(this, () => {
+      this.shadowRoot!
+          .querySelector<HTMLElement>(
+              'os-japanese-dictionary-entry-row:last-of-type')!.shadowRoot!
+          .querySelector<HTMLElement>('cr-input')!.focus();
+    });
   }
 
   // Renames the dictionary.
diff --git a/chrome/browser/resources/chromeos/floating_workspace/BUILD.gn b/chrome/browser/resources/chromeos/floating_workspace/BUILD.gn
index 0eafae5b..c20ebf1 100644
--- a/chrome/browser/resources/chromeos/floating_workspace/BUILD.gn
+++ b/chrome/browser/resources/chromeos/floating_workspace/BUILD.gn
@@ -13,7 +13,6 @@
   ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
 
   ts_deps = [
-    "//ash/webui/common/resources:build_ts",
     "//ash/webui/common/resources/cr_elements:build_ts",
     "//chrome/browser/resources/chromeos/login:build_ts",
     "//third_party/cros-components:cros_components_ts",
diff --git a/chrome/browser/resources/chromeos/floating_workspace/app.html b/chrome/browser/resources/chromeos/floating_workspace/app.html
index 836231e..8939c2573 100644
--- a/chrome/browser/resources/chromeos/floating_workspace/app.html
+++ b/chrome/browser/resources/chromeos/floating_workspace/app.html
@@ -5,82 +5,34 @@
 -->
 
 <style include="oobe-dialog-host-styles">
-  #defaultDialog {
+  #floatingDialog {
+    width: 100%;
+    height: 100%;
     background-color: var(--cros-sys-app_base_shaded);
   }
 
   #checkingAnimation {
     max-height: 90%;
   }
-
-  #networkErrorDialog {
-    background-color: var(--cros-sys-app_base_shaded);
-  }
-
-  #generalErrorDialog {
-    background-color: var(--cros-sys-app_base_shaded);
-  }
 </style>
 
-<oobe-adaptive-dialog id="defaultDialog"
-aria-live="polite" footer-shrinkable>
+<oobe-adaptive-dialog id="floatingDialog"
+  tabindex="0" aria-live="polite" footer-shrinkable>
   <iron-icon slot="icon" icon="oobe-32:googleg"></iron-icon>
   <h1 slot="title">
-    [[defaultDialogTitleString]]
+    [[titleString]]
   </h1>
   <paper-progress slot="progress" id="checking-progress" indeterminate>
   </paper-progress>
   <div slot="content" class="flex layout vertical center-justified center">
     <oobe-cr-lottie id="checkingAnimation"
-    animation-url="animations/checking_for_update.json">
+    animation-url="animations/checking_for_update.json" playing=true>
     </oobe-cr-lottie>
   </div>
+
   <div slot="bottom-buttons">
     <oobe-text-button id="cancelButton" on-click="onCancelButtonClick_"
     text-key="floatingWorkspaceStartupDialogButton" border>
     </oobe-text-button>
   </div>
-</oobe-adaptive-dialog>
-
-<oobe-adaptive-dialog id="networkErrorDialog"
-aria-live="polite" footer-shrinkable>
-  <iron-icon slot="icon" icon="oobe-32:wifi"></iron-icon>
-  <h1 slot="title">
-    $i18n{floatingWorkspaceNetworkDialogTitle}
-  </h1>
-  <div slot="subtitle">
-    $i18n{floatingWorkspaceNetworkDialogSubtitle}
-  </div>
-  <div slot="content" class="flex layout vertical">
-    <network-select on-network-item-selected="onNetworkItemSelected"
-    on-custom-item-selected="onCustomItemSelected"
-    custom-items="[[getNetworkCustomItems()]]">
-    </network-select>
-  </div>
-  <div slot="bottom-buttons">
-    <oobe-text-button id="cancelButton" on-click="onCancelButtonClick_"
-    text-key="floatingWorkspaceStartupDialogButton" border>
-    </oobe-text-button>
-  </div>
-</oobe-adaptive-dialog>
-
-<oobe-adaptive-dialog id="generalErrorDialog"
-aria-live="polite" footer-shrinkable>
-  <iron-icon slot="icon" icon="oobe-32:googleg"></iron-icon>
-  <h1 slot="title">
-    $i18n{floatingWorkspaceErrorDialogTitle}
-  </h1>
-  <div slot="subtitle">
-    $i18n{floatingWorkspaceErrorDialogSubtitle}
-  </div>
-  <div slot="content" class="flex layout vertical center center-justified">
-    <iron-icon icon="oobe-illos:error-illo" class="illustration-jelly">
-    </iron-icon>
-  </div>
-  <div slot="bottom-buttons">
-    <oobe-text-button id="cancelButton" on-click="onCancelButtonClick_"
-    text-key="floatingWorkspaceErrorDialogButton" class="focus-on-show"
-    border>
-    </oobe-text-button>
-  </div>
 </oobe-adaptive-dialog>
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/floating_workspace/app.ts b/chrome/browser/resources/chromeos/floating_workspace/app.ts
index 05bfdaa..17459d40 100644
--- a/chrome/browser/resources/chromeos/floating_workspace/app.ts
+++ b/chrome/browser/resources/chromeos/floating_workspace/app.ts
@@ -6,39 +6,20 @@
 import '//resources/polymer/v3_0/paper-progress/paper-progress.js';
 import '/components/oobe_cr_lottie.js';
 import '/components/oobe_icons.html.js';
-import '/components/oobe_illo_icons.html.js';
-import '/components/oobe_network_icons.html.js';
 import '/components/common_styles/oobe_dialog_host_styles.css.js';
 import '/components/dialogs/oobe_adaptive_dialog.js';
-import '/components/buttons/oobe_text_button.js';
-import 'chrome://resources/ash/common/network/network_select.js';
 
-import {NetworkList} from '//resources/ash/common/network/network_list_types.js';
-import {$} from '//resources/ash/common/util.js';
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {OobeAdaptiveDialog} from '/components/dialogs/oobe_adaptive_dialog.js';
-import {OobeCrLottie} from '/components/oobe_cr_lottie.js';
 import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
-import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
-import type {CrosNetworkConfigRemote} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
-import {CrosNetworkConfig, StartConnectResult} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
 
 import {getTemplate} from './app.html.js';
 
 const EXPECTED_LOAD_TIME_MILLISEC = 5000;
 
-interface NetworkCustomItem {
-  customItemType: NetworkList.CustomItemType;
-  customItemName: string;
-  polymerIcon: string;
-  showBeforeNetworksList: boolean;
-}
-
 export interface FloatingWorkspace {
   $: {
-    defaultDialog: OobeAdaptiveDialog,
-    networkErrorDialog: OobeAdaptiveDialog,
-    generalErrorDialog: OobeAdaptiveDialog
+    floatingDialog: OobeAdaptiveDialog,
   };
 }
 
@@ -53,17 +34,14 @@
     return getTemplate();
   }
 
-  private networkConfig: CrosNetworkConfigRemote =
-      CrosNetworkConfig.getRemote();
-
-  // Main string of the default dialog. It is expected to change after
+  // Main string of the dialog. It is changed after
   // EXPECTED_LOAD_TIME_MILLISEC of time.
-  private defaultDialogTitleString: string;
+  private titleString: string|'';
+
 
   constructor() {
     super();
-    this.defaultDialogTitleString =
-        this.i18n('floatingWorkspaceStartupDialogTitle');
+    this.titleString = this.i18n('floatingWorkspaceStartupDialogTitle');
   }
 
   override ready(): void {
@@ -80,44 +58,15 @@
     });
     this.onWindowResolutionChange_();
 
-    // This would open one of the 3 screens depending on the state
-    // provided in the message handler.
-    chrome.send('initialize');
-  }
-
-  showDefaultScreen(): void {
-    this.hideAllScreens();
-    this.$.defaultDialog.hidden = false;
-
-    this.defaultDialogTitleString =
-        this.i18n('floatingWorkspaceStartupDialogTitle');
     // Change title when floating workspace takes too long.
     setTimeout(this.onNoResponse.bind(this), EXPECTED_LOAD_TIME_MILLISEC);
-    this.$.defaultDialog.onBeforeShow();
-    this.$.defaultDialog.show();
-    this.playAnimation();
+    this.$.floatingDialog.onBeforeShow();
+    this.$.floatingDialog.show();
   }
 
-  showNetworkScreen(): void {
-    this.hideAllScreens();
-    this.$.networkErrorDialog.hidden = false;
-
-    this.$.networkErrorDialog.onBeforeShow();
-    this.$.networkErrorDialog.show();
-  }
-
-  showErrorScreen(): void {
-    this.hideAllScreens();
-    this.$.generalErrorDialog.hidden = false;
-
-    this.$.generalErrorDialog.onBeforeShow();
-    this.$.generalErrorDialog.show();
-  }
-
-  private hideAllScreens(): void {
-    this.$.networkErrorDialog.hidden = true;
-    this.$.defaultDialog.hidden = true;
-    this.$.generalErrorDialog.hidden = true;
+  private onNoResponse(): void {
+    this.titleString =
+        this.i18n('floatingWorkspaceStartupDialogLongResponseTitle');
   }
 
   private onWindowResolutionChange_(): void {
@@ -134,56 +83,9 @@
     }
   }
 
-  // Responsible for "Add WiFi" button.
-  private getNetworkCustomItems(): NetworkCustomItem[] {
-    return [{
-      customItemType: NetworkList.CustomItemType.OOBE,
-      customItemName: 'addWiFiListItemName',
-      polymerIcon: 'oobe-network-20:add-wifi',
-      showBeforeNetworksList: false,
-    }];
-  }
-
-  private onNetworkItemSelected(
-      event: CustomEvent<OncMojo.NetworkStateProperties>) {
-    const networkState = event.detail;
-    // If the network is already connected, show network details.
-    if (OncMojo.connectionStateIsConnected(networkState.connectionState)) {
-      chrome.send('showNetworkDetails', [networkState.guid]);
-      return;
-    }
-    // If the network is not connectable, show a configuration dialog.
-    if (networkState.connectable === false || networkState.errorState) {
-      chrome.send('showNetworkConfig', [networkState.guid]);
-      return;
-    }
-    // Otherwise, connect.
-    this.networkConfig.startConnect(networkState.guid).then(response => {
-      if (response.result === StartConnectResult.kSuccess) {
-        return;
-      }
-      chrome.send('showNetworkConfig', [networkState.guid]);
-    });
-  }
-  private onCustomItemSelected(event: CustomEvent<{customData: string}>) {
-    chrome.send('addNetwork', [event.detail.customData]);
-  }
-
-  private onNoResponse(): void {
-    this.defaultDialogTitleString =
-        this.i18n('floatingWorkspaceStartupDialogLongResponseTitle');
-  }
-
   private onCancelButtonClick_(): void {
     chrome.send('dialogClose', ['stopRestoringSession']);
   }
-
-  private playAnimation(): void {
-    const animation = this.shadowRoot?.querySelector('#checkingAnimation');
-    if (animation instanceof OobeCrLottie) {
-      animation.playing = true;
-    }
-  }
 }
 
 declare global {
@@ -191,12 +93,5 @@
     [FloatingWorkspace.is]: FloatingWorkspace;
   }
 }
-function initialize() {
-  // '$(id)' is an alias for 'document.getElementById(id)'. It is defined
-  // in chrome://resources/ash/common/util.js. If this function is not exposed
-  // via the global object, it would not be available to tests that inject
-  // JavaScript directly into the renderer.
-  (window as any).$ = $;
-}
+
 customElements.define(FloatingWorkspace.is, FloatingWorkspace);
-initialize();
diff --git a/chrome/browser/resources/chromeos/floating_workspace/floating_workspace.html b/chrome/browser/resources/chromeos/floating_workspace/floating_workspace.html
index ba18122..0e0874b 100644
--- a/chrome/browser/resources/chromeos/floating_workspace/floating_workspace.html
+++ b/chrome/browser/resources/chromeos/floating_workspace/floating_workspace.html
@@ -7,13 +7,11 @@
   <title></title>
   <link rel="stylesheet" href="chrome://theme/colors.css?sets=sys">
   <link rel="stylesheet" href="chrome://theme/typography.css">
-  <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
-  <link rel="stylesheet" href="chrome://resources/chromeos/colors/cros_styles.css">
   <link rel="stylesheet" href="./oobe.css">
 </head>
 
 <body class="jelly-enabled">
-  <floating-workspace id="floating-workspace-dialog"></floating-workspace>
+  <floating-workspace></floating-workspace>
   <script type="module" src="app.js"></script>
 </body>
 </html>
\ No newline at end of file
diff --git a/chrome/browser/resources/print_preview/BUILD.gn b/chrome/browser/resources/print_preview/BUILD.gn
index 15673b0..a3b8d81 100644
--- a/chrome/browser/resources/print_preview/BUILD.gn
+++ b/chrome/browser/resources/print_preview/BUILD.gn
@@ -53,7 +53,6 @@
 
   ts_files = [
     "dark_mode_mixin.ts",
-    "dark_mode_mixin_lit.ts",
     "data/cdd.ts",
     "data/coordinate2d.ts",
     "data/destination.ts",
@@ -78,12 +77,9 @@
     "ui/destination_settings.ts",
     "ui/highlight_utils.ts",
     "ui/input_mixin.ts",
-    "ui/input_mixin_lit.ts",
     "ui/plugin_proxy.ts",
     "ui/select_mixin.ts",
-    "ui/select_mixin_lit.ts",
     "ui/settings_mixin.ts",
-    "ui/settings_mixin_lit.ts",
   ]
 
   # Files that are passed as input to css_to_wrapper().
diff --git a/chrome/browser/resources/print_preview/dark_mode_mixin.ts b/chrome/browser/resources/print_preview/dark_mode_mixin.ts
index 8143ec9..8fc8d5d 100644
--- a/chrome/browser/resources/print_preview/dark_mode_mixin.ts
+++ b/chrome/browser/resources/print_preview/dark_mode_mixin.ts
@@ -1,29 +1,25 @@
-// Copyright 2019 The Chromium Authors
+// Copyright 2025 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-import type {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {dedupingMixin} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
 const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
 
 type Constructor<T> = new (...args: any[]) => T;
 
-export const DarkModeMixin = dedupingMixin(
-    <T extends Constructor<PolymerElement>>(superClass: T): T&
+export const DarkModeMixin =
+    <T extends Constructor<CrLitElement>>(superClass: T): T&
     Constructor<DarkModeMixinInterface> => {
       class DarkModeMixin extends superClass {
         static get properties() {
           return {
             /** Whether or not the OS is in dark mode. */
-            inDarkMode: {
-              type: Boolean,
-              value: prefersDark.matches,
-            },
+            inDarkMode: {type: Boolean},
           };
         }
 
         private boundOnChange_: (() => void)|null = null;
-        declare inDarkMode: boolean;
+        accessor inDarkMode: boolean = prefersDark.matches;
 
         override connectedCallback() {
           super.connectedCallback();
@@ -45,7 +41,7 @@
       }
 
       return DarkModeMixin;
-    });
+    };
 
 export function inDarkMode(): boolean {
   return prefersDark.matches;
diff --git a/chrome/browser/resources/print_preview/dark_mode_mixin_lit.ts b/chrome/browser/resources/print_preview/dark_mode_mixin_lit.ts
deleted file mode 100644
index 9abd2c2..0000000
--- a/chrome/browser/resources/print_preview/dark_mode_mixin_lit.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-import type {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
-
-const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
-
-type Constructor<T> = new (...args: any[]) => T;
-
-export const DarkModeMixinLit =
-    <T extends Constructor<CrLitElement>>(superClass: T): T&
-    Constructor<DarkModeMixinLitInterface> => {
-      class DarkModeMixinLit extends superClass {
-        static get properties() {
-          return {
-            /** Whether or not the OS is in dark mode. */
-            inDarkMode: {type: Boolean},
-          };
-        }
-
-        private boundOnChange_: (() => void)|null = null;
-        accessor inDarkMode: boolean = prefersDark.matches;
-
-        override connectedCallback() {
-          super.connectedCallback();
-          if (!this.boundOnChange_) {
-            this.boundOnChange_ = () => this.onChange_();
-          }
-          prefersDark.addListener(this.boundOnChange_);
-        }
-
-        override disconnectedCallback() {
-          super.disconnectedCallback();
-          prefersDark.removeListener(this.boundOnChange_);
-          this.boundOnChange_ = null;
-        }
-
-        private onChange_() {
-          this.inDarkMode = prefersDark.matches;
-        }
-      }
-
-      return DarkModeMixinLit;
-    };
-
-export function inDarkMode(): boolean {
-  return prefersDark.matches;
-}
-
-export interface DarkModeMixinLitInterface {
-  inDarkMode: boolean;
-}
diff --git a/chrome/browser/resources/print_preview/print_preview.ts b/chrome/browser/resources/print_preview/print_preview.ts
index 1682bffa..b9cb3aec 100644
--- a/chrome/browser/resources/print_preview/print_preview.ts
+++ b/chrome/browser/resources/print_preview/print_preview.ts
@@ -51,8 +51,7 @@
 export {PreviewAreaState, PreviewTicket, PrintPreviewPreviewAreaElement} from './ui/preview_area.js';
 export {PrintPreviewSearchBoxElement} from './ui/print_preview_search_box.js';
 export {PrintPreviewScalingSettingsElement} from './ui/scaling_settings.js';
-export {SelectMixin, SelectMixinInterface} from './ui/select_mixin.js';
-export {SelectMixinLit} from './ui/select_mixin_lit.js';
+export {SelectMixin} from './ui/select_mixin.js';
 export {SettingsMixinInterface} from './ui/settings_mixin.js';
 export {PrintPreviewSettingsSelectElement} from './ui/settings_select.js';
 export {PrintPreviewSidebarElement} from './ui/sidebar.js';
diff --git a/chrome/browser/resources/print_preview/ui/advanced_settings_dialog.ts b/chrome/browser/resources/print_preview/ui/advanced_settings_dialog.ts
index 52351beb..3f237e1 100644
--- a/chrome/browser/resources/print_preview/ui/advanced_settings_dialog.ts
+++ b/chrome/browser/resources/print_preview/ui/advanced_settings_dialog.ts
@@ -21,7 +21,7 @@
 import {getCss} from './advanced_settings_dialog.css.js';
 import {getHtml} from './advanced_settings_dialog.html.js';
 import type {PrintPreviewSearchBoxElement} from './print_preview_search_box.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SettingsMixin} from './settings_mixin.js';
 
 export interface PrintPreviewAdvancedSettingsDialogElement {
   $: {
@@ -31,7 +31,7 @@
 }
 
 const PrintPreviewAdvancedSettingsDialogElementBase =
-    I18nMixinLit(SettingsMixinLit(CrLitElement));
+    I18nMixinLit(SettingsMixin(CrLitElement));
 
 export class PrintPreviewAdvancedSettingsDialogElement extends
     PrintPreviewAdvancedSettingsDialogElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/advanced_settings_item.ts b/chrome/browser/resources/print_preview/ui/advanced_settings_item.ts
index 452b6a8..59730e5 100644
--- a/chrome/browser/resources/print_preview/ui/advanced_settings_item.ts
+++ b/chrome/browser/resources/print_preview/ui/advanced_settings_item.ts
@@ -17,10 +17,9 @@
 import {getCss} from './advanced_settings_item.css.js';
 import {getHtml} from './advanced_settings_item.html.js';
 import {updateHighlights} from './highlight_utils.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SettingsMixin} from './settings_mixin.js';
 
-const PrintPreviewAdvancedSettingsItemElementBase =
-    SettingsMixinLit(CrLitElement);
+const PrintPreviewAdvancedSettingsItemElementBase = SettingsMixin(CrLitElement);
 
 export class PrintPreviewAdvancedSettingsItemElement extends
     PrintPreviewAdvancedSettingsItemElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/app.ts b/chrome/browser/resources/print_preview/ui/app.ts
index cc19030c..d4e1e15 100644
--- a/chrome/browser/resources/print_preview/ui/app.ts
+++ b/chrome/browser/resources/print_preview/ui/app.ts
@@ -38,7 +38,7 @@
 import {DestinationState} from './destination_settings.js';
 import type {PrintPreviewPreviewAreaElement} from './preview_area.js';
 import {PreviewAreaState} from './preview_area.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SettingsMixin} from './settings_mixin.js';
 import type {PrintPreviewSidebarElement} from './sidebar.js';
 
 export interface PrintPreviewAppElement {
@@ -52,7 +52,7 @@
 }
 
 const PrintPreviewAppElementBase =
-    WebUiListenerMixinLit(SettingsMixinLit(CrLitElement));
+    WebUiListenerMixinLit(SettingsMixin(CrLitElement));
 
 export class PrintPreviewAppElement extends PrintPreviewAppElementBase {
   static get is() {
diff --git a/chrome/browser/resources/print_preview/ui/color_settings.ts b/chrome/browser/resources/print_preview/ui/color_settings.ts
index 8289908..25ee82c 100644
--- a/chrome/browser/resources/print_preview/ui/color_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/color_settings.ts
@@ -11,11 +11,11 @@
 
 import {getHtml} from './color_settings.html.js';
 import {getCss as getPrintPreviewSharedLitCss} from './print_preview_shared_lit.css.js';
-import {SelectMixinLit} from './select_mixin_lit.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SelectMixin} from './select_mixin.js';
+import {SettingsMixin} from './settings_mixin.js';
 
 const PrintPreviewColorSettingsElementBase =
-    SettingsMixinLit(SelectMixinLit(CrLitElement));
+    SettingsMixin(SelectMixin(CrLitElement));
 
 export class PrintPreviewColorSettingsElement extends
     PrintPreviewColorSettingsElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/copies_settings.ts b/chrome/browser/resources/print_preview/ui/copies_settings.ts
index 0f6344d8..e5332d1 100644
--- a/chrome/browser/resources/print_preview/ui/copies_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/copies_settings.ts
@@ -14,7 +14,7 @@
 
 import {getCss} from './copies_settings.css.js';
 import {getHtml} from './copies_settings.html.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SettingsMixin} from './settings_mixin.js';
 
 /**
  * Maximum number of copies supported by the printer if not explicitly
@@ -28,7 +28,7 @@
   };
 }
 
-const PrintPreviewCopiesSettingsElementBase = SettingsMixinLit(CrLitElement);
+const PrintPreviewCopiesSettingsElementBase = SettingsMixin(CrLitElement);
 
 export class PrintPreviewCopiesSettingsElement extends
     PrintPreviewCopiesSettingsElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/destination_select.ts b/chrome/browser/resources/print_preview/ui/destination_select.ts
index ca41538..b6a6f43 100644
--- a/chrome/browser/resources/print_preview/ui/destination_select.ts
+++ b/chrome/browser/resources/print_preview/ui/destination_select.ts
@@ -16,9 +16,9 @@
 
 import {getCss} from './destination_select.css.js';
 import {getHtml} from './destination_select.html.js';
-import {SelectMixinLit} from './select_mixin_lit.js';
+import {SelectMixin} from './select_mixin.js';
 
-const PrintPreviewDestinationSelectElementBase = SelectMixinLit(CrLitElement);
+const PrintPreviewDestinationSelectElementBase = SelectMixin(CrLitElement);
 
 export class PrintPreviewDestinationSelectElement extends
     PrintPreviewDestinationSelectElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/destination_settings.ts b/chrome/browser/resources/print_preview/ui/destination_settings.ts
index 0733a3b4..c51c525 100644
--- a/chrome/browser/resources/print_preview/ui/destination_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/destination_settings.ts
@@ -24,7 +24,7 @@
 import type {PrintPreviewDestinationSelectElement} from './destination_select.js';
 import {getHtml} from './destination_settings.html.js';
 import {getCss as getPrintPreviewSharedLitCss} from './print_preview_shared_lit.css.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SettingsMixin} from './settings_mixin.js';
 
 export enum DestinationState {
   INIT = 0,
@@ -51,7 +51,7 @@
 }
 
 const PrintPreviewDestinationSettingsElementBase =
-    I18nMixinLit(WebUiListenerMixinLit(SettingsMixinLit(CrLitElement)));
+    I18nMixinLit(WebUiListenerMixinLit(SettingsMixin(CrLitElement)));
 
 export class PrintPreviewDestinationSettingsElement extends
     PrintPreviewDestinationSettingsElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/dpi_settings.ts b/chrome/browser/resources/print_preview/ui/dpi_settings.ts
index 6d82e1a2..e663198 100644
--- a/chrome/browser/resources/print_preview/ui/dpi_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/dpi_settings.ts
@@ -14,14 +14,14 @@
 
 import {getCss} from './dpi_settings.css.js';
 import {getHtml} from './dpi_settings.html.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SettingsMixin} from './settings_mixin.js';
 
 type LabelledDpiOption = DpiOption&SelectOption;
 export interface LabelledDpiCapability {
   option: LabelledDpiOption[];
 }
 
-const PrintPreviewDpiSettingsElementBase = SettingsMixinLit(CrLitElement);
+const PrintPreviewDpiSettingsElementBase = SettingsMixin(CrLitElement);
 
 export class PrintPreviewDpiSettingsElement extends
     PrintPreviewDpiSettingsElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/duplex_settings.ts b/chrome/browser/resources/print_preview/ui/duplex_settings.ts
index cede582..5b70d2c 100644
--- a/chrome/browser/resources/print_preview/ui/duplex_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/duplex_settings.ts
@@ -18,8 +18,8 @@
 
 import {getCss} from './duplex_settings.css.js';
 import {getHtml} from './duplex_settings.html.js';
-import {SelectMixinLit} from './select_mixin_lit.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SelectMixin} from './select_mixin.js';
+import {SettingsMixin} from './settings_mixin.js';
 
 export interface PrintPreviewDuplexSettingsElement {
   $: {
@@ -28,7 +28,7 @@
 }
 
 const PrintPreviewDuplexSettingsElementBase =
-    SettingsMixinLit(SelectMixinLit(CrLitElement));
+    SettingsMixin(SelectMixin(CrLitElement));
 
 export class PrintPreviewDuplexSettingsElement extends
     PrintPreviewDuplexSettingsElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/header.ts b/chrome/browser/resources/print_preview/ui/header.ts
index 1ebe4ff5..3e1a33f 100644
--- a/chrome/browser/resources/print_preview/ui/header.ts
+++ b/chrome/browser/resources/print_preview/ui/header.ts
@@ -17,10 +17,10 @@
 
 import {getCss} from './header.css.js';
 import {getHtml} from './header.html.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SettingsMixin} from './settings_mixin.js';
 
 
-const PrintPreviewHeaderElementBase = SettingsMixinLit(CrLitElement);
+const PrintPreviewHeaderElementBase = SettingsMixin(CrLitElement);
 
 export class PrintPreviewHeaderElement extends PrintPreviewHeaderElementBase {
   static get is() {
diff --git a/chrome/browser/resources/print_preview/ui/input_mixin.ts b/chrome/browser/resources/print_preview/ui/input_mixin.ts
index b1ce33ed..37fe2f4 100644
--- a/chrome/browser/resources/print_preview/ui/input_mixin.ts
+++ b/chrome/browser/resources/print_preview/ui/input_mixin.ts
@@ -1,11 +1,10 @@
-// Copyright 2018 The Chromium Authors
+// Copyright 2025 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
-import type {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {dedupingMixin} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
 /**
  * Helper functions for an input with timeout.
@@ -19,96 +18,91 @@
 
 type Constructor<T> = new (...args: any[]) => T;
 
-export const InputMixin = dedupingMixin(
-    <T extends Constructor<PolymerElement>>(superClass: T): T&
-    Constructor<InputMixinInterface> => {
-      class InputMixin extends superClass {
-        static get properties() {
-          return {
-            lastValue_: {
-              type: String,
-              value: '',
-            },
-          };
-        }
+export const InputMixin = <T extends Constructor<CrLitElement>>(
+    superClass: T): T&Constructor<InputMixinInterface> => {
+  class InputMixin extends superClass {
+    static get properties() {
+      return {
+        lastValue_: {type: String},
+      };
+    }
 
-        declare private lastValue_: string|null;
-        /** Timeout used to delay processing of the input, in ms. */
-        private timeout_: number|null = null;
+    private accessor lastValue_: string|null = '';
+    /** Timeout used to delay processing of the input, in ms. */
+    private timeout_: number|null = null;
 
-        override connectedCallback() {
-          super.connectedCallback();
-          this.getInput().addEventListener('input', () => this.resetTimeout_());
-          this.getInput().addEventListener(
-              'keydown', (e: KeyboardEvent) => this.onKeyDown_(e));
-        }
+    override connectedCallback() {
+      super.connectedCallback();
+      this.getInput().addEventListener('input', () => this.resetTimeout_());
+      this.getInput().addEventListener(
+          'keydown', (e: KeyboardEvent) => this.onKeyDown_(e));
+    }
 
-        getInput(): HTMLInputElement {
-          assertNotReached();
-        }
+    getInput(): HTMLInputElement {
+      assertNotReached();
+    }
 
-        /**
-         * @return The delay to use for the timeout, in ms. Elements using
-         *     this behavior must set this delay as data-timeout-delay on the
-         *     input element returned by getInput().
-         */
-        private getTimeoutDelayMs_(): number {
-          const delay = parseInt(this.getInput().dataset['timeoutDelay']!, 10);
-          assert(!Number.isNaN(delay));
-          return delay;
-        }
+    /**
+     * @return The delay to use for the timeout, in ms. Elements using
+     *     this behavior must set this delay as data-timeout-delay on the
+     *     input element returned by getInput().
+     */
+    private getTimeoutDelayMs_(): number {
+      const delay = parseInt(this.getInput().dataset['timeoutDelay']!, 10);
+      assert(!Number.isNaN(delay));
+      return delay;
+    }
 
-        /**
-         * Called when a key is pressed on the input.
-         */
-        private onKeyDown_(event: KeyboardEvent) {
-          if (event.key !== 'Enter' && event.key !== 'Tab') {
-            return;
-          }
-
-          this.resetAndUpdate();
-        }
-
-        /**
-         * Called when a input event occurs on the textfield. Starts an input
-         * timeout.
-         */
-        private resetTimeout_() {
-          if (this.timeout_) {
-            clearTimeout(this.timeout_);
-          }
-          this.timeout_ =
-              setTimeout(() => this.onTimeout_(), this.getTimeoutDelayMs_());
-        }
-
-        /**
-         * Called after a timeout after user input into the textfield.
-         */
-        private onTimeout_() {
-          this.timeout_ = null;
-          const value = this.getInput().value || '';
-          if (this.lastValue_ !== value) {
-            this.lastValue_ = value;
-            this.dispatchEvent(new CustomEvent(
-                'input-change',
-                {bubbles: true, composed: true, detail: value}));
-          }
-        }
-
-        resetString() {
-          this.lastValue_ = null;
-        }
-
-        resetAndUpdate() {
-          if (this.timeout_) {
-            clearTimeout(this.timeout_);
-          }
-          this.onTimeout_();
-        }
+    /**
+     * Called when a key is pressed on the input.
+     */
+    private onKeyDown_(event: KeyboardEvent) {
+      if (event.key !== 'Enter' && event.key !== 'Tab') {
+        return;
       }
 
-      return InputMixin;
-    });
+      this.resetAndUpdate();
+    }
+
+    /**
+     * Called when a input event occurs on the textfield. Starts an input
+     * timeout.
+     */
+    private resetTimeout_() {
+      if (this.timeout_) {
+        clearTimeout(this.timeout_);
+      }
+      this.timeout_ =
+          setTimeout(() => this.onTimeout_(), this.getTimeoutDelayMs_());
+    }
+
+    /**
+     * Called after a timeout after user input into the textfield.
+     */
+    private onTimeout_() {
+      this.timeout_ = null;
+      const value = this.getInput().value || '';
+      if (this.lastValue_ !== value) {
+        this.lastValue_ = value;
+        this.dispatchEvent(new CustomEvent(
+            'input-change', {bubbles: true, composed: true, detail: value}));
+      }
+    }
+
+    resetString() {
+      this.lastValue_ = null;
+    }
+
+    resetAndUpdate() {
+      if (this.timeout_) {
+        clearTimeout(this.timeout_);
+      }
+      this.onTimeout_();
+    }
+  }
+
+  return InputMixin;
+};
 
 export interface InputMixinInterface {
   /**
diff --git a/chrome/browser/resources/print_preview/ui/input_mixin_lit.ts b/chrome/browser/resources/print_preview/ui/input_mixin_lit.ts
deleted file mode 100644
index 3daf824..0000000
--- a/chrome/browser/resources/print_preview/ui/input_mixin_lit.ts
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
-import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
-import type {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
-
-/**
- * Helper functions for an input with timeout.
- */
-
-declare global {
-  interface HTMLElementEventMap {
-    'input-change': CustomEvent<string>;
-  }
-}
-
-type Constructor<T> = new (...args: any[]) => T;
-
-export const InputMixinLit = <T extends Constructor<CrLitElement>>(
-    superClass: T): T&Constructor<InputMixinLitInterface> => {
-  class InputMixinLit extends superClass {
-    static get properties() {
-      return {
-        lastValue_: {type: String},
-      };
-    }
-
-    private accessor lastValue_: string|null = '';
-    /** Timeout used to delay processing of the input, in ms. */
-    private timeout_: number|null = null;
-
-    override connectedCallback() {
-      super.connectedCallback();
-      this.getInput().addEventListener('input', () => this.resetTimeout_());
-      this.getInput().addEventListener(
-          'keydown', (e: KeyboardEvent) => this.onKeyDown_(e));
-    }
-
-    getInput(): HTMLInputElement {
-      assertNotReached();
-    }
-
-    /**
-     * @return The delay to use for the timeout, in ms. Elements using
-     *     this behavior must set this delay as data-timeout-delay on the
-     *     input element returned by getInput().
-     */
-    private getTimeoutDelayMs_(): number {
-      const delay = parseInt(this.getInput().dataset['timeoutDelay']!, 10);
-      assert(!Number.isNaN(delay));
-      return delay;
-    }
-
-    /**
-     * Called when a key is pressed on the input.
-     */
-    private onKeyDown_(event: KeyboardEvent) {
-      if (event.key !== 'Enter' && event.key !== 'Tab') {
-        return;
-      }
-
-      this.resetAndUpdate();
-    }
-
-    /**
-     * Called when a input event occurs on the textfield. Starts an input
-     * timeout.
-     */
-    private resetTimeout_() {
-      if (this.timeout_) {
-        clearTimeout(this.timeout_);
-      }
-      this.timeout_ =
-          setTimeout(() => this.onTimeout_(), this.getTimeoutDelayMs_());
-    }
-
-    /**
-     * Called after a timeout after user input into the textfield.
-     */
-    private onTimeout_() {
-      this.timeout_ = null;
-      const value = this.getInput().value || '';
-      if (this.lastValue_ !== value) {
-        this.lastValue_ = value;
-        this.dispatchEvent(new CustomEvent(
-            'input-change', {bubbles: true, composed: true, detail: value}));
-      }
-    }
-
-    resetString() {
-      this.lastValue_ = null;
-    }
-
-    resetAndUpdate() {
-      if (this.timeout_) {
-        clearTimeout(this.timeout_);
-      }
-      this.onTimeout_();
-    }
-  }
-
-  return InputMixinLit;
-};
-
-export interface InputMixinLitInterface {
-  /**
-   * @return The cr-input or input element the behavior should use. Should be
-   *     overridden by elements using this behavior.
-   */
-  getInput(): (CrInputElement|HTMLInputElement);
-
-  // Resets the lastValue_ so that future inputs trigger a change event.
-  resetString(): void;
-
-  // Called to clear the timeout and update the value.
-  resetAndUpdate(): void;
-}
diff --git a/chrome/browser/resources/print_preview/ui/layout_settings.ts b/chrome/browser/resources/print_preview/ui/layout_settings.ts
index d28dbed..0857a19 100644
--- a/chrome/browser/resources/print_preview/ui/layout_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/layout_settings.ts
@@ -9,11 +9,11 @@
 
 import {getHtml} from './layout_settings.html.js';
 import {getCss as getPrintPreviewSharedCss} from './print_preview_shared_lit.css.js';
-import {SelectMixinLit} from './select_mixin_lit.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SelectMixin} from './select_mixin.js';
+import {SettingsMixin} from './settings_mixin.js';
 
 const PrintPreviewLayoutSettingsElementBase =
-    SettingsMixinLit(SelectMixinLit(CrLitElement));
+    SettingsMixin(SelectMixin(CrLitElement));
 
 export class PrintPreviewLayoutSettingsElement extends
     PrintPreviewLayoutSettingsElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/margin_control.ts b/chrome/browser/resources/print_preview/ui/margin_control.ts
index 92f15cc..6360793 100644
--- a/chrome/browser/resources/print_preview/ui/margin_control.ts
+++ b/chrome/browser/resources/print_preview/ui/margin_control.ts
@@ -15,7 +15,7 @@
 import type {MeasurementSystem} from '../data/measurement_system.js';
 import type {Size} from '../data/size.js';
 
-import {InputMixinLit} from './input_mixin_lit.js';
+import {InputMixin} from './input_mixin.js';
 import {getCss} from './margin_control.css.js';
 import {getHtml} from './margin_control.html.js';
 
@@ -33,7 +33,7 @@
 }
 
 const PrintPreviewMarginControlElementBase =
-    I18nMixinLit(WebUiListenerMixinLit(InputMixinLit(CrLitElement)));
+    I18nMixinLit(WebUiListenerMixinLit(InputMixin(CrLitElement)));
 
 export class PrintPreviewMarginControlElement extends
     PrintPreviewMarginControlElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/margin_control_container.ts b/chrome/browser/resources/print_preview/ui/margin_control_container.ts
index 52dff04..aee8b5f4 100644
--- a/chrome/browser/resources/print_preview/ui/margin_control_container.ts
+++ b/chrome/browser/resources/print_preview/ui/margin_control_container.ts
@@ -19,7 +19,7 @@
 import type {PrintPreviewMarginControlElement} from './margin_control.js';
 import {getCss} from './margin_control_container.css.js';
 import {getHtml} from './margin_control_container.html.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SettingsMixin} from './settings_mixin.js';
 
 export const MARGIN_KEY_MAP:
     Map<CustomMarginsOrientation, keyof MarginsSetting> = new Map([
@@ -33,7 +33,7 @@
 
 
 const PrintPreviewMarginControlContainerElementBase =
-    SettingsMixinLit(CrLitElement);
+    SettingsMixin(CrLitElement);
 
 export class PrintPreviewMarginControlContainerElement extends
     PrintPreviewMarginControlContainerElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/margins_settings.ts b/chrome/browser/resources/print_preview/ui/margins_settings.ts
index 64d44be..20facb1 100644
--- a/chrome/browser/resources/print_preview/ui/margins_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/margins_settings.ts
@@ -13,12 +13,12 @@
 
 import {getHtml} from './margins_settings.html.js';
 import {getCss as getPrintPreviewSharedCss} from './print_preview_shared_lit.css.js';
-import {SelectMixinLit} from './select_mixin_lit.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SelectMixin} from './select_mixin.js';
+import {SettingsMixin} from './settings_mixin.js';
 
 
 const PrintPreviewMarginsSettingsElementBase =
-    SettingsMixinLit(SelectMixinLit(CrLitElement));
+    SettingsMixin(SelectMixin(CrLitElement));
 
 export class PrintPreviewMarginsSettingsElement extends
     PrintPreviewMarginsSettingsElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/media_size_settings.ts b/chrome/browser/resources/print_preview/ui/media_size_settings.ts
index 7978dc0..a27280b 100644
--- a/chrome/browser/resources/print_preview/ui/media_size_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/media_size_settings.ts
@@ -12,9 +12,9 @@
 
 import {getCss} from './media_size_settings.css.js';
 import {getHtml} from './media_size_settings.html.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SettingsMixin} from './settings_mixin.js';
 
-const PrintPreviewMediaSizeSettingsElementBase = SettingsMixinLit(CrLitElement);
+const PrintPreviewMediaSizeSettingsElementBase = SettingsMixin(CrLitElement);
 
 export class PrintPreviewMediaSizeSettingsElement extends
     PrintPreviewMediaSizeSettingsElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/number_settings_section.ts b/chrome/browser/resources/print_preview/ui/number_settings_section.ts
index 0006379c..a6ac41a 100644
--- a/chrome/browser/resources/print_preview/ui/number_settings_section.ts
+++ b/chrome/browser/resources/print_preview/ui/number_settings_section.ts
@@ -10,7 +10,7 @@
 import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 import type {PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
-import {InputMixinLit} from './input_mixin_lit.js';
+import {InputMixin} from './input_mixin.js';
 import {getCss} from './number_settings_section.css.js';
 import {getHtml} from './number_settings_section.html.js';
 
@@ -21,7 +21,7 @@
 }
 
 const PrintPreviewNumberSettingsSectionElementBase =
-    WebUiListenerMixinLit(InputMixinLit(CrLitElement));
+    WebUiListenerMixinLit(InputMixin(CrLitElement));
 
 export class PrintPreviewNumberSettingsSectionElement extends
     PrintPreviewNumberSettingsSectionElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/other_options_settings.ts b/chrome/browser/resources/print_preview/ui/other_options_settings.ts
index 2b8793b59..c847b790e 100644
--- a/chrome/browser/resources/print_preview/ui/other_options_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/other_options_settings.ts
@@ -14,7 +14,7 @@
 
 import {getCss} from './other_options_settings.css.js';
 import {getHtml} from './other_options_settings.html.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SettingsMixin} from './settings_mixin.js';
 
 interface CheckboxOption {
   name: keyof Settings;
@@ -25,7 +25,7 @@
 }
 
 const PrintPreviewOtherOptionsSettingsElementBase =
-    SettingsMixinLit(I18nMixinLit(CrLitElement));
+    SettingsMixin(I18nMixinLit(CrLitElement));
 
 export class PrintPreviewOtherOptionsSettingsElement extends
     PrintPreviewOtherOptionsSettingsElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/pages_per_sheet_settings.ts b/chrome/browser/resources/print_preview/ui/pages_per_sheet_settings.ts
index 3ca7b835..e818f2d 100644
--- a/chrome/browser/resources/print_preview/ui/pages_per_sheet_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/pages_per_sheet_settings.ts
@@ -9,11 +9,11 @@
 
 import {getHtml} from './pages_per_sheet_settings.html.js';
 import {getCss as getPrintPreviewSharedCss} from './print_preview_shared_lit.css.js';
-import {SelectMixinLit} from './select_mixin_lit.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SelectMixin} from './select_mixin.js';
+import {SettingsMixin} from './settings_mixin.js';
 
 const PrintPreviewPagesPerSheetSettingsElementBase =
-    SettingsMixinLit(SelectMixinLit(CrLitElement));
+    SettingsMixin(SelectMixin(CrLitElement));
 
 export class PrintPreviewPagesPerSheetSettingsElement extends
     PrintPreviewPagesPerSheetSettingsElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/pages_settings.ts b/chrome/browser/resources/print_preview/ui/pages_settings.ts
index 92c108c..7018ef3 100644
--- a/chrome/browser/resources/print_preview/ui/pages_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/pages_settings.ts
@@ -19,11 +19,11 @@
 import type {Range} from '../print_preview_utils.js';
 import {areRangesEqual} from '../print_preview_utils.js';
 
-import {InputMixinLit} from './input_mixin_lit.js';
+import {InputMixin} from './input_mixin.js';
 import {getCss} from './pages_settings.css.js';
 import {getHtml} from './pages_settings.html.js';
-import {SelectMixinLit} from './select_mixin_lit.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SelectMixin} from './select_mixin.js';
+import {SettingsMixin} from './settings_mixin.js';
 
 enum PagesInputErrorState {
   NO_ERROR = 0,
@@ -59,8 +59,8 @@
   };
 }
 
-const PrintPreviewPagesSettingsElementBase = WebUiListenerMixinLit(
-    InputMixinLit(SettingsMixinLit(SelectMixinLit(CrLitElement))));
+const PrintPreviewPagesSettingsElementBase =
+    WebUiListenerMixinLit(InputMixin(SettingsMixin(SelectMixin(CrLitElement))));
 
 export class PrintPreviewPagesSettingsElement extends
     PrintPreviewPagesSettingsElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/preview_area.ts b/chrome/browser/resources/print_preview/ui/preview_area.ts
index 24b0b1f5..fabd4b4 100644
--- a/chrome/browser/resources/print_preview/ui/preview_area.ts
+++ b/chrome/browser/resources/print_preview/ui/preview_area.ts
@@ -10,7 +10,7 @@
 import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 import type {PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
-import {DarkModeMixinLit} from '../dark_mode_mixin_lit.js';
+import {DarkModeMixin} from '../dark_mode_mixin.js';
 import {Coordinate2d} from '../data/coordinate2d.js';
 import type {Destination} from '../data/destination.js';
 import type {Margins, MarginsSetting} from '../data/margins.js';
@@ -31,7 +31,7 @@
 import {PluginProxyImpl} from './plugin_proxy.js';
 import {getCss} from './preview_area.css.js';
 import {getHtml} from './preview_area.html.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SettingsMixin} from './settings_mixin.js';
 
 export type PreviewTicket = Ticket&{
   headerFooterEnabled: boolean,
@@ -54,7 +54,7 @@
 }
 
 const PrintPreviewPreviewAreaElementBase = WebUiListenerMixinLit(
-    I18nMixinLit(SettingsMixinLit(DarkModeMixinLit(CrLitElement))));
+    I18nMixinLit(SettingsMixin(DarkModeMixin(CrLitElement))));
 
 export class PrintPreviewPreviewAreaElement extends
     PrintPreviewPreviewAreaElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/scaling_settings.ts b/chrome/browser/resources/print_preview/ui/scaling_settings.ts
index d3a3ab77..09c6b8eb 100644
--- a/chrome/browser/resources/print_preview/ui/scaling_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/scaling_settings.ts
@@ -15,8 +15,8 @@
 
 import {getCss as getPrintPreviewSharedCss} from './print_preview_shared_lit.css.js';
 import {getHtml} from './scaling_settings.html.js';
-import {SelectMixinLit} from './select_mixin_lit.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SelectMixin} from './select_mixin.js';
+import {SettingsMixin} from './settings_mixin.js';
 
 /*
  * Fit to page and fit to paper options will only be displayed for PDF
@@ -25,7 +25,7 @@
  */
 
 const PrintPreviewScalingSettingsElementBase =
-    SettingsMixinLit(SelectMixinLit(CrLitElement));
+    SettingsMixin(SelectMixin(CrLitElement));
 
 export class PrintPreviewScalingSettingsElement extends
     PrintPreviewScalingSettingsElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/select_mixin.ts b/chrome/browser/resources/print_preview/ui/select_mixin.ts
index 5bc712a3..03367ad 100644
--- a/chrome/browser/resources/print_preview/ui/select_mixin.ts
+++ b/chrome/browser/resources/print_preview/ui/select_mixin.ts
@@ -1,8 +1,9 @@
-// Copyright 2018 The Chromium Authors
+// Copyright 2025 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-import type {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {Debouncer, dedupingMixin, timeOut} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
+
+import {Debouncer} from './debouncer.js';
 
 /**
  * Helper functions for a select with timeout. Implemented by select settings
@@ -10,7 +11,7 @@
  * freeze the dropdown when the value is changed.
  * Assumes that the elements using this mixin have no more than one <select>
  * element. Clients should:
- * (1) Single-bind `selectedValue` to the <select>'s `value` property.
+ * (1) Bind `selectedValue` to the <select>'s `value` property.
  * (2) Register `onSelectChange` as an event listener for the <select>'s
  *     `change` event.
  * (3) Override `onProcessSelectChange` to receive notifications of new values
@@ -19,8 +20,8 @@
 
 type Constructor<T> = new (...args: any[]) => T;
 
-export const SelectMixin = dedupingMixin(
-    <T extends Constructor<PolymerElement>>(superClass: T): T&
+export const SelectMixin =
+    <T extends Constructor<CrLitElement>>(superClass: T): T&
     Constructor<SelectMixinInterface> => {
       class SelectMixin extends superClass {
         static get properties() {
@@ -29,14 +30,12 @@
           };
         }
 
-        declare selectedValue: string;
-        private debouncer_: Debouncer|null = null;
+        accessor selectedValue: string;
+        private debouncer_: Debouncer = new Debouncer(100);
 
         onSelectChange(e: Event) {
           const newValue = (e.target as HTMLSelectElement).value;
-          this.debouncer_ = Debouncer.debounce(
-              this.debouncer_, timeOut.after(100),
-              () => this.callProcessSelectChange_(newValue));
+          this.debouncer_.call(() => this.callProcessSelectChange_(newValue));
         }
 
         private callProcessSelectChange_(newValue: string) {
@@ -54,10 +53,11 @@
       }
 
       return SelectMixin;
-    });
+    };
 
 export interface SelectMixinInterface {
   selectedValue: string;
+  onSelectChange(e: Event): void;
 
   /**
    * Should be overridden by elements using this mixin to receive select
diff --git a/chrome/browser/resources/print_preview/ui/select_mixin_lit.ts b/chrome/browser/resources/print_preview/ui/select_mixin_lit.ts
deleted file mode 100644
index dc3c354..0000000
--- a/chrome/browser/resources/print_preview/ui/select_mixin_lit.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-import type {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
-
-import {Debouncer} from './debouncer.js';
-
-/**
- * Helper functions for a select with timeout. Implemented by select settings
- * sections, so that the preview does not immediately begin generating and
- * freeze the dropdown when the value is changed.
- * Assumes that the elements using this mixin have no more than one <select>
- * element. Clients should:
- * (1) Bind `selectedValue` to the <select>'s `value` property.
- * (2) Register `onSelectChange` as an event listener for the <select>'s
- *     `change` event.
- * (3) Override `onProcessSelectChange` to receive notifications of new values
- *     set from the UI.
- */
-
-type Constructor<T> = new (...args: any[]) => T;
-
-export const SelectMixinLit =
-    <T extends Constructor<CrLitElement>>(superClass: T): T&
-    Constructor<SelectMixinLitInterface> => {
-      class SelectMixinLit extends superClass {
-        static get properties() {
-          return {
-            selectedValue: {type: String},
-          };
-        }
-
-        accessor selectedValue: string;
-        private debouncer_: Debouncer = new Debouncer(100);
-
-        onSelectChange(e: Event) {
-          const newValue = (e.target as HTMLSelectElement).value;
-          this.debouncer_.call(() => this.callProcessSelectChange_(newValue));
-        }
-
-        private callProcessSelectChange_(newValue: string) {
-          if (!this.isConnected || newValue === this.selectedValue) {
-            return;
-          }
-          this.selectedValue = newValue;
-          this.onProcessSelectChange(newValue);
-          // For testing only
-          this.dispatchEvent(new CustomEvent(
-              'process-select-change', {bubbles: true, composed: true}));
-        }
-
-        onProcessSelectChange(_value: string) {}
-      }
-
-      return SelectMixinLit;
-    };
-
-export interface SelectMixinLitInterface {
-  selectedValue: string;
-  onSelectChange(e: Event): void;
-
-  /**
-   * Should be overridden by elements using this mixin to receive select
-   * value updates.
-   * @param value The new select value to process.
-   */
-  onProcessSelectChange(value: string): void;
-}
diff --git a/chrome/browser/resources/print_preview/ui/settings_mixin.ts b/chrome/browser/resources/print_preview/ui/settings_mixin.ts
index 01a673f2..5e4393d 100644
--- a/chrome/browser/resources/print_preview/ui/settings_mixin.ts
+++ b/chrome/browser/resources/print_preview/ui/settings_mixin.ts
@@ -1,50 +1,77 @@
-// Copyright 2017 The Chromium Authors
+// Copyright 2025 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import type {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {dedupingMixin} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {assert} from 'chrome://resources/js/assert.js';
+import type {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
 import type {Setting, Settings} from '../data/model.js';
 import {getInstance} from '../data/model.js';
+import type {PrintPreviewModelElement} from '../data/model.js';
+import type {ChangeCallback} from '../data/observable.js';
 
 type Constructor<T> = new (...args: any[]) => T;
 
-export const SettingsMixin = dedupingMixin(
-    <T extends Constructor<PolymerElement>>(superClass: T): T&
-    Constructor<SettingsMixinInterface> => {
-      class SettingsMixin extends superClass implements SettingsMixinInterface {
-        static get properties() {
-          return {
-            settings: Object,
-          };
-        }
+export const SettingsMixin = <T extends Constructor<CrLitElement>>(
+    superClass: T): T&Constructor<SettingsMixinInterface> => {
+  class SettingsMixin extends superClass implements SettingsMixinInterface {
+    private observers_: number[] = [];
+    private model_: PrintPreviewModelElement|null = null;
 
-        declare settings: Settings;
+    override connectedCallback() {
+      super.connectedCallback();
+      // Cache this reference, so that the same one can be used in
+      // disconnectedCallback(), othehrwise if `model_` has already been removed
+      // from the DOM, getInstance() will throw an error.
+      this.model_ = getInstance();
+    }
 
-        getSetting(settingName: keyof Settings): Setting {
-          return getInstance().getSetting(settingName);
-        }
+    override disconnectedCallback() {
+      super.disconnectedCallback();
+      assert(this.model_);
 
-        getSettingValue(settingName: keyof Settings): any {
-          return getInstance().getSettingValue(settingName);
-        }
-
-        setSetting(
-            settingName: keyof Settings, value: any, noSticky?: boolean) {
-          getInstance().setSetting(settingName, value, noSticky);
-        }
-
-        setSettingValid(settingName: keyof Settings, valid: boolean) {
-          getInstance().setSettingValid(settingName, valid);
+      if (this.model_.isConnected) {
+        // Only remove observers if the PrintPreviewModelElement original
+        // singleton instance is still connected to the DOM. Otherwise all
+        // observers have already been remomved in PrintPreviewModelElement's
+        // disconnectedCallback.
+        for (const id of this.observers_) {
+          const removed = this.model_.observable.removeObserver(id);
+          assert(removed);
         }
       }
 
-      return SettingsMixin;
-    });
+      this.model_ = null;
+      this.observers_ = [];
+    }
+
+    addSettingObserver(path: string, callback: ChangeCallback) {
+      const id = getInstance().observable.addObserver(path, callback);
+      this.observers_.push(id);
+    }
+
+    getSetting(settingName: keyof Settings): Setting {
+      return getInstance().getSetting(settingName);
+    }
+
+    getSettingValue(settingName: keyof Settings): any {
+      return getInstance().getSettingValue(settingName);
+    }
+
+    setSetting(settingName: keyof Settings, value: any, noSticky?: boolean) {
+      getInstance().setSetting(settingName, value, noSticky);
+    }
+
+    setSettingValid(settingName: keyof Settings, valid: boolean) {
+      getInstance().setSettingValid(settingName, valid);
+    }
+  }
+
+  return SettingsMixin;
+};
 
 export interface SettingsMixinInterface {
-  settings: Settings;
+  addSettingObserver(path: string, callback: ChangeCallback): void;
 
   /**
    * @param settingName Name of the setting to get.
diff --git a/chrome/browser/resources/print_preview/ui/settings_mixin_lit.ts b/chrome/browser/resources/print_preview/ui/settings_mixin_lit.ts
deleted file mode 100644
index 2e3b1ef..0000000
--- a/chrome/browser/resources/print_preview/ui/settings_mixin_lit.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {assert} from 'chrome://resources/js/assert.js';
-import type {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
-
-import type {Setting, Settings} from '../data/model.js';
-import {getInstance} from '../data/model.js';
-import type {PrintPreviewModelElement} from '../data/model.js';
-import type {ChangeCallback} from '../data/observable.js';
-
-type Constructor<T> = new (...args: any[]) => T;
-
-export const SettingsMixinLit = <T extends Constructor<CrLitElement>>(
-    superClass: T): T&Constructor<SettingsMixinLitInterface> => {
-  class SettingsMixinLit extends superClass implements
-      SettingsMixinLitInterface {
-    private observers_: number[] = [];
-    private model_: PrintPreviewModelElement|null = null;
-
-    override connectedCallback() {
-      super.connectedCallback();
-      // Cache this reference, so that the same one can be used in
-      // disconnectedCallback(), othehrwise if `model_` has already been removed
-      // from the DOM, getInstance() will throw an error.
-      this.model_ = getInstance();
-    }
-
-    override disconnectedCallback() {
-      super.disconnectedCallback();
-      assert(this.model_);
-
-      if (this.model_.isConnected) {
-        // Only remove observers if the PrintPreviewModelElement original
-        // singleton instance is still connected to the DOM. Otherwise all
-        // observers have already been remomved in PrintPreviewModelElement's
-        // disconnectedCallback.
-        for (const id of this.observers_) {
-          const removed = this.model_.observable.removeObserver(id);
-          assert(removed);
-        }
-      }
-
-      this.model_ = null;
-      this.observers_ = [];
-    }
-
-    addSettingObserver(path: string, callback: ChangeCallback) {
-      const id = getInstance().observable.addObserver(path, callback);
-      this.observers_.push(id);
-    }
-
-    getSetting(settingName: keyof Settings): Setting {
-      return getInstance().getSetting(settingName);
-    }
-
-    getSettingValue(settingName: keyof Settings): any {
-      return getInstance().getSettingValue(settingName);
-    }
-
-    setSetting(settingName: keyof Settings, value: any, noSticky?: boolean) {
-      getInstance().setSetting(settingName, value, noSticky);
-    }
-
-    setSettingValid(settingName: keyof Settings, valid: boolean) {
-      getInstance().setSettingValid(settingName, valid);
-    }
-  }
-
-  return SettingsMixinLit;
-};
-
-export interface SettingsMixinLitInterface {
-  addSettingObserver(path: string, callback: ChangeCallback): void;
-
-  /**
-   * @param settingName Name of the setting to get.
-   * @return The setting object.
-   */
-  getSetting(settingName: keyof Settings): Setting;
-
-  /**
-   * @param settingName Name of the setting to get the value for.
-   * @return The value of the setting, accounting for availability.
-   */
-  getSettingValue(settingName: keyof Settings): any;
-
-  /**
-   * Sets settings.settingName.value to |value|, unless updating the setting is
-   * disallowed by enterprise policy. Fires preview-setting-changed and
-   * sticky-setting-changed events if the update impacts the preview or requires
-   * an update to sticky settings.
-   * @param settingName Name of the setting to set
-   * @param value The value to set the setting to.
-   * @param noSticky Whether to avoid stickying the setting. Defaults to false.
-   */
-  setSetting(settingName: keyof Settings, value: any, noSticky?: boolean): void;
-
-  /**
-   * Sets the validity of |settingName| to |valid|. If the validity is changed,
-   * fires a setting-valid-changed event.
-   * @param settingName Name of the setting to set
-   * @param valid Whether the setting value is currently valid.
-   */
-  setSettingValid(settingName: keyof Settings, valid: boolean): void;
-}
diff --git a/chrome/browser/resources/print_preview/ui/settings_select.ts b/chrome/browser/resources/print_preview/ui/settings_select.ts
index 8a4dfaa..59bd002 100644
--- a/chrome/browser/resources/print_preview/ui/settings_select.ts
+++ b/chrome/browser/resources/print_preview/ui/settings_select.ts
@@ -9,13 +9,13 @@
 import type {Settings} from '../data/model.js';
 import {getStringForCurrentLocale} from '../print_preview_utils.js';
 
-import {SelectMixinLit} from './select_mixin_lit.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SelectMixin} from './select_mixin.js';
+import {SettingsMixin} from './settings_mixin.js';
 import {getCss} from './settings_select.css.js';
 import {getHtml} from './settings_select.html.js';
 
 const PrintPreviewSettingsSelectElementBase =
-    SettingsMixinLit(SelectMixinLit(CrLitElement));
+    SettingsMixin(SelectMixin(CrLitElement));
 
 export class PrintPreviewSettingsSelectElement extends
     PrintPreviewSettingsSelectElementBase {
diff --git a/chrome/browser/resources/print_preview/ui/sidebar.ts b/chrome/browser/resources/print_preview/ui/sidebar.ts
index a6a97c8..b3166aab 100644
--- a/chrome/browser/resources/print_preview/ui/sidebar.ts
+++ b/chrome/browser/resources/print_preview/ui/sidebar.ts
@@ -28,7 +28,7 @@
 import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 import type {PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
-import {DarkModeMixinLit} from '../dark_mode_mixin_lit.js';
+import {DarkModeMixin} from '../dark_mode_mixin.js';
 import type {Cdd} from '../data/cdd.js';
 import type {Destination} from '../data/destination.js';
 import type {Settings} from '../data/model.js';
@@ -37,7 +37,7 @@
 import {MetricsContext, PrintSettingsUiBucket} from '../metrics.js';
 
 import type {DestinationState, PrintPreviewDestinationSettingsElement} from './destination_settings.js';
-import {SettingsMixinLit} from './settings_mixin_lit.js';
+import {SettingsMixin} from './settings_mixin.js';
 import {getCss} from './sidebar.css.js';
 import {getHtml} from './sidebar.html.js';
 
@@ -71,7 +71,7 @@
 }
 
 const PrintPreviewSidebarElementBase = CrContainerShadowMixinLit(
-    WebUiListenerMixinLit(SettingsMixinLit(DarkModeMixinLit(CrLitElement))));
+    WebUiListenerMixinLit(SettingsMixin(DarkModeMixin(CrLitElement))));
 
 export class PrintPreviewSidebarElement extends PrintPreviewSidebarElementBase {
   static get is() {
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html
index ede8a7e..64e24bd5 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.html
+++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -218,8 +218,7 @@
       </h2>
     </div>
     <settings-personalization-options class="list-frame" prefs="{{prefs}}"
-        page-visibility="[[pageVisibility]]" sync-status="[[syncStatus]]"
-        focus-config="[[focusConfig]]">
+        page-visibility="[[pageVisibility]]" sync-status="[[syncStatus]]">
     </settings-personalization-options>
 
 <if expr="not chromeos_ash">
diff --git a/chrome/browser/resources/settings/privacy_page/personalization_options.ts b/chrome/browser/resources/settings/privacy_page/personalization_options.ts
index 993aca33..3ffec6a 100644
--- a/chrome/browser/resources/settings/privacy_page/personalization_options.ts
+++ b/chrome/browser/resources/settings/privacy_page/personalization_options.ts
@@ -37,7 +37,6 @@
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 
 import type {SettingsToggleButtonElement} from '../controls/settings_toggle_button.js';
-import type {FocusConfig} from '../focus_config.js';
 import {loadTimeData} from '../i18n_setup.js';
 import type {PrivacyPageVisibility} from '../page_visibility.js';
 import type {SettingsSignoutDialogElement} from '../people_page/signout_dialog.js';
@@ -76,11 +75,6 @@
 
   static get properties() {
     return {
-      focusConfig: {
-        type: Object,
-        observer: 'onFocusConfigChange_',
-      },
-
       pageVisibility: Object,
 
       syncStatus: Object,
@@ -129,7 +123,6 @@
   }
 
   declare pageVisibility: PrivacyPageVisibility;
-  declare focusConfig: FocusConfig;
   declare syncStatus: SyncStatus;
 
   // <if expr="_google_chrome and not chromeos_ash">
diff --git a/chrome/browser/resources/signin/history_sync_optin/history_sync_optin.html b/chrome/browser/resources/signin/history_sync_optin/history_sync_optin.html
index bc7cad7..cb110cd 100644
--- a/chrome/browser/resources/signin/history_sync_optin/history_sync_optin.html
+++ b/chrome/browser/resources/signin/history_sync_optin/history_sync_optin.html
@@ -7,6 +7,10 @@
     <style>
       body {
         margin: 0;
+        /* Corresponds to the default browser modal width. */
+        min-width: 448px;
+        padding: 0;
+        width: 100vw;
       }
 
       @media (prefers-color-scheme: dark) {
diff --git a/chrome/browser/resources/signin/history_sync_optin/history_sync_optin.ts b/chrome/browser/resources/signin/history_sync_optin/history_sync_optin.ts
index 8b3027c9..d958de2 100644
--- a/chrome/browser/resources/signin/history_sync_optin/history_sync_optin.ts
+++ b/chrome/browser/resources/signin/history_sync_optin/history_sync_optin.ts
@@ -4,5 +4,23 @@
 
 import './history_sync_optin_app.js';
 
+import {HistorySyncOptInBrowserProxyImpl} from './browser_proxy.js';
+
 export {PageCallbackRouter, PageHandlerInterface, PageRemote} from './history_sync_optin.mojom-webui.js';
 export {HistorySyncOptinAppElement} from './history_sync_optin_app.js';
+
+function initialize() {
+  const historySyncOptInBrowserProxy =
+      HistorySyncOptInBrowserProxyImpl.getInstance();
+  // Prefer using |document.body.offsetHeight| instead of
+  // |document.body.scrollHeight| as it returns the correct height of the
+  // even when the page zoom in Chrome is different than 100%.
+  historySyncOptInBrowserProxy.handler.updateDialogHeight(
+      document.body.offsetHeight);
+  // The web dialog size has been initialized, so reset the body width to
+  // auto. This makes sure that the body only takes up the viewable width,
+  // e.g. when there is a scrollbar.
+  document.body.style.width = 'auto';
+}
+
+document.addEventListener('DOMContentLoaded', initialize);
diff --git a/chrome/browser/resources/signin/history_sync_optin/history_sync_optin_app.css b/chrome/browser/resources/signin/history_sync_optin/history_sync_optin_app.css
index 5563182cf..1628aa6 100644
--- a/chrome/browser/resources/signin/history_sync_optin/history_sync_optin_app.css
+++ b/chrome/browser/resources/signin/history_sync_optin/history_sync_optin_app.css
@@ -11,9 +11,9 @@
  * #css_wrapper_metadata_end */
 
 :host {
+  border-radius: 12px;
   color: var(--cr-primary-text-color);
   display: block;
-  border-radius: 12px;
   overflow: hidden;
 }
 
@@ -30,14 +30,14 @@
   border: solid var(--md-background-color);
   border-radius: 50%;
   height: 64px;
-  width: 64px;
   /* To place the avatar at the center of the background image,
      place it at top left point of its parent,
      then shift it back by half its width and height. */
+  left: 50%;
   position: absolute;
   top: 50%;
-  left: 50%;
   transform: translate(-50%, -50%);
+  width: 64px;
 }
 
 .dialog-illustration {
@@ -48,6 +48,9 @@
 #imageContainer {
   align-self: center;
   display: flex;
+  /* The height must be set so that the height of this relatively positioned
+  container is taken into account in the height calculator of the entire screen. */
+  height: 162px;
   justify-content: center;
   /* Establishes a positioning context for absolute children. */
   position: relative;
@@ -55,19 +58,19 @@
 
 #buttonRow {
   display: flex;
-  justify-content: flex-end;
   gap: 8px;
+  justify-content: flex-end;
 }
 
 #modalDescription {
-  /* layout */
-  padding: 12px 20px 12px 20px;
   /* colors */
-  border-top: 1px solid var(--cr-fallback-color-neutral-outline);
   background-color: var(--cr-fallback-color-neutral-container);
+  border-top: 1px solid var(--cr-fallback-color-neutral-outline);
   /* fonts */
   font-size: 11px;
   line-height: 20px;
+  /* layout */
+  padding: 12px 20px 12px 20px;
 }
 
 h1 {
@@ -75,7 +78,6 @@
   font-weight: 500;
 }
 
-
 @media (prefers-color-scheme: dark) {
   .dialog-illustration {
    content: url(./images/avatar_surrounding_illustration_dark.svg);
diff --git a/chrome/browser/resources/tab_strip/alert_indicator.ts b/chrome/browser/resources/tab_strip/alert_indicator.ts
index af9526d..af862ec1 100644
--- a/chrome/browser/resources/tab_strip/alert_indicator.ts
+++ b/chrome/browser/resources/tab_strip/alert_indicator.ts
@@ -34,6 +34,7 @@
     case TabAlertState.kAudioMuting:
       return loadTimeData.getStringF('audioMuting', '');
     case TabAlertState.kBluetoothConnected:
+    case TabAlertState.kBluetoothScanActive:
       return loadTimeData.getStringF('bluetoothConnected', '');
     case TabAlertState.kUsbConnected:
       return loadTimeData.getStringF('usbConnected', '');
@@ -60,6 +61,7 @@
   [TabAlertState.kAudioPlaying, 'audio-playing'],
   [TabAlertState.kAudioMuting, 'audio-muting'],
   [TabAlertState.kBluetoothConnected, 'bluetooth-connected'],
+  [TabAlertState.kBluetoothScanActive, 'bluetooth-connected'],
   [TabAlertState.kUsbConnected, 'usb-connected'],
   [TabAlertState.kHidConnected, 'hid-connected'],
   [TabAlertState.kSerialConnected, 'serial-connected'],
diff --git a/chrome/browser/resources/tab_strip/alert_indicators.ts b/chrome/browser/resources/tab_strip/alert_indicators.ts
index f317740d..32de369 100644
--- a/chrome/browser/resources/tab_strip/alert_indicators.ts
+++ b/chrome/browser/resources/tab_strip/alert_indicators.ts
@@ -34,6 +34,7 @@
       [TabAlertState.kAudioPlaying, audioIndicator],
       [TabAlertState.kAudioMuting, audioIndicator],
       [TabAlertState.kBluetoothConnected, new AlertIndicatorElement()],
+      [TabAlertState.kBluetoothScanActive, new AlertIndicatorElement()],
       [TabAlertState.kUsbConnected, new AlertIndicatorElement()],
       [TabAlertState.kHidConnected, new AlertIndicatorElement()],
       [TabAlertState.kSerialConnected, new AlertIndicatorElement()],
diff --git a/chrome/browser/resources/tab_strip/drag_manager.ts b/chrome/browser/resources/tab_strip/drag_manager.ts
index 15ffad8..3b10bc0 100644
--- a/chrome/browser/resources/tab_strip/drag_manager.ts
+++ b/chrome/browser/resources/tab_strip/drag_manager.ts
@@ -12,7 +12,7 @@
 import type {TabGroupElement} from './tab_group.js';
 import {isDragHandle, isTabGroupElement} from './tab_group.js';
 import type {Tab} from './tab_strip.mojom-webui.js';
-import {TabNetworkState} from './tab_strip.mojom-webui.js';
+import {TabNetworkState} from './tabs.mojom-webui.js';
 import type {TabsApiProxy} from './tabs_api_proxy.js';
 import {TabsApiProxyImpl} from './tabs_api_proxy.js';
 
diff --git a/chrome/browser/resources/tab_strip/tab.ts b/chrome/browser/resources/tab_strip/tab.ts
index f4dc3ff2..e6ddfc3 100644
--- a/chrome/browser/resources/tab_strip/tab.ts
+++ b/chrome/browser/resources/tab_strip/tab.ts
@@ -14,8 +14,8 @@
 import type {AlertIndicatorsElement} from './alert_indicators.js';
 import {getTemplate} from './tab.html.js';
 import type {Tab} from './tab_strip.mojom-webui.js';
-import {TabNetworkState} from './tab_strip.mojom-webui.js';
 import {TabSwiper} from './tab_swiper.js';
+import {TabNetworkState} from './tabs.mojom-webui.js';
 import type {TabsApiProxy} from './tabs_api_proxy.js';
 import {CloseTabAction, TabsApiProxyImpl} from './tabs_api_proxy.js';
 
diff --git a/chrome/browser/safe_browsing/delayed_warning_navigation_throttle.cc b/chrome/browser/safe_browsing/delayed_warning_navigation_throttle.cc
index ba870de..71f4bcf 100644
--- a/chrome/browser/safe_browsing/delayed_warning_navigation_throttle.cc
+++ b/chrome/browser/safe_browsing/delayed_warning_navigation_throttle.cc
@@ -20,24 +20,26 @@
 namespace safe_browsing {
 
 DelayedWarningNavigationThrottle::DelayedWarningNavigationThrottle(
-    content::NavigationHandle* navigation_handle)
-    : content::NavigationThrottle(navigation_handle) {}
+    content::NavigationThrottleRegistry& registry)
+    : content::NavigationThrottle(registry) {}
 
 DelayedWarningNavigationThrottle::~DelayedWarningNavigationThrottle() = default;
 
-std::unique_ptr<DelayedWarningNavigationThrottle>
-DelayedWarningNavigationThrottle::MaybeCreateNavigationThrottle(
-    content::NavigationHandle* navigation_handle) {
+// static
+void DelayedWarningNavigationThrottle::MaybeCreateAndAdd(
+    content::NavigationThrottleRegistry& registry) {
   // If the tab is being no-state prefetched, stop here before it breaks
   // metrics.
-  content::WebContents* web_contents = navigation_handle->GetWebContents();
+  content::WebContents* web_contents =
+      registry.GetNavigationHandle().GetWebContents();
   if (prerender::ChromeNoStatePrefetchContentsDelegate::FromWebContents(
           web_contents)) {
-    return nullptr;
+    return;
   }
 
   // Otherwise, always insert the throttle for metrics recording.
-  return std::make_unique<DelayedWarningNavigationThrottle>(navigation_handle);
+  registry.AddThrottle(
+      std::make_unique<DelayedWarningNavigationThrottle>(registry));
 }
 
 const char* DelayedWarningNavigationThrottle::GetNameForLogging() {
diff --git a/chrome/browser/safe_browsing/delayed_warning_navigation_throttle.h b/chrome/browser/safe_browsing/delayed_warning_navigation_throttle.h
index 07635003..489240f 100644
--- a/chrome/browser/safe_browsing/delayed_warning_navigation_throttle.h
+++ b/chrome/browser/safe_browsing/delayed_warning_navigation_throttle.h
@@ -5,14 +5,8 @@
 #ifndef CHROME_BROWSER_SAFE_BROWSING_DELAYED_WARNING_NAVIGATION_THROTTLE_H_
 #define CHROME_BROWSER_SAFE_BROWSING_DELAYED_WARNING_NAVIGATION_THROTTLE_H_
 
-#include <memory>
-
 #include "content/public/browser/navigation_throttle.h"
 
-namespace content {
-class NavigationHandle;
-}  // namespace content
-
 namespace safe_browsing {
 
 // A navigation throttle that detects downloads when a SafeBrowsing warning is
@@ -21,11 +15,11 @@
 // security moment such as a download or permission request occurs.
 class DelayedWarningNavigationThrottle : public content::NavigationThrottle {
  public:
-  explicit DelayedWarningNavigationThrottle(content::NavigationHandle* handle);
-  ~DelayedWarningNavigationThrottle() override;
+  static void MaybeCreateAndAdd(content::NavigationThrottleRegistry& registry);
 
-  static std::unique_ptr<DelayedWarningNavigationThrottle>
-  MaybeCreateNavigationThrottle(content::NavigationHandle* navigation_handle);
+  explicit DelayedWarningNavigationThrottle(
+      content::NavigationThrottleRegistry& registry);
+  ~DelayedWarningNavigationThrottle() override;
 
   // content::NavigationThrottle:
   ThrottleCheckResult WillProcessResponse() override;
diff --git a/chrome/browser/share/OWNERS b/chrome/browser/share/OWNERS
index 61cf4c4..640aae4b 100644
--- a/chrome/browser/share/OWNERS
+++ b/chrome/browser/share/OWNERS
@@ -2,9 +2,6 @@
 # context, especially for desktop:
 file://chrome/browser/follow/OWNERS
 
-# For Android:
-wenyufu@chromium.org
-
 # For iOS:
 # kalettuce@chromium.org - pending committership
 sczs@chromium.org
diff --git a/chrome/browser/share/android/OWNERS b/chrome/browser/share/android/OWNERS
new file mode 100644
index 0000000..54c4baf5
--- /dev/null
+++ b/chrome/browser/share/android/OWNERS
@@ -0,0 +1 @@
+wenyufu@chromium.org
diff --git a/chrome/browser/sync/test/integration/BUILD.gn b/chrome/browser/sync/test/integration/BUILD.gn
index 80a3ff9b..4e5e7f5 100644
--- a/chrome/browser/sync/test/integration/BUILD.gn
+++ b/chrome/browser/sync/test/integration/BUILD.gn
@@ -29,10 +29,8 @@
     "//components/bookmarks/browser",
     "//components/data_sharing/public",
     "//components/password_manager/core/browser/sharing",
-    "//components/plus_addresses",
     "//components/plus_addresses:test_support",
-    "//components/plus_addresses/settings",
-    "//components/plus_addresses/settings:test_support",
+    "//components/plus_addresses/resources/strings",
     "//components/plus_addresses/webdata",
     "//components/reading_list/core:test_support",
     "//components/saved_tab_groups/test_support",
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index a0b86a0..2f63a04f 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -415,6 +415,7 @@
     "//chrome/browser/ui/tab_contents:impl",
     "//chrome/browser/ui/webui",
     "//chrome/browser/ui/webui/about",
+    "//chrome/browser/ui/webui/about:impl",
     "//chrome/browser/ui/webui/accessibility",
     "//chrome/browser/ui/webui/bluetooth_internals",
     "//chrome/browser/ui/zoom",
@@ -746,7 +747,7 @@
 
     # TODO(crbug.com/364667551): includes //c/b/about_flags.h and other sources
     # from //chrome/browser/ui/webui, which are not modularized yet.
-    "//chrome/browser/ui/webui/about",
+    "//chrome/browser/ui/webui/about:impl",
   ]
 
   if (enable_vr && is_win) {
@@ -970,6 +971,8 @@
 
     if (enable_extensions_core) {
       sources += [
+        "android/extensions/extension_keybinding_registry_android.cc",
+        "android/extensions/extension_keybinding_registry_android.h",
         "android/toolbar/extension_actions_bridge.cc",
         "android/toolbar/extension_actions_bridge.h",
         "android/toolbar/extension_actions_bridge_factory.cc",
diff --git a/chrome/browser/ui/android/edge_to_edge/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeUtils.java b/chrome/browser/ui/android/edge_to_edge/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeUtils.java
index 731a93e..5eb61661 100644
--- a/chrome/browser/ui/android/edge_to_edge/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeUtils.java
+++ b/chrome/browser/ui/android/edge_to_edge/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeUtils.java
@@ -16,6 +16,7 @@
 
 import org.chromium.base.ApkInfo;
 import org.chromium.base.BuildInfo;
+import org.chromium.base.DeviceInfo;
 import org.chromium.base.Log;
 import org.chromium.base.ResettersForTesting;
 import org.chromium.base.metrics.RecordHistogram;
@@ -49,6 +50,8 @@
             "Android.EdgeToEdge.IneligibilityReason";
     private static final String PARAM_SAFE_AREA_CONSTRAINT_SCROLLABLE_WHEN_STACKING =
             "scrollable_when_stacking";
+    private static final String MISSING_NAVBAR_INSETS_HISTOGRAM =
+            "Android.EdgeToEdge.MissingNavbarInsets2";
 
     /** The reason of why the current session is not eligible for edge to edge. */
     @IntDef({
@@ -67,6 +70,32 @@
         int NUM_TYPES = 4;
     }
 
+
+    /**
+     * The reason of why the navigation bar insets are missing.
+     */
+    // These values are persisted to logs. Entries should not be renumbered and
+    // numeric values should never be reused.
+    @IntDef({
+        MissingNavbarInsetsReason.OTHER,
+        MissingNavbarInsetsReason.IN_MULTI_WINDOW,
+        MissingNavbarInsetsReason.IN_DESKTOP_WINDOW,
+        MissingNavbarInsetsReason.IN_FULLSCREEN,
+        MissingNavbarInsetsReason.ACTIVITY_NOT_VISIBLE,
+        MissingNavbarInsetsReason.SYSTEM_BAR_INSETS_EMPTY,
+        MissingNavbarInsetsReason.NUM_ENTRIES
+    })
+    public @interface MissingNavbarInsetsReason {
+        int OTHER = 0;
+        int IN_MULTI_WINDOW = 1;
+        int IN_DESKTOP_WINDOW = 2;
+        int IN_FULLSCREEN = 3;
+        int ACTIVITY_NOT_VISIBLE = 4;
+        int SYSTEM_BAR_INSETS_EMPTY = 5;
+
+        int NUM_ENTRIES = 5;
+    }
+
     /**
      * Whether the draw edge to edge infrastructure is on. When this is enabled, Chrome will start
      * drawing edge to edge on start up.
@@ -100,7 +129,7 @@
             return false;
         }
 
-        if (BuildInfo.getInstance().isAutomotive || BuildInfo.getInstance().isDesktop) {
+        if (DeviceInfo.isAutomotive() || DeviceInfo.isDesktop()) {
             return false;
         }
 
@@ -193,6 +222,17 @@
     }
 
     /**
+     * Record if the current activity is missing the navigation bar.
+     *
+     * @param reason The reason of why the navigation bar is missing.
+     */
+    public static void recordIfMissingNavigationBar(@MissingNavbarInsetsReason int reason) {
+        RecordHistogram.recordEnumeratedHistogram(
+                    MISSING_NAVBAR_INSETS_HISTOGRAM, reason,
+                    MissingNavbarInsetsReason.NUM_ENTRIES);
+    }
+
+    /**
      * @param isPageOptedIntoEdgeToEdge Whether the page has opted into edge-to-edge.
      * @param layoutType The active layout type being shown.
      * @param bottomInset The bottom inset representing the height of the bottom OS navbar.
diff --git a/chrome/browser/ui/android/extensions/BUILD.gn b/chrome/browser/ui/android/extensions/BUILD.gn
new file mode 100644
index 0000000..59db8c69
--- /dev/null
+++ b/chrome/browser/ui/android/extensions/BUILD.gn
@@ -0,0 +1,31 @@
+# Copyright 2025 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/config.gni")
+import("//build/config/android/rules.gni")
+import("//extensions/buildflags/buildflags.gni")
+import("//third_party/jni_zero/jni_zero.gni")
+
+android_library("java") {
+  sources = [ "java/src/org/chromium/chrome/browser/ui/extensions/ExtensionKeybindingRegistry.java" ]
+  deps = [
+    "//base:lifetime_java",
+    "//base:service_loader_java",
+    "//chrome/browser/profiles/android:java",
+  ]
+
+  if (enable_extensions_core) {
+    deps += [
+      "//chrome/browser/flags:java",
+      "//third_party/jni_zero:jni_zero_java",
+    ]
+    sources += [ "java/src/org/chromium/chrome/browser/ui/extensions/ExtensionKeybindingRegistryAndroid.java" ]
+    srcjar_deps = [ ":jni_headers" ]
+    resources_package = "org.chromium.chrome.browser.ui.extensions"
+  }
+}
+
+generate_jni("jni_headers") {
+  sources = [ "java/src/org/chromium/chrome/browser/ui/extensions/ExtensionKeybindingRegistryAndroid.java" ]
+}
diff --git a/chrome/browser/ui/android/extensions/OWNERS b/chrome/browser/ui/android/extensions/OWNERS
new file mode 100644
index 0000000..6987daa9
--- /dev/null
+++ b/chrome/browser/ui/android/extensions/OWNERS
@@ -0,0 +1 @@
+file://extensions/OWNERS
diff --git a/chrome/browser/ui/android/extensions/extension_keybinding_registry_android.cc b/chrome/browser/ui/android/extensions/extension_keybinding_registry_android.cc
new file mode 100644
index 0000000..1936ddf0
--- /dev/null
+++ b/chrome/browser/ui/android/extensions/extension_keybinding_registry_android.cc
@@ -0,0 +1,80 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/android/extensions/extension_keybinding_registry_android.h"
+
+#include <jni.h>
+
+#include "base/containers/contains.h"
+#include "base/functional/bind.h"
+#include "chrome/browser/extensions/commands/command_service.h"
+#include "chrome/browser/extensions/extension_keybinding_registry.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/android/extensions/jni_headers/ExtensionKeybindingRegistryAndroid_jni.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+#include "third_party/jni_zero/jni_zero.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/events/android/key_event_android.h"
+#include "ui/events/event.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+
+ExtensionKeybindingRegistryAndroid::ExtensionKeybindingRegistryAndroid(
+    content::BrowserContext* context,
+    ExtensionFilter extension_filter,
+    Delegate* delegate)
+    : ExtensionKeybindingRegistry(context, extension_filter, delegate) {}
+
+ExtensionKeybindingRegistryAndroid::~ExtensionKeybindingRegistryAndroid() =
+    default;
+
+void ExtensionKeybindingRegistryAndroid::RegisterAccelerator(
+    const ui::Accelerator& accelerator) {
+  active_accelerators_.insert(accelerator);
+}
+
+void ExtensionKeybindingRegistryAndroid::UnregisterAccelerator(
+    const ui::Accelerator& accelerator) {
+  active_accelerators_.erase(accelerator);
+}
+
+void ExtensionKeybindingRegistryAndroid::OnShortcutHandlingSuspended(
+    bool suspended) {
+  is_shortcut_handling_suspended_ = suspended;
+}
+
+// JNI functions
+
+static jlong JNI_ExtensionKeybindingRegistryAndroid_Init(
+    JNIEnv* env,
+    const jni_zero::JavaParamRef<jobject>& profile_obj) {
+  Profile* profile = Profile::FromJavaObject(profile_obj);
+  ExtensionKeybindingRegistryAndroid* instance =
+      new ExtensionKeybindingRegistryAndroid(
+          profile, extensions::ExtensionKeybindingRegistry::ALL_EXTENSIONS,
+          nullptr);
+  return reinterpret_cast<jlong>(instance);
+}
+
+void ExtensionKeybindingRegistryAndroid::Destroy(JNIEnv* env) {
+  delete this;
+}
+
+jboolean ExtensionKeybindingRegistryAndroid::HandleKeyEvent(
+    JNIEnv* env,
+    const jni_zero::JavaParamRef<jobject>& java_key_event) {
+  if (is_shortcut_handling_suspended_) {
+    return false;
+  }
+
+  ui::KeyEventAndroid native_key_event(env, java_key_event);
+  ui::KeyEvent key_event = native_key_event.ToKeyEvent();
+  ui::Accelerator accelerator(key_event);
+
+  if (!active_accelerators_.contains(accelerator)) {
+    return false;
+  }
+
+  return NotifyEventTargets(accelerator);
+}
diff --git a/chrome/browser/ui/android/extensions/extension_keybinding_registry_android.h b/chrome/browser/ui/android/extensions/extension_keybinding_registry_android.h
new file mode 100644
index 0000000..c923a9a
--- /dev/null
+++ b/chrome/browser/ui/android/extensions/extension_keybinding_registry_android.h
@@ -0,0 +1,49 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ANDROID_EXTENSIONS_EXTENSION_KEYBINDING_REGISTRY_ANDROID_H_
+#define CHROME_BROWSER_UI_ANDROID_EXTENSIONS_EXTENSION_KEYBINDING_REGISTRY_ANDROID_H_
+
+#include "chrome/browser/extensions/extension_keybinding_registry.h"
+#include "ui/base/accelerators/accelerator.h"
+
+namespace content {
+class BrowserContext;
+}
+
+// This class handles keyboard accelerators for extensions on Android.
+class ExtensionKeybindingRegistryAndroid
+    : public extensions::ExtensionKeybindingRegistry {
+ public:
+  ExtensionKeybindingRegistryAndroid(content::BrowserContext* context,
+                                     ExtensionFilter extension_filter,
+                                     Delegate* delegate);
+
+  ExtensionKeybindingRegistryAndroid(
+      const ExtensionKeybindingRegistryAndroid&) = delete;
+  ExtensionKeybindingRegistryAndroid& operator=(
+      const ExtensionKeybindingRegistryAndroid&) = delete;
+
+  ~ExtensionKeybindingRegistryAndroid() override;
+
+  // Destroys this instance.
+  void Destroy(JNIEnv* env);
+
+  // Handles the key event. It returns whether the key event was handled. It
+  // immediately returns false if the given key event should not intercept.
+  jboolean HandleKeyEvent(
+      JNIEnv* env,
+      const jni_zero::JavaParamRef<jobject>& java_key_event);
+
+ private:
+  // Overridden from ExtensionKeybindingRegistry:
+  void RegisterAccelerator(const ui::Accelerator& accelerator) override;
+  void UnregisterAccelerator(const ui::Accelerator& accelerator) override;
+  void OnShortcutHandlingSuspended(bool suspended) override;
+
+  std::set<ui::Accelerator> active_accelerators_;
+  bool is_shortcut_handling_suspended_ = false;
+};
+
+#endif  // CHROME_BROWSER_UI_ANDROID_EXTENSIONS_EXTENSION_KEYBINDING_REGISTRY_ANDROID_H_
diff --git a/chrome/browser/ui/android/extensions/java/src/org/chromium/chrome/browser/ui/extensions/ExtensionKeybindingRegistry.java b/chrome/browser/ui/android/extensions/java/src/org/chromium/chrome/browser/ui/extensions/ExtensionKeybindingRegistry.java
new file mode 100644
index 0000000..c2ddf82
--- /dev/null
+++ b/chrome/browser/ui/android/extensions/java/src/org/chromium/chrome/browser/ui/extensions/ExtensionKeybindingRegistry.java
@@ -0,0 +1,43 @@
+// Copyright 2025 The Chromium Authors
+// 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.ui.extensions;
+
+import android.view.KeyEvent;
+
+import org.chromium.base.ServiceLoaderUtil;
+import org.chromium.base.lifetime.Destroyable;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.profiles.Profile;
+
+/**
+ * Manages the registration and handling of keybindings (keyboard shortcuts) defined by chrome
+ * extensions.
+ */
+@NullMarked
+public interface ExtensionKeybindingRegistry extends Destroyable {
+    /** Initializes the registry. */
+    void initialize(Profile profile);
+
+    /**
+     * Handles the KeyEvent corresponding to an extension shortcut. This triggers a matching command
+     * execution, if any.
+     *
+     * @param event The KeyEvent to handle.
+     */
+    boolean handleKeyEvent(KeyEvent event);
+
+    /** Factory method to obtain an instance of the registry, if available. */
+    @Nullable
+    public static ExtensionKeybindingRegistry maybeCreate(Profile profile) {
+        ExtensionKeybindingRegistry registry =
+                ServiceLoaderUtil.maybeCreate(ExtensionKeybindingRegistry.class);
+        if (registry == null) {
+            return null;
+        }
+        registry.initialize(profile);
+        return registry;
+    }
+}
diff --git a/chrome/browser/ui/android/extensions/java/src/org/chromium/chrome/browser/ui/extensions/ExtensionKeybindingRegistryAndroid.java b/chrome/browser/ui/android/extensions/java/src/org/chromium/chrome/browser/ui/extensions/ExtensionKeybindingRegistryAndroid.java
new file mode 100644
index 0000000..2f30b03
--- /dev/null
+++ b/chrome/browser/ui/android/extensions/java/src/org/chromium/chrome/browser/ui/extensions/ExtensionKeybindingRegistryAndroid.java
@@ -0,0 +1,58 @@
+// Copyright 2025 The Chromium Authors
+// 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.ui.extensions;
+
+import android.view.KeyEvent;
+
+import org.jni_zero.NativeMethods;
+
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.ServiceImpl;
+import org.chromium.chrome.browser.profiles.Profile;
+
+/**
+ * JNI bridge for ExtensionKeybindingRegistryAndroid (C++). Owns the native
+ * ExtensionKeybindingRegistryAndroid object and handles communication between Java and C++.
+ */
+@NullMarked
+@ServiceImpl(ExtensionKeybindingRegistry.class)
+public class ExtensionKeybindingRegistryAndroid implements ExtensionKeybindingRegistry {
+    private long mNativeExtensionKeybindingRegistryAndroid;
+
+    @Override
+    public void initialize(Profile profile) {
+        mNativeExtensionKeybindingRegistryAndroid =
+                ExtensionKeybindingRegistryAndroidJni.get().init(profile);
+    }
+
+    @Override
+    public boolean handleKeyEvent(KeyEvent event) {
+        if (event.getAction() != KeyEvent.ACTION_DOWN || event.getRepeatCount() > 0) return false;
+
+        if (mNativeExtensionKeybindingRegistryAndroid == 0) return false;
+
+        return ExtensionKeybindingRegistryAndroidJni.get()
+                .handleKeyEvent(mNativeExtensionKeybindingRegistryAndroid, event);
+    }
+
+    /** Destroys the native counterpart. */
+    @Override
+    public void destroy() {
+        if (mNativeExtensionKeybindingRegistryAndroid != 0) {
+            ExtensionKeybindingRegistryAndroidJni.get()
+                    .destroy(mNativeExtensionKeybindingRegistryAndroid);
+            mNativeExtensionKeybindingRegistryAndroid = 0;
+        }
+    }
+
+    @NativeMethods
+    interface Natives {
+        long init(Profile profile);
+
+        void destroy(long nativeExtensionKeybindingRegistryAndroid);
+
+        boolean handleKeyEvent(long nativeExtensionKeybindingRegistryAndroid, KeyEvent event);
+    }
+}
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinator.java b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinator.java
index 6ad488b..bdfd0c6c 100644
--- a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinator.java
+++ b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinator.java
@@ -45,6 +45,13 @@
 @NullMarked
 public class PdfCoordinator {
     private static final String TAG = "PdfCoordinator";
+
+    /**
+     * The timestamp when the last pdf document starts to load. Used to calculate the elapsed time
+     * between two pdf loads.
+     */
+    private static long sLastPdfLoadTimestamp;
+
     private static boolean sSkipLoadPdfForTesting;
     private final View mView;
     private final FragmentManager mFragmentManager;
@@ -230,17 +237,22 @@
         }
         mUri = PdfUtils.getUriFromFilePath(mPdfFilePath);
         if (mUri != null) {
+            // TODO(crbug.com/418075119): Minimize the try catch block.
             try {
                 if (!sSkipLoadPdfForTesting) {
                     // Committing the fragment
-                    // TODO(b/360717802): Reuse fragment from savedInstance.
+                    // TODO(crbug.com/360717802): Reuse fragment from savedInstance.
                     FragmentTransaction transaction = mFragmentManager.beginTransaction();
                     transaction.add(mFragmentContainerViewId, mChromePdfViewerFragment, mTabId);
                     transaction.commitAllowingStateLoss();
                     mFragmentManager.executePendingTransactions();
                     PdfUtils.recordPdfLoad();
-                    mChromePdfViewerFragment.mDocumentLoadStartTimestamp =
-                            SystemClock.elapsedRealtime();
+                    long currentTime = SystemClock.elapsedRealtime();
+                    mChromePdfViewerFragment.mDocumentLoadStartTimestamp = currentTime;
+                    if (sLastPdfLoadTimestamp > 0) {
+                        PdfUtils.recordPdfLoadInterval(currentTime - sLastPdfLoadTimestamp);
+                    }
+                    sLastPdfLoadTimestamp = currentTime;
                     mProgressBar.setVisibility(View.GONE);
                     mChromePdfViewerFragment.setDocumentUri(mUri);
                 }
@@ -250,7 +262,7 @@
                 mIsPdfLoaded = true;
             }
         } else {
-            // TODO(b/348712628): show some error UI when content URI is null.
+            // TODO(crbug.com/348712628): show some error UI when content URI is null.
             Log.e(TAG, "Uri is null.");
         }
     }
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfUtils.java b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfUtils.java
index 9e69e56d..6c29922 100644
--- a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfUtils.java
+++ b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfUtils.java
@@ -346,6 +346,10 @@
         RecordHistogram.recordTimesHistogram("Android.Pdf.DocumentLoadTime.FirstPaired", duration);
     }
 
+    static void recordPdfLoadInterval(long duration) {
+        RecordHistogram.recordMediumTimesHistogram("Android.Pdf.DocumentLoadInterval", duration);
+    }
+
     static void recordPdfTransientDownloadTime(long duration) {
         RecordHistogram.recordTimesHistogram("Android.Pdf.DownloadTime.Transient", duration);
     }
diff --git a/chrome/browser/ui/android/toolbar/BUILD.gn b/chrome/browser/ui/android/toolbar/BUILD.gn
index b2e7142..3b4e3e9 100644
--- a/chrome/browser/ui/android/toolbar/BUILD.gn
+++ b/chrome/browser/ui/android/toolbar/BUILD.gn
@@ -14,7 +14,7 @@
     "java/src/org/chromium/chrome/browser/toolbar/ControlContainer.java",
     "java/src/org/chromium/chrome/browser/toolbar/CustomTabCount.java",
     "java/src/org/chromium/chrome/browser/toolbar/FormFieldFocusedSupplier.java",
-    "java/src/org/chromium/chrome/browser/toolbar/KeyboardAccessoryHeightSupplier.java",
+    "java/src/org/chromium/chrome/browser/toolbar/KeyboardAccessoryStateSupplier.java",
     "java/src/org/chromium/chrome/browser/toolbar/LocationBarFocusScrimHandler.java",
     "java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java",
     "java/src/org/chromium/chrome/browser/toolbar/MenuBuilderHelper.java",
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/KeyboardAccessoryHeightSupplier.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/KeyboardAccessoryStateSupplier.java
similarity index 78%
rename from chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/KeyboardAccessoryHeightSupplier.java
rename to chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/KeyboardAccessoryStateSupplier.java
index f111e4e..4df6bf4 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/KeyboardAccessoryHeightSupplier.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/KeyboardAccessoryStateSupplier.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.toolbar;
 
+import android.view.View;
+
 import org.chromium.base.Callback;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
@@ -13,10 +15,11 @@
 
 /**
  * Helper class that provides the current height of the keyboard accessory (sheet or bar), providing
- * 0 until the keyboard accessory's own supplier of its height is available.
+ * 0 until the keyboard accessory's own supplier of its height is available. Also provides the
+ * current visibility of the accessory sheet.
  */
 @NullMarked
-public class KeyboardAccessoryHeightSupplier extends ObservableSupplierImpl<Integer> {
+public class KeyboardAccessoryStateSupplier extends ObservableSupplierImpl<Integer> {
 
     private final Callback<ManualFillingComponent> mManualFillingAvailableCallback =
             this::onManualFillingComponentAvailable;
@@ -24,7 +27,7 @@
     private final Callback<Integer> mInsetChangeCallback = this::set;
     private @Nullable ManualFillingComponent mManualFillingComponent;
 
-    public KeyboardAccessoryHeightSupplier(
+    public KeyboardAccessoryStateSupplier(
             ObservableSupplier<ManualFillingComponent> manualFillingComponentSupplier) {
         super(0);
         mManualFillingComponentSupplier = manualFillingComponentSupplier;
@@ -46,4 +49,14 @@
             mManualFillingComponent.getBottomInsetSupplier().removeObserver(mInsetChangeCallback);
         }
     }
+
+    /**
+     * {@link ManualFillingComponent#isFillingViewShown(View)}
+     *
+     * @param view A {@link View} that is used to find the window root.
+     */
+    public boolean isSheetShowing(View view) {
+        if (mManualFillingComponent == null) return false;
+        return mManualFillingComponent.isFillingViewShown(view);
+    }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java
index 9889f40..46bbe6c 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java
@@ -6,9 +6,16 @@
 import android.content.Context;
 import android.view.Gravity;
 import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.widget.FrameLayout;
 
+import androidx.annotation.VisibleForTesting;
+import androidx.core.view.WindowInsetsAnimationCompat;
+import androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat;
+import androidx.core.view.WindowInsetsCompat;
+
 import org.chromium.base.Callback;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
@@ -18,8 +25,14 @@
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider.Observer;
 import org.chromium.chrome.browser.omnibox.LocationBar;
 import org.chromium.components.browser_ui.widget.TouchEventObserver;
+import org.chromium.ui.InsetObserver;
+import org.chromium.ui.InsetObserver.WindowInsetsAnimationListener;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.KeyboardVisibilityDelegate.KeyboardVisibilityListener;
+import org.chromium.ui.base.ViewUtils;
+
+import java.util.List;
+import java.util.function.BooleanSupplier;
 
 /**
  * Controller responsible for toggling the "mini origin" presentation of the browsing mode toolbar.
@@ -28,6 +41,7 @@
  */
 @NullMarked
 public class MiniOriginBarController implements Observer {
+
     private final LocationBar mLocationBar;
     private final ObservableSupplier<Boolean> mIsFormFieldFocusedSupplier;
     private final KeyboardVisibilityDelegate mKeyboardVisibilityDelegate;
@@ -37,11 +51,14 @@
     private final ControlContainer mControlContainer;
     private final ObservableSupplierImpl<Boolean> mSuppressToolbarSceneLayerSupplier;
     private final BrowserControlsSizer mBrowserControlsSizer;
+    private final BooleanSupplier mIsKeyboardAccessorySheetShowing;
+    private final MiniOriginWindowInsetsAnimationListener mWindowInsetsAnimationListener;
     private boolean mShowMiniOriginBar;
     private FrameLayout.LayoutParams mDefaultLocationBarLayoutParams;
     private boolean mOriginBarClickedInSession;
     private final TouchEventObserver mTouchEventObserver;
     private final int mDefaultLocationBarRightPadding;
+    private boolean mFormFieldFocusChanged;
 
     /**
      * @param locationBar LocationBar instance used to change the presentation of e.g. the UrlBar
@@ -60,7 +77,10 @@
             Context context,
             ControlContainer controlContainer,
             ObservableSupplierImpl<Boolean> suppressToolbarSceneLayerSupplier,
-            BrowserControlsSizer browserControlsSizer) {
+            BrowserControlsSizer browserControlsSizer,
+            InsetObserver insetObserver,
+            ObservableSupplierImpl<Integer> controlContainerTranslationSupplier,
+            BooleanSupplier isKeyboardAccessorySheetShowing) {
         mLocationBar = locationBar;
         mIsFormFieldFocusedSupplier = isFormFieldFocusedSupplier;
         mKeyboardVisibilityDelegate = keyboardVisibilityDelegate;
@@ -68,12 +88,25 @@
         mControlContainer = controlContainer;
         mSuppressToolbarSceneLayerSupplier = suppressToolbarSceneLayerSupplier;
         mBrowserControlsSizer = browserControlsSizer;
+        mIsKeyboardAccessorySheetShowing = isKeyboardAccessorySheetShowing;
         mDefaultLocationBarRightPadding = mLocationBar.getContainerView().getPaddingRight();
         mDefaultLocationBarLayoutParams =
                 (FrameLayout.LayoutParams) mLocationBar.getContainerView().getLayoutParams();
         mBrowserControlsSizer.addObserver(this);
+        mWindowInsetsAnimationListener =
+                new MiniOriginWindowInsetsAnimationListener(
+                        keyboardVisibilityDelegate,
+                        (ViewGroup) mLocationBar.getContainerView(),
+                        controlContainerTranslationSupplier,
+                        () -> mFormFieldFocusChanged = false,
+                        this::waitingForImeAnimationToStart);
+        insetObserver.addWindowInsetsAnimationListener(mWindowInsetsAnimationListener);
 
-        mIsFormFieldFocusedObserver = (focused) -> updateMiniOriginBarState();
+        mIsFormFieldFocusedObserver =
+                (focused) -> {
+                    mFormFieldFocusChanged = true;
+                    updateMiniOriginBarState();
+                };
         mKeyboardVisibilityObserver = (showing) -> updateMiniOriginBarState();
 
         mIsFormFieldFocusedSupplier.addObserver(mIsFormFieldFocusedObserver);
@@ -105,6 +138,7 @@
                         && isFormFieldFocused
                         && isKeyboardVisible;
         if (showMiniOriginBar == mShowMiniOriginBar) return;
+
         if (showMiniOriginBar) {
             mDefaultLocationBarLayoutParams =
                     (FrameLayout.LayoutParams) mLocationBar.getContainerView().getLayoutParams();
@@ -150,4 +184,77 @@
     public void onControlsPositionChanged(int controlsPosition) {
         updateMiniOriginBarState();
     }
+
+    @VisibleForTesting
+    boolean waitingForImeAnimationToStart() {
+        return mFormFieldFocusChanged && !mIsKeyboardAccessorySheetShowing.getAsBoolean();
+    }
+
+    @VisibleForTesting
+    static class MiniOriginWindowInsetsAnimationListener implements WindowInsetsAnimationListener {
+
+        private boolean mAnimationInProgress;
+        private int mFinalKeyboardHeight;
+        private final KeyboardVisibilityDelegate mKeyboardVisibilityDelegate;
+        private final ViewGroup mContainerView;
+        private final ObservableSupplierImpl<Integer> mTranslationSupplier;
+        private final Context mContext;
+        private final Runnable mAnimationStartedSignal;
+        private final BooleanSupplier mWaitingForAnimation;
+
+        MiniOriginWindowInsetsAnimationListener(
+                KeyboardVisibilityDelegate keyboardVisibilityDelegate,
+                ViewGroup containerView,
+                ObservableSupplierImpl<Integer> translationSupplier,
+                Runnable animationStartedSignal,
+                BooleanSupplier waitingForAnimation) {
+            mKeyboardVisibilityDelegate = keyboardVisibilityDelegate;
+            mContainerView = containerView;
+            mTranslationSupplier = translationSupplier;
+            mContext = containerView.getContext();
+            mAnimationStartedSignal = animationStartedSignal;
+            mWaitingForAnimation = waitingForAnimation;
+        }
+
+        @Override
+        public void onPrepare(WindowInsetsAnimationCompat animation) {}
+
+        @Override
+        public void onStart(WindowInsetsAnimationCompat animation, BoundsCompat bounds) {
+            if (!mWaitingForAnimation.getAsBoolean()
+                    || ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) == 0)) {
+                return;
+            }
+
+            mAnimationStartedSignal.run();
+            // Prevent clipping so that the mini origin bar can draw in bounds allocated for the
+            // keyboard; we will prevent overlap by syncing our translation to its movement in
+            // onProgress.
+            ViewUtils.setAncestorsShouldClipChildren(mContainerView, false, View.NO_ID);
+            ViewUtils.setAncestorsShouldClipToPadding(mContainerView, false, View.NO_ID);
+            mAnimationInProgress = true;
+            mFinalKeyboardHeight =
+                    mKeyboardVisibilityDelegate.isKeyboardShowing(mContext, mContainerView)
+                            ? bounds.getUpperBound().bottom
+                            : 0;
+        }
+
+        @Override
+        public void onProgress(
+                WindowInsetsCompat windowInsetsCompat, List<WindowInsetsAnimationCompat> list) {
+            if (!mAnimationInProgress) return;
+            mTranslationSupplier.set(
+                    mFinalKeyboardHeight
+                            - windowInsetsCompat.getInsets(WindowInsetsCompat.Type.ime()).bottom);
+        }
+
+        @Override
+        public void onEnd(WindowInsetsAnimationCompat animation) {
+            if (!mAnimationInProgress) return;
+            mAnimationInProgress = false;
+            ViewUtils.setAncestorsShouldClipChildren(mContainerView, true, ViewGroup.NO_ID);
+            ViewUtils.setAncestorsShouldClipToPadding(mContainerView, true, ViewGroup.NO_ID);
+            mTranslationSupplier.set(0);
+        }
+    }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarControllerTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarControllerTest.java
index ff41fed..6d18da7 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarControllerTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarControllerTest.java
@@ -14,12 +14,15 @@
 import android.content.Context;
 import android.view.Gravity;
 import android.view.MotionEvent;
-import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
 import androidx.coordinatorlayout.widget.CoordinatorLayout;
 import androidx.coordinatorlayout.widget.CoordinatorLayout.LayoutParams;
+import androidx.core.graphics.Insets;
+import androidx.core.view.WindowInsetsAnimationCompat;
+import androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat;
+import androidx.core.view.WindowInsetsCompat;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -38,7 +41,12 @@
 import org.chromium.chrome.browser.browser_controls.BrowserControlsSizer;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider.ControlsPosition;
 import org.chromium.chrome.browser.omnibox.LocationBar;
+import org.chromium.chrome.browser.toolbar.MiniOriginBarController.MiniOriginWindowInsetsAnimationListener;
 import org.chromium.components.browser_ui.widget.TouchEventObserver;
+import org.chromium.ui.InsetObserver;
+
+import java.util.Collections;
+import java.util.function.BooleanSupplier;
 
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
@@ -47,8 +55,9 @@
 
     @Mock private ControlContainer mControlContainer;
     @Mock private LocationBar mLocationBar;
-    @Mock private View mLocationBarView;
+    @Mock private ViewGroup mLocationBarView;
     @Mock private BrowserControlsSizer mBrowserControlsSizer;
+    @Mock private InsetObserver mInsetObserver;
     @Captor ArgumentCaptor<TouchEventObserver> mTouchEventObserverCaptor;
     @Captor private ArgumentCaptor<FrameLayout.LayoutParams> mLayoutParamsCaptor;
 
@@ -64,6 +73,16 @@
     private MiniOriginBarController mMiniOriginBarController;
     private final ObservableSupplierImpl<Boolean> mSuppressToolbarSceneLayerSupplier =
             new ObservableSupplierImpl<>(false);
+    ObservableSupplierImpl<Integer> mControlContainerTranslationSupplier =
+            new ObservableSupplierImpl<>(0);
+    private boolean mIsSheetShowing;
+    BooleanSupplier mIsKeyboardAccessorySheetShowing = () -> mIsSheetShowing;
+
+    private final WindowInsetsAnimationCompat mImeAnimation =
+            new WindowInsetsAnimationCompat(WindowInsetsCompat.Type.ime(), null, 160);
+    private final WindowInsetsAnimationCompat mNonImeAnimation =
+            new WindowInsetsAnimationCompat(WindowInsetsCompat.Type.systemBars(), null, 160);
+    private boolean mAnimationStarted;
 
     @Before
     public void setUp() {
@@ -81,7 +100,10 @@
                         mContext,
                         mControlContainer,
                         mSuppressToolbarSceneLayerSupplier,
-                        mBrowserControlsSizer);
+                        mBrowserControlsSizer,
+                        mInsetObserver,
+                        mControlContainerTranslationSupplier,
+                        mIsKeyboardAccessorySheetShowing);
     }
 
     @Test
@@ -145,4 +167,164 @@
         mIsFormFieldFocused.onNodeAttributeUpdated(true, false);
         assertTrue(observer.onInterceptTouchEvent(clickEvent));
     }
+
+    @Test
+    public void testAnimateWithKeyboard_notReadyForAnimation() {
+        final MiniOriginWindowInsetsAnimationListener animationListener =
+                new MiniOriginWindowInsetsAnimationListener(
+                        mKeyboardVisibilityDelegate,
+                        mLocationBarView,
+                        mControlContainerTranslationSupplier,
+                        () -> mAnimationStarted = true,
+                        mMiniOriginBarController::waitingForImeAnimationToStart);
+        final BoundsCompat bounds = new BoundsCompat(Insets.NONE, Insets.of(0, 0, 0, 40));
+
+        animationListener.onStart(mImeAnimation, bounds);
+        assertFalse(mAnimationStarted);
+
+        mIsFormFieldFocused.onNodeAttributeUpdated(true, false);
+        animationListener.onStart(mImeAnimation, bounds);
+        assertTrue(mAnimationStarted);
+
+        animationListener.onEnd(mImeAnimation);
+        mAnimationStarted = false;
+        mIsSheetShowing = true;
+        animationListener.onStart(mImeAnimation, bounds);
+        assertFalse(mAnimationStarted);
+
+        mIsSheetShowing = false;
+        animationListener.onStart(mNonImeAnimation, bounds);
+        assertFalse(mAnimationStarted);
+    }
+
+    @Test
+    public void testAnimateWithKeyboard() {
+        final MiniOriginWindowInsetsAnimationListener animationListener =
+                new MiniOriginWindowInsetsAnimationListener(
+                        mKeyboardVisibilityDelegate,
+                        mLocationBarView,
+                        mControlContainerTranslationSupplier,
+                        () -> mAnimationStarted = true,
+                        mMiniOriginBarController::waitingForImeAnimationToStart);
+        final int finalKeyboardHeight = 100;
+        final BoundsCompat bounds =
+                new BoundsCompat(Insets.NONE, Insets.of(0, 0, 0, finalKeyboardHeight));
+
+        mIsFormFieldFocused.onNodeAttributeUpdated(true, false);
+        mKeyboardVisibilityDelegate.setVisibilityForTests(true);
+
+        animationListener.onStart(mImeAnimation, bounds);
+        assertTrue(mAnimationStarted);
+
+        int currentKeyboardHeight = 10;
+        WindowInsetsCompat insets =
+                new WindowInsetsCompat.Builder()
+                        .setInsets(
+                                WindowInsetsCompat.Type.ime(),
+                                Insets.of(0, 0, 0, currentKeyboardHeight))
+                        .build();
+        animationListener.onProgress(insets, Collections.singletonList(mImeAnimation));
+        assertEquals(
+                finalKeyboardHeight - currentKeyboardHeight,
+                (int) mControlContainerTranslationSupplier.get());
+
+        currentKeyboardHeight = 40;
+        insets =
+                new WindowInsetsCompat.Builder()
+                        .setInsets(
+                                WindowInsetsCompat.Type.ime(),
+                                Insets.of(0, 0, 0, currentKeyboardHeight))
+                        .build();
+        animationListener.onProgress(insets, Collections.singletonList(mImeAnimation));
+        assertEquals(
+                finalKeyboardHeight - currentKeyboardHeight,
+                (int) mControlContainerTranslationSupplier.get());
+
+        currentKeyboardHeight = 90;
+        insets =
+                new WindowInsetsCompat.Builder()
+                        .setInsets(
+                                WindowInsetsCompat.Type.ime(),
+                                Insets.of(0, 0, 0, currentKeyboardHeight))
+                        .build();
+        animationListener.onProgress(insets, Collections.singletonList(mImeAnimation));
+        assertEquals(
+                finalKeyboardHeight - currentKeyboardHeight,
+                (int) mControlContainerTranslationSupplier.get());
+
+        animationListener.onEnd(mImeAnimation);
+        assertEquals(0, (int) mControlContainerTranslationSupplier.get());
+
+        // Simulate hiding the keyboard
+        mIsFormFieldFocused.onNodeAttributeUpdated(false, false);
+        mKeyboardVisibilityDelegate.setVisibilityForTests(false);
+        mAnimationStarted = false;
+
+        animationListener.onStart(mImeAnimation, bounds);
+        assertTrue(mAnimationStarted);
+
+        animationListener.onProgress(insets, Collections.singletonList(mImeAnimation));
+        assertEquals(-currentKeyboardHeight, (int) mControlContainerTranslationSupplier.get());
+
+        currentKeyboardHeight = 40;
+        insets =
+                new WindowInsetsCompat.Builder()
+                        .setInsets(
+                                WindowInsetsCompat.Type.ime(),
+                                Insets.of(0, 0, 0, currentKeyboardHeight))
+                        .build();
+        animationListener.onProgress(insets, Collections.singletonList(mImeAnimation));
+        assertEquals(-currentKeyboardHeight, (int) mControlContainerTranslationSupplier.get());
+
+        animationListener.onEnd(mImeAnimation);
+        assertEquals(0, (int) mControlContainerTranslationSupplier.get());
+    }
+
+    @Test
+    public void testAnimateWithKeyboard_animationFinishesInStartingState() {
+        // Predictive back gestures can cause an IME hide animation to run but finish with the IME
+        // still showing if the gesture is cancelled.
+
+        final MiniOriginWindowInsetsAnimationListener animationListener =
+                new MiniOriginWindowInsetsAnimationListener(
+                        mKeyboardVisibilityDelegate,
+                        mLocationBarView,
+                        mControlContainerTranslationSupplier,
+                        () -> mAnimationStarted = true,
+                        mMiniOriginBarController::waitingForImeAnimationToStart);
+        final int finalKeyboardHeight = 100;
+        final BoundsCompat bounds =
+                new BoundsCompat(Insets.NONE, Insets.of(0, 0, 0, finalKeyboardHeight));
+
+        mIsFormFieldFocused.onNodeAttributeUpdated(true, false);
+        mKeyboardVisibilityDelegate.setVisibilityForTests(true);
+
+        animationListener.onStart(mImeAnimation, bounds);
+        animationListener.onEnd(mImeAnimation);
+        assertEquals(0, (int) mControlContainerTranslationSupplier.get());
+
+        // Simulate the beginning of an animation hiding the keyboard
+
+        mKeyboardVisibilityDelegate.setVisibilityForTests(false);
+        mAnimationStarted = false;
+
+        animationListener.onStart(mImeAnimation, bounds);
+        assertTrue(mAnimationStarted);
+
+        int currentKeyboardHeight = 90;
+        WindowInsetsCompat insets =
+                new WindowInsetsCompat.Builder()
+                        .setInsets(
+                                WindowInsetsCompat.Type.ime(),
+                                Insets.of(0, 0, 0, currentKeyboardHeight))
+                        .build();
+        animationListener.onProgress(insets, Collections.singletonList(mImeAnimation));
+        assertEquals(-currentKeyboardHeight, (int) mControlContainerTranslationSupplier.get());
+
+        mKeyboardVisibilityDelegate.setVisibilityForTests(true);
+        animationListener.onEnd(mImeAnimation);
+        assertEquals(0, (int) mControlContainerTranslationSupplier.get());
+    }
+
+    // show again, start, finish showing (predictive back)
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java
index 11400d9..e750da80 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java
@@ -87,6 +87,7 @@
     private final KeyboardVisibilityDelegate mKeyboardVisibilityDelegate;
     private final Context mContext;
     private final ObservableSupplier<Integer> mKeyboardAccessoryHeightSupplier;
+    private final ObservableSupplier<Integer> mControlContainerTranslationSupplier;
     @LayerVisibility private int mLayerVisibility;
     private final BottomControlsLayerWithOffset mBottomToolbarLayer;
     private final BottomControlsLayerWithOffset mProgressBarLayer;
@@ -128,6 +129,7 @@
             BottomControlsStacker bottomControlsStacker,
             ObservableSupplierImpl<Integer> browserControlsOffsetSupplier,
             View toolbarProgressBarContainer,
+            ObservableSupplier<Integer> controlContainerTranslationSupplier,
             Context context) {
         mBrowserControlsSizer = browserControlsSizer;
         mIsNtpShowingSupplier = isNtpShowingSupplier;
@@ -141,6 +143,7 @@
         mBottomControlsStacker = bottomControlsStacker;
         mBrowserControlsOffsetSupplier = browserControlsOffsetSupplier;
         mToolbarProgressBarContainer = toolbarProgressBarContainer;
+        mControlContainerTranslationSupplier = controlContainerTranslationSupplier;
         mContext = context;
         mCurrentPosition = mBrowserControlsSizer.getControlsPosition();
 
@@ -244,6 +247,8 @@
                 (showing) -> updateViewOffset(mBottomToolbarLayer, mControlContainer.getView()));
         mIsFormFieldFocusedSupplier.addObserver(
                 (focused) -> updateViewOffset(mProgressBarLayer, mToolbarProgressBarContainer));
+        mControlContainerTranslationSupplier.addObserver(
+                (offset) -> updateViewOffset(mBottomToolbarLayer, mControlContainer.getView()));
         updateCurrentPosition();
     }
 
@@ -450,7 +455,10 @@
     private void updateViewOffset(BottomControlsLayerWithOffset layer, View viewForLayer) {
         if (mLayerVisibility != LayerVisibility.VISIBLE) return;
 
-        int layerYOffset = layer.getLayerOffsetPx() - mKeyboardAccessoryHeightSupplier.get();
+        int layerYOffset =
+                layer.getLayerOffsetPx()
+                        - mKeyboardAccessoryHeightSupplier.get()
+                        + mControlContainerTranslationSupplier.get();
         viewForLayer.setTranslationY(layerYOffset);
     }
 
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java
index 64a9ae1..5e44126 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java
@@ -251,6 +251,8 @@
             new ObservableSupplierImpl<>();
     private final ObservableSupplierImpl<Integer> mKeyboardAccessoryHeightSupplier =
             new ObservableSupplierImpl<>(0);
+    private final ObservableSupplierImpl<Integer> mControlContainerTranslationSupplier =
+            new ObservableSupplierImpl<>(0);
     private HistogramWatcher mStartupExpectation;
     private WindowAndroid mWindowAndroid;
 
@@ -314,6 +316,7 @@
                         mBottomControlsStacker,
                         mBottomToolbarOffsetSupplier,
                         mProgressBarContainer,
+                        mControlContainerTranslationSupplier,
                         mContext);
     }
 
@@ -791,7 +794,7 @@
 
     @Test
     @EnableFeatures({ChromeFeatureList.ANDROID_BOTTOM_TOOLBAR, ChromeFeatureList.MINI_ORIGIN_BAR})
-    public void testKeyboardAccessoryHeight() {
+    public void testControlContainerTranslationAdjustments() {
         setUserToolbarAnchorPreference(/* showToolbarOnTop= */ false);
         mIsFormFieldFocused.onNodeAttributeUpdated(true, false);
         mKeyboardVisibilityDelegate.setVisibilityForTests(true);
@@ -808,6 +811,12 @@
 
         mKeyboardAccessoryHeightSupplier.set(0);
         verify(mControlContainerView, times(2)).setTranslationY(12);
+
+        mControlContainerTranslationSupplier.set(10);
+        verify(mControlContainerView).setTranslationY(22);
+
+        mControlContainerTranslationSupplier.set(20);
+        verify(mControlContainerView).setTranslationY(32);
     }
 
     private void assertControlsAtBottom() {
diff --git a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/Snackbar.java b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/Snackbar.java
index ec1620cd..0be21837 100644
--- a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/Snackbar.java
+++ b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/Snackbar.java
@@ -129,6 +129,7 @@
     public static final int UMA_REVOKE_FILE_EDIT_GRANT = 72;
     public static final int UMA_SEARCH_ENGINE_CHANGED_NOTIFICATION = 73;
     public static final int UMA_BOOKMARK_BATCH_UPLOAD = 74;
+    public static final int UMA_NTP_MOST_VISITED_UNPIN_UNDO = 75;
 
     private final @Nullable SnackbarController mController;
     private final CharSequence mText;
diff --git a/chrome/browser/ui/tabs/tab_strip_api/BUILD.gn b/chrome/browser/ui/tabs/tab_strip_api/BUILD.gn
index 7c9c2a4..54d004a 100644
--- a/chrome/browser/ui/tabs/tab_strip_api/BUILD.gn
+++ b/chrome/browser/ui/tabs/tab_strip_api/BUILD.gn
@@ -10,6 +10,7 @@
   sources = [ "tab_strip_api.mojom" ]
 
   public_deps = [
+    "//chrome/browser/ui/webui/tabs:mojo_bindings",
     "//mojo/public/mojom/base",
     "//url/mojom:url_mojom_gurl",
   ]
@@ -27,25 +28,13 @@
           mojom = "tabs_api.mojom.TabId.Type"
           cpp = "enum ::tabs_api::TabId::Type"
         },
-        {
-          mojom = "tabs_api.mojom.TabAlertState"
-          cpp = "::tabs::TabAlert"
-        },
-        {
-          mojom = "tabs_api.mojom.TabNetworkState"
-          cpp = "::TabNetworkState"
-        },
       ]
       traits_headers = [
         "//chrome/browser/ui/tabs/tab_strip_api/tab_id.h",
         "//chrome/browser/ui/tabs/tab_strip_api/tab_id_traits.h",
-        "//chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.h",
-        "//chrome/browser/ui/tabs/alert/tab_alert.h",
       ]
-      traits_sources = [
-        "//chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.cc",
-        "//chrome/browser/ui/tabs/tab_strip_api/tab_id_traits.cc",
-      ]
+      traits_sources =
+          [ "//chrome/browser/ui/tabs/tab_strip_api/tab_id_traits.cc" ]
       traits_public_deps = [
         ":types",
         "//base",
diff --git a/chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.h b/chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.h
deleted file mode 100644
index 2e52a0a8..0000000
--- a/chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_TABS_TAB_STRIP_API_TAB_ENUM_TRAITS_H_
-#define CHROME_BROWSER_UI_TABS_TAB_STRIP_API_TAB_ENUM_TRAITS_H_
-
-#include "chrome/browser/ui/tabs/alert/tab_alert.h"
-#include "chrome/browser/ui/tabs/tab_enums.h"
-#include "chrome/browser/ui/tabs/tab_network_state.h"
-#include "chrome/browser/ui/tabs/tab_strip_api/tab_strip_api.mojom.h"
-#include "mojo/public/cpp/bindings/enum_traits.h"
-#include "mojo/public/cpp/bindings/struct_traits.h"
-
-using MojoTabNetworkState = tabs_api::mojom::TabNetworkState;
-using NativeTabNetworkState = enum TabNetworkState;
-
-// TabNetworkState Enum mapping.
-template <>
-struct mojo::EnumTraits<MojoTabNetworkState, NativeTabNetworkState> {
-  static MojoTabNetworkState ToMojom(NativeTabNetworkState input);
-  static bool FromMojom(MojoTabNetworkState in, NativeTabNetworkState* out);
-};
-
-using MojoTabAlertState = tabs_api::mojom::TabAlertState;
-using NativeTabAlertState = enum tabs::TabAlert;
-
-// TabAlertState Enum mapping.
-template <>
-struct mojo::EnumTraits<MojoTabAlertState, NativeTabAlertState> {
-  static MojoTabAlertState ToMojom(NativeTabAlertState input);
-  static bool FromMojom(MojoTabAlertState in, NativeTabAlertState* out);
-};
-
-#endif  // CHROME_BROWSER_UI_TABS_TAB_STRIP_API_TAB_ENUM_TRAITS_H_
diff --git a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_api.mojom b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_api.mojom
index a7591fc0..cc01d5d5 100644
--- a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_api.mojom
+++ b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_api.mojom
@@ -4,38 +4,10 @@
 
 module tabs_api.mojom;
 
+import "chrome/browser/ui/webui/tabs/tabs.mojom";
 import "mojo/public/mojom/base/error.mojom";
 import "url/mojom/url.mojom";
 
-// In C++, this enum is typemapped to
-// "//chrome/browser/ui/tabs/tab_enums.h"
-enum TabAlertState {
-  kMediaRecording,
-  kTabCapturing,
-  kAudioPlaying,
-  kAudioMuting,
-  kBluetoothConnected,
-  kBluetoothScanActive,
-  kUsbConnected,
-  kHidConnected,
-  kSerialConnected,
-  kPipPlaying,
-  kDesktopCapturing,
-  kVrPresentingInHeadset,
-  kAudioRecording,
-  kVideoRecording,
-  kGlicAccessing,
-};
-
-// In C++, this enum is typemapped to
-// "//chrome/browser/ui/tabs/tab_network_state.h"
-enum TabNetworkState {
-  kNone,
-  kWaiting,
-  kLoading,
-  kError,
-};
-
 // References a tab object. The id can refer to different types of underlying
 // resource. The |type| field can be used to differentiate between the type
 // of underlying resources.
@@ -69,8 +41,8 @@
   // in c++. Leave this as a data uri for now.
   url.mojom.Url favicon_url;
 
-  array<TabAlertState> alert_states;
-  TabNetworkState network_state;
+  array<tabs.mojom.TabAlertState> alert_states;
+  tabs.mojom.TabNetworkState network_state;
 };
 
 // Position is an ephemeral object that should not be saved nor act as an
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_context_menu_unittest.cc b/chrome/browser/ui/views/bookmarks/bookmark_context_menu_unittest.cc
index a4715dc..50182f8 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_context_menu_unittest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_context_menu_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/with_feature_override.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
@@ -31,10 +32,12 @@
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/browser/bookmark_node.h"
 #include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/bookmarks/managed/managed_bookmark_service.h"
 #include "components/bookmarks/test/bookmark_test_helpers.h"
 #include "components/prefs/pref_service.h"
+#include "components/signin/public/base/signin_switches.h"
 #include "content/public/browser/page_navigator.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -57,10 +60,13 @@
 using content::PageNavigator;
 using content::WebContents;
 
-class BookmarkContextMenuTest : public testing::Test {
+class BookmarkContextMenuTest : public testing::Test,
+                                public base::test::WithFeatureOverride {
  public:
-  BookmarkContextMenuTest() : model_(nullptr) {
-    feature_list_.InitWithFeatures({features::kSideBySide}, {});
+  BookmarkContextMenuTest()
+      : base::test::WithFeatureOverride(
+            switches::kSyncEnableBookmarksInTransportMode) {
+    feature_list_.InitAndEnableFeature(features::kSideBySide);
   }
 
   void SetUp() override {
@@ -75,11 +81,17 @@
         BookmarkMergedSurfaceServiceFactory::GetInstance(),
         BookmarkMergedSurfaceServiceFactory::GetDefaultFactory());
     profile_ = profile_builder.Build();
-    model_ = BookmarkModelFactory::GetForBrowserContext(profile_.get());
     WaitForBookmarkMergedSurfaceServiceToLoad(
         BookmarkMergedSurfaceServiceFactory::GetForProfile(profile_.get()));
+    model_ = BookmarkMergedSurfaceServiceFactory::GetForProfile(profile_.get())
+                 ->bookmark_model();
     AddTestData();
 
+    if (SyncEnableBookmarksInTransportModeEnabled()) {
+      model_->CreateAccountPermanentFolders();
+      AddAccountTestData();
+    }
+
     chrome::BookmarkNavigationWrapper::SetInstanceForTesting(&wrapper_);
 
     // CutCopyPasteNode executes IDC_COPY and IDC_CUT commands.
@@ -87,16 +99,23 @@
   }
 
   void TearDown() override {
+    if (SyncEnableBookmarksInTransportModeEnabled()) {
+      model_->RemoveAccountPermanentFolders();
+    }
     ui::Clipboard::DestroyClipboardForCurrentThread();
   }
 
  protected:
+  bool SyncEnableBookmarksInTransportModeEnabled() {
+    return IsParamFeatureEnabled();
+  }
+
   content::BrowserTaskEnvironment task_environment_;
   views::ScopedViewsTestHelper views_test_helper_;
+  base::test::ScopedFeatureList feature_list_;
   std::unique_ptr<TestingProfile> profile_;
   raw_ptr<BookmarkModel> model_;
   TestingBookmarkNavigationWrapper wrapper_;
-  base::test::ScopedFeatureList feature_list_;
 
  private:
   // Creates the following structure:
@@ -124,16 +143,38 @@
     const BookmarkNode* f4 = model_->AddFolder(bb_node, 4, u"F4");
     model_->AddURL(f4, 0, u"f4a", GURL(test_base + "f4a"));
   }
+
+  // Creates the following structure:
+  // acc_a
+  // acc_F1
+  //  acc_f1a
+  //  acc_F11
+  //   acc_f11a
+  // acc_F2
+  void AddAccountTestData() {
+    CHECK(SyncEnableBookmarksInTransportModeEnabled());
+    const BookmarkNode* bb_node = model_->account_bookmark_bar_node();
+    std::string test_base = "file:///c:/tmp/";
+    model_->AddURL(bb_node, 0, u"acc_a", GURL(test_base + "acc_a"));
+    const BookmarkNode* f1 = model_->AddFolder(bb_node, 1, u"acc_F1");
+    model_->AddURL(f1, 0, u"acc_f1a", GURL(test_base + "acc_f1a"));
+    const BookmarkNode* f11 = model_->AddFolder(f1, 1, u"acc_F11");
+    model_->AddURL(f11, 0, u"acc_f11a", GURL(test_base + "acc_f11a"));
+    model_->AddFolder(bb_node, 2, u"acc_F2");
+  }
 };
 
-// Tests Deleting from the menu.
-TEST_F(BookmarkContextMenuTest, DeleteURL) {
+// Tests deleting from the menu.
+TEST_P(BookmarkContextMenuTest, DeleteURL) {
+  const BookmarkNode* node_to_delete =
+      SyncEnableBookmarksInTransportModeEnabled()
+          ? model_->account_bookmark_bar_node()->children().front().get()
+          : model_->bookmark_bar_node()->children().front().get();
   std::vector<raw_ptr<const BookmarkNode, VectorExperimental>> nodes = {
-      model_->bookmark_bar_node()->children().front().get(),
-  };
+      node_to_delete};
   BookmarkContextMenu controller(nullptr, nullptr, profile_.get(),
                                  BookmarkLaunchLocation::kNone, nodes, false);
-  GURL url = model_->bookmark_bar_node()->children().front()->url();
+  GURL url = node_to_delete->url();
   ASSERT_TRUE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE));
   // Delete the URL.
   controller.ExecuteCommand(IDC_BOOKMARK_BAR_REMOVE, 0);
@@ -142,26 +183,31 @@
 }
 
 // Tests counting tabs for 'open all' on a folder with a couple of bookmarks.
-TEST_F(BookmarkContextMenuTest, OpenCount) {
+TEST_P(BookmarkContextMenuTest, OpenCount) {
   const BookmarkNode* folder = model_->bookmark_bar_node()->children()[1].get();
-
   // Should count F1's child but not F11's child, as that's what OpenAll would
   // open.
   EXPECT_EQ(2, chrome::OpenCount(gfx::NativeWindow(), folder));
+
+  if (SyncEnableBookmarksInTransportModeEnabled()) {
+    folder = model_->account_bookmark_bar_node()->children()[1].get();
+    // Should count acc_F1's child but not acc_F11's child.
+    EXPECT_EQ(1, chrome::OpenCount(gfx::NativeWindow(), folder));
+  }
 }
 
 // Same as above, but for counting bookmarks that would be opened in an
 // incognito window.
-TEST_F(BookmarkContextMenuTest, OpenCountIncognito) {
+TEST_P(BookmarkContextMenuTest, OpenCountIncognito) {
   const BookmarkNode* folder = model_->bookmark_bar_node()->children()[1].get();
 
-  // Should count f1a but not f2a, as that's what OpenAll would open.
+  // Should count f1a but not f1b, as that's what OpenAll would open.
   EXPECT_EQ(1, chrome::OpenCount(gfx::NativeWindow(), folder, profile_.get()));
 }
 
 // Tests the enabled state of the menus when supplied a vector with a single
 // url.
-TEST_F(BookmarkContextMenuTest, SingleURL) {
+TEST_P(BookmarkContextMenuTest, SingleURL) {
   std::vector<raw_ptr<const BookmarkNode, VectorExperimental>> nodes = {
       model_->bookmark_bar_node()->children().front().get(),
   };
@@ -179,7 +225,7 @@
 
 // Tests the enabled state of the menus when supplied a vector with multiple
 // urls.
-TEST_F(BookmarkContextMenuTest, MultipleURLs) {
+TEST_P(BookmarkContextMenuTest, MultipleURLs) {
   std::vector<raw_ptr<const BookmarkNode, VectorExperimental>> nodes = {
       model_->bookmark_bar_node()->children()[0].get(),
       model_->bookmark_bar_node()->children()[1]->children()[0].get(),
@@ -197,7 +243,7 @@
 
 // Tests the enabled state of the menus when supplied an vector with a single
 // folder.
-TEST_F(BookmarkContextMenuTest, SingleFolder) {
+TEST_P(BookmarkContextMenuTest, SingleFolder) {
   std::vector<raw_ptr<const BookmarkNode, VectorExperimental>> nodes = {
       model_->bookmark_bar_node()->children()[2].get(),
   };
@@ -216,7 +262,7 @@
 
 // Tests the enabled state of the menus when supplied a vector with multiple
 // folders, all of which are empty.
-TEST_F(BookmarkContextMenuTest, MultipleEmptyFolders) {
+TEST_P(BookmarkContextMenuTest, MultipleEmptyFolders) {
   std::vector<raw_ptr<const BookmarkNode, VectorExperimental>> nodes = {
       model_->bookmark_bar_node()->children()[2].get(),
       model_->bookmark_bar_node()->children()[3].get(),
@@ -235,7 +281,7 @@
 
 // Tests the enabled state of the menus when supplied a vector with multiple
 // folders, some of which contain URLs.
-TEST_F(BookmarkContextMenuTest, MultipleFoldersWithURLs) {
+TEST_P(BookmarkContextMenuTest, MultipleFoldersWithURLs) {
   std::vector<raw_ptr<const BookmarkNode, VectorExperimental>> nodes = {
       model_->bookmark_bar_node()->children()[3].get(),
       model_->bookmark_bar_node()->children()[4].get(),
@@ -252,7 +298,7 @@
 }
 
 // Tests the enabled state of open incognito.
-TEST_F(BookmarkContextMenuTest, DisableIncognito) {
+TEST_P(BookmarkContextMenuTest, DisableIncognito) {
   std::vector<raw_ptr<const BookmarkNode, VectorExperimental>> nodes = {
       model_->bookmark_bar_node()->children().front().get(),
   };
@@ -266,7 +312,7 @@
 }
 
 // Tests that you can't remove/edit when showing the other node.
-TEST_F(BookmarkContextMenuTest, DisabledItemsWithOtherNode) {
+TEST_P(BookmarkContextMenuTest, DisabledItemsWithOtherNode) {
   std::vector<raw_ptr<const BookmarkNode, VectorExperimental>> nodes = {
       model_->other_node()};
   BookmarkContextMenu controller(nullptr, nullptr, profile_.get(),
@@ -275,42 +321,140 @@
   EXPECT_FALSE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE));
 }
 
-TEST_F(BookmarkContextMenuTest, CutCopyPasteNode) {
+// Tests the Cut/Copy/Paste commands.
+TEST_P(BookmarkContextMenuTest, CutCopyPasteNodeWithoutAccountNodes) {
+  // Skip this test if account folders are created.
+  if (SyncEnableBookmarksInTransportModeEnabled()) {
+    GTEST_SKIP();
+  }
+
   const BookmarkNode* bb_node = model_->bookmark_bar_node();
-  std::vector<raw_ptr<const BookmarkNode, VectorExperimental>> nodes = {
-      bb_node->children()[0].get()};
-  std::unique_ptr<BookmarkContextMenu> controller(
-      new BookmarkContextMenu(nullptr, nullptr, profile_.get(),
-                              BookmarkLaunchLocation::kNone, nodes, false));
-  EXPECT_TRUE(controller->IsCommandEnabled(IDC_COPY));
-  EXPECT_TRUE(controller->IsCommandEnabled(IDC_CUT));
+  size_t original_count = bb_node->children().size();
+  ASSERT_EQ(original_count, 5u);
 
-  // Copy the URL.
-  controller->ExecuteCommand(IDC_COPY, 0);
+  // Select the first bookmark (f1a).
+  std::vector<raw_ptr<const BookmarkNode, VectorExperimental>> selected_nodes =
+      {bb_node->children()[0].get()};
 
-  controller = std::make_unique<BookmarkContextMenu>(
-      nullptr, nullptr, profile_.get(), BookmarkLaunchLocation::kNone, nodes,
-      false);
-  size_t old_count = bb_node->children().size();
-  controller->ExecuteCommand(IDC_PASTE, 0);
+  // Test the Copy command.
+  {
+    BookmarkContextMenu controller(nullptr, nullptr, profile_.get(),
+                                   BookmarkLaunchLocation::kNone,
+                                   selected_nodes, false);
 
-  ASSERT_TRUE(bb_node->children()[1]->is_url());
-  ASSERT_EQ(old_count + 1, bb_node->children().size());
-  ASSERT_EQ(bb_node->children()[0]->url(), bb_node->children()[1]->url());
+    ASSERT_TRUE(controller.IsCommandEnabled(IDC_COPY));
+    controller.ExecuteCommand(IDC_COPY, 0);
 
-  controller = std::make_unique<BookmarkContextMenu>(
-      nullptr, nullptr, profile_.get(), BookmarkLaunchLocation::kNone, nodes,
-      false);
-  // Cut the URL.
-  controller->ExecuteCommand(IDC_CUT, 0);
-  ASSERT_TRUE(bb_node->children()[0]->is_url());
-  ASSERT_TRUE(bb_node->children()[1]->is_folder());
-  ASSERT_EQ(old_count, bb_node->children().size());
+    // No change after copy.
+    EXPECT_EQ(original_count, bb_node->children().size());
+  }
+
+  // Test the Paste command.
+  {
+    BookmarkContextMenu controller(nullptr, nullptr, profile_.get(),
+                                   BookmarkLaunchLocation::kNone,
+                                   selected_nodes, false);
+
+    ASSERT_TRUE(controller.IsCommandEnabled(IDC_PASTE));
+    controller.ExecuteCommand(IDC_PASTE, 0);
+
+    // The newly pasted bookmark should be inserted after the copied bookmark.
+    EXPECT_EQ(original_count + 1, bb_node->children().size());
+    EXPECT_TRUE(bb_node->children()[1]->is_url());
+    EXPECT_EQ(bb_node->children()[0]->url(), bb_node->children()[1]->url());
+  }
+
+  // Test the Cut command.
+  {
+    BookmarkContextMenu controller(nullptr, nullptr, profile_.get(),
+                                   BookmarkLaunchLocation::kNone,
+                                   selected_nodes, false);
+
+    ASSERT_TRUE(controller.IsCommandEnabled(IDC_CUT));
+    controller.ExecuteCommand(IDC_CUT, 0);
+
+    EXPECT_TRUE(bb_node->children()[0]->is_url());
+    EXPECT_TRUE(bb_node->children()[1]->is_folder());
+    EXPECT_EQ(original_count, bb_node->children().size());
+  }
+}
+
+// Tests the Cut/Copy/Paste commands when account nodes are created.
+TEST_P(BookmarkContextMenuTest, CutCopyPasteNodeWithAccountNodes) {
+  // This test requires account nodes to be created.
+  if (!SyncEnableBookmarksInTransportModeEnabled()) {
+    GTEST_SKIP();
+  }
+
+  const BookmarkNode* bb_node = model_->bookmark_bar_node();
+  const BookmarkNode* account_bb_node = model_->account_bookmark_bar_node();
+  size_t original_local_count = bb_node->children().size();
+  size_t original_account_count = account_bb_node->children().size();
+  ASSERT_EQ(original_local_count, 5u);
+  ASSERT_EQ(original_account_count, 3u);
+
+  // Test the Copy command.
+  {
+    // Select the first local bookmark (f1a).
+    std::vector<raw_ptr<const BookmarkNode, VectorExperimental>>
+        selected_nodes = {bb_node->children()[0].get()};
+    BookmarkContextMenu controller(nullptr, nullptr, profile_.get(),
+                                   BookmarkLaunchLocation::kNone,
+                                   selected_nodes, false);
+
+    ASSERT_TRUE(controller.IsCommandEnabled(IDC_COPY));
+    controller.ExecuteCommand(IDC_COPY, 0);
+
+    // No change in local count after copy.
+    EXPECT_EQ(original_local_count, bb_node->children().size());
+    EXPECT_EQ(original_account_count, account_bb_node->children().size());
+  }
+
+  // Test the Paste command.
+  {
+    // Select the first local bookmark (f1a).
+    const BookmarkNode* local_bookmark_to_copy = bb_node->children()[0].get();
+    std::vector<raw_ptr<const BookmarkNode, VectorExperimental>>
+        selected_nodes = {local_bookmark_to_copy};
+    BookmarkContextMenu controller(nullptr, nullptr, profile_.get(),
+                                   BookmarkLaunchLocation::kNone,
+                                   selected_nodes, false);
+
+    ASSERT_TRUE(controller.IsCommandEnabled(IDC_PASTE));
+    controller.ExecuteCommand(IDC_PASTE, 0);
+
+    // The newly pasted bookmark should be the last child of the account
+    // bookmark bar node.
+    ASSERT_EQ(original_account_count + 1, account_bb_node->children().size());
+    EXPECT_EQ(original_local_count, bb_node->children().size());
+
+    // Verify the pasted bookmark.
+    EXPECT_TRUE(account_bb_node->children().back()->is_url());
+    EXPECT_EQ(local_bookmark_to_copy->url(),
+              account_bb_node->children().back()->url());
+  }
+
+  // Test the Cut command.
+  {
+    // Select the first account bookmark (acc_f1a).
+    std::vector<raw_ptr<const BookmarkNode, VectorExperimental>>
+        selected_nodes = {account_bb_node->children()[0].get()};
+    BookmarkContextMenu controller(nullptr, nullptr, profile_.get(),
+                                   BookmarkLaunchLocation::kNone,
+                                   selected_nodes, false);
+
+    ASSERT_TRUE(controller.IsCommandEnabled(IDC_CUT));
+    controller.ExecuteCommand(IDC_CUT, 0);
+
+    EXPECT_EQ(original_account_count, account_bb_node->children().size());
+    EXPECT_TRUE(account_bb_node->children()[0]->is_folder());
+    EXPECT_EQ(original_local_count, bb_node->children().size());
+  }
 }
 
 // Tests that the "Show managed bookmarks" option in the context menu is only
 // visible if the policy is set.
-TEST_F(BookmarkContextMenuTest, ShowManagedBookmarks) {
+TEST_P(BookmarkContextMenuTest, ShowManagedBookmarks) {
   // Create a BookmarkContextMenu for the bookmarks bar.
   const BookmarkNode* bb_node = model_->bookmark_bar_node();
   std::vector<raw_ptr<const BookmarkNode, VectorExperimental>> nodes = {
@@ -359,3 +503,5 @@
   EXPECT_TRUE(menu->GetMenuItemByID(IDC_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS)
                   ->GetVisible());
 }
+
+INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(BookmarkContextMenuTest);
diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
index bde18a08..7147463 100644
--- a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
+++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
@@ -144,12 +144,11 @@
 SigninViewControllerDelegateViews::CreateHistorySyncOptInWebView(
     Browser* browser) {
   GURL url = GURL(chrome::kChromeUIHistorySyncOptinURL);
-  // TODO(crbug.com/404807488): Height to be adjusted dynamically on the dialog
-  // based on the contents' size.
-  int height = 300;
-  auto web_view = CreateDialogWebView(browser, url, height, kModalDialogWidth,
-                                      InitializeSigninWebDialogUI(false));
-
+  // The the actual dialog's height will be set dynamically based on its
+  // contents, so the initial height does not matter.
+  auto web_view =
+      CreateDialogWebView(browser, url, /*dialog_height=*/0, kModalDialogWidth,
+                          InitializeSigninWebDialogUI(false));
   CHECK(web_view);
   HistorySyncOptinUI* web_ui = web_view->GetWebContents()
                                    ->GetWebUI()
@@ -549,9 +548,7 @@
       SigninViewControllerDelegateViews::CreateHistorySyncOptInWebView(browser);
   return new SigninViewControllerDelegateViews(
       std::move(content_view), browser, ui::mojom::ModalType::kWindow,
-      // TODO(crbug.com/404807488): Update wait for size once dynamic sizing is
-      // implemented for the view.
-      /*wait_for_size=*/false, /*should_show_close_button=*/false,
+      /*wait_for_size=*/true, /*should_show_close_button=*/false,
       /*animate_on_resize=*/true);
 }
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_navigation_throttle.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_navigation_throttle.cc
index 550885011..b6302014 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_navigation_throttle.cc
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_navigation_throttle.cc
@@ -15,10 +15,10 @@
 #include "ui/base/page_transition_types.h"
 
 // static
-std::unique_ptr<content::NavigationThrottle>
-ReadAnythingSidePanelNavigationThrottle::CreateFor(
-    content::NavigationHandle* handle) {
-  return base::WrapUnique(new ReadAnythingSidePanelNavigationThrottle(handle));
+void ReadAnythingSidePanelNavigationThrottle::CreateAndAdd(
+    content::NavigationThrottleRegistry& registry) {
+  registry.AddThrottle(
+      base::WrapUnique(new ReadAnythingSidePanelNavigationThrottle(registry)));
 }
 
 ReadAnythingSidePanelNavigationThrottle::ThrottleCheckResult
@@ -32,8 +32,8 @@
 
 ReadAnythingSidePanelNavigationThrottle::
     ReadAnythingSidePanelNavigationThrottle(
-        content::NavigationHandle* navigation_handle)
-    : NavigationThrottle(navigation_handle) {}
+        content::NavigationThrottleRegistry& registry)
+    : NavigationThrottle(registry) {}
 
 ReadAnythingSidePanelNavigationThrottle::ThrottleCheckResult
 ReadAnythingSidePanelNavigationThrottle::HandleSidePanelRequest() {
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_navigation_throttle.h b/chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_navigation_throttle.h
index 5416df2..50bc20c76 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_navigation_throttle.h
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_navigation_throttle.h
@@ -7,14 +7,17 @@
 
 #include "content/public/browser/navigation_throttle.h"
 
+namespace content {
+class NavigationThrottleRegistry;
+}  // namespace content
+
 // This class prevents users from opening reading mode in the main content area
 // by intercepting a request for the read anything page and instead showing the
 // side panel.
 class ReadAnythingSidePanelNavigationThrottle
     : public content::NavigationThrottle {
  public:
-  static std::unique_ptr<content::NavigationThrottle> CreateFor(
-      content::NavigationHandle* handle);
+  static void CreateAndAdd(content::NavigationThrottleRegistry& registry);
 
   // NavigationThrottle overrides:
   ThrottleCheckResult WillStartRequest() override;
@@ -22,7 +25,7 @@
 
  private:
   explicit ReadAnythingSidePanelNavigationThrottle(
-      content::NavigationHandle* navigation_handle);
+      content::NavigationThrottleRegistry& registry);
 
   ThrottleCheckResult HandleSidePanelRequest();
 };
diff --git a/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc b/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
index 2a9c630..1386120 100644
--- a/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
@@ -762,8 +762,14 @@
   }
 }
 
+// TODO(crbug.com/394710875): Re-enable this test
+#if BUILDFLAG(IS_LINUX)
+#define MAYBE_NoLinkCapturePopupNavigation DISABLED_NoLinkCapturePopupNavigation
+#else
+#define MAYBE_NoLinkCapturePopupNavigation NoLinkCapturePopupNavigation
+#endif
 IN_PROC_BROWSER_TEST_P(WebAppLinkCapturingBrowserTest,
-                       NoLinkCapturePopupNavigation) {
+                       MAYBE_NoLinkCapturePopupNavigation) {
   const auto [app_id, in_scope, _, scope] =
       InstallTestApp("/web_apps/basic.html");
 
diff --git a/chrome/browser/ui/webui/about/BUILD.gn b/chrome/browser/ui/webui/about/BUILD.gn
index df8f43be..d0f87997 100644
--- a/chrome/browser/ui/webui/about/BUILD.gn
+++ b/chrome/browser/ui/webui/about/BUILD.gn
@@ -5,18 +5,24 @@
 assert(is_win || is_mac || is_linux || is_chromeos || is_android)
 
 source_set("about") {
-  sources = [
-    "about_ui.cc",
-    "about_ui.h",
-  ]
+  sources = [ "about_ui.h" ]
 
   public_deps = [
     "//base",
-    "//chrome/browser:browser_public_dependencies",
     "//content/public/browser",
   ]
+}
+
+# TODO(crbug.com/364667551): includes //c/b/about_flags.h and other sources
+# from //chrome/browser/ui/webui, which are not modularized yet. Merge this
+# to "about" source set when the dependencies are modularized.
+source_set("impl") {
+  sources = [ "about_ui.cc" ]
+
+  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
 
   deps = [
+    ":about",
     "//chrome/browser:browser_process",
     "//chrome/browser/profiles:profile",
     "//components/language/core/common",
diff --git a/chrome/browser/ui/webui/ash/floating_workspace/BUILD.gn b/chrome/browser/ui/webui/ash/floating_workspace/BUILD.gn
index a7c987e..b4602dd 100644
--- a/chrome/browser/ui/webui/ash/floating_workspace/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/floating_workspace/BUILD.gn
@@ -8,8 +8,6 @@
   sources = [
     "floating_workspace_dialog.cc",
     "floating_workspace_dialog.h",
-    "floating_workspace_handler.cc",
-    "floating_workspace_handler.h",
     "floating_workspace_ui.cc",
     "floating_workspace_ui.h",
   ]
@@ -31,19 +29,14 @@
     "//chrome/browser/ash/floating_workspace:floating_workspace",
     "//chrome/browser/resources/chromeos/floating_workspace:resources",
     "//chrome/browser/ui/ash/login:login",
-    "//chrome/browser/ui/webui/ash/internet:internet",
     "//chrome/browser/ui/webui/ash/login:login",
     "//chromeos/ash/components/browser_context_helper:browser_context_helper",
     "//content/public/browser",
     "//ui/aura",
     "//ui/base:types",
-    "//ui/chromeos/strings:strings_provider",
     "//ui/display",
     "//ui/views",
     "//ui/webui",
     "//url",
   ]
-
-  allow_circular_includes_from =
-      [ "//chrome/browser/ash/floating_workspace:floating_workspace" ]
 }
diff --git a/chrome/browser/ui/webui/ash/floating_workspace/DEPS b/chrome/browser/ui/webui/ash/floating_workspace/DEPS
index c5cf251..00efad7 100644
--- a/chrome/browser/ui/webui/ash/floating_workspace/DEPS
+++ b/chrome/browser/ui/webui/ash/floating_workspace/DEPS
@@ -5,7 +5,6 @@
   "+chrome/browser/ash/floating_workspace",
   "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/webui/ash/login",
-  "+chrome/browser/ui/webui/ash/internet",
   "+chrome/browser/ui/webui/ash/system_web_dialog",
   "+chrome/browser/ui/webui/webui_util.h",
   "+chrome/common",
diff --git a/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.cc b/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.cc
index c31bf08..0050292 100644
--- a/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.cc
+++ b/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.cc
@@ -9,8 +9,6 @@
 #include "chrome/browser/ash/floating_workspace/floating_workspace_service.h"
 #include "chrome/browser/ash/floating_workspace/floating_workspace_service_factory.h"
 #include "chrome/browser/ui/ash/login/oobe_dialog_size_utils.h"
-#include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_handler.h"
-#include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_ui.h"
 #include "chrome/browser/ui/webui/ash/system_web_dialog/system_web_dialog_delegate.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
@@ -40,21 +38,6 @@
   g_dialog = nullptr;
 }
 
-FloatingWorkspaceDialogHandler* FloatingWorkspaceDialog::GetHandler() {
-  if (!g_dialog) {
-    return nullptr;
-  }
-  auto* web_ui = g_dialog->webui();
-  if (!web_ui) {
-    return nullptr;
-  }
-  auto* controller = web_ui->GetController();
-  if (!controller) {
-    return nullptr;
-  }
-  return static_cast<FloatingWorkspaceUI*>(controller)->GetMainHandler();
-}
-
 void FloatingWorkspaceDialog::GetDialogSize(gfx::Size* size) const {
   *size = CalculateOobeDialogSizeForPrimaryDisplay();
 }
@@ -69,9 +52,8 @@
     CHECK(browser_context);
     FloatingWorkspaceService* service =
         FloatingWorkspaceServiceFactory::GetForProfile(browser_context);
-    if (service) {
-      service->StopRestoringSession();
-    }
+    CHECK(service);
+    service->StopRestoringSession();
   } else if (!json_retval.empty()) {
     NOTREACHED();
   }
@@ -91,37 +73,6 @@
   return false;
 }
 
-gfx::NativeWindow FloatingWorkspaceDialog::GetNativeWindow() {
-  if (g_dialog) {
-    return g_dialog->dialog_window();
-  }
-  return nullptr;
-}
-
-void FloatingWorkspaceDialog::ShowDefaultScreen() {
-  FloatingWorkspaceDialog::Show();
-  FloatingWorkspaceDialogHandler* handler = GetHandler();
-  if (handler) {
-    handler->ShowDefaultScreen();
-  }
-}
-
-void FloatingWorkspaceDialog::ShowNetworkScreen() {
-  FloatingWorkspaceDialog::Show();
-  FloatingWorkspaceDialogHandler* handler = GetHandler();
-  if (handler) {
-    handler->ShowNetworkScreen();
-  }
-}
-
-void FloatingWorkspaceDialog::ShowErrorScreen() {
-  FloatingWorkspaceDialog::Show();
-  FloatingWorkspaceDialogHandler* handler = GetHandler();
-  if (handler) {
-    handler->ShowErrorScreen();
-  }
-}
-
 void FloatingWorkspaceDialog::Close() {
   if (g_dialog) {
     g_dialog->SystemWebDialogDelegate::Close();
@@ -140,16 +91,4 @@
   g_dialog->ShowSystemDialog();
 }
 
-std::optional<FloatingWorkspaceDialog::State>
-FloatingWorkspaceDialog::IsShown() {
-  if (!g_dialog) {
-    return std::nullopt;
-  }
-  FloatingWorkspaceDialogHandler* handler = GetHandler();
-  if (!handler) {
-    return std::nullopt;
-  }
-  return handler->state();
-}
-
 }  // namespace ash
diff --git a/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.h b/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.h
index 44f5991..d75fbcf 100644
--- a/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.h
+++ b/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.h
@@ -5,43 +5,26 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_ASH_FLOATING_WORKSPACE_FLOATING_WORKSPACE_DIALOG_H_
 #define CHROME_BROWSER_UI_WEBUI_ASH_FLOATING_WORKSPACE_FLOATING_WORKSPACE_DIALOG_H_
 
-#include <optional>
-
 #include "chrome/browser/ui/webui/ash/system_web_dialog/system_web_dialog_delegate.h"
 #include "ui/base/mojom/ui_base_types.mojom-shared.h"
 
 namespace ash {
-
-class FloatingWorkspaceDialogHandler;
-
 // This dialog is shown at startup during the delay in which floating
 // workspace fetches the state.
 class FloatingWorkspaceDialog : public SystemWebDialogDelegate {
  public:
-  // This dialog can be in only one of three states.
-  enum class State { kDefault, kNetwork, kError };
-
   FloatingWorkspaceDialog(const FloatingWorkspaceDialog&) = delete;
   FloatingWorkspaceDialog& operator=(const FloatingWorkspaceDialog&) = delete;
   ~FloatingWorkspaceDialog() override;
 
-  static void ShowDefaultScreen();
-  static void ShowNetworkScreen();
-  static void ShowErrorScreen();
-  // Returns an empty optional if the dialog is not shown, otherwise returns
-  // it's current state.
-  static std::optional<State> IsShown();
+  // Displays the dialog.
+  static void Show();
 
   // Closes the dialog if it's currently opened.
   static void Close();
 
-  static gfx::NativeWindow GetNativeWindow();
-
  protected:
   FloatingWorkspaceDialog();
-  static FloatingWorkspaceDialogHandler* GetHandler();
-  // Creates and displays the dialog.
-  static void Show();
 
   // ui::WebDialogDelegate overrides
   void GetDialogSize(gfx::Size* size) const override;
@@ -50,5 +33,7 @@
   bool ShouldShowDialogTitle() const override;
   bool ShouldCloseDialogOnEscape() const override;
 };
+
 }  // namespace ash
+
 #endif  // CHROME_BROWSER_UI_WEBUI_ASH_FLOATING_WORKSPACE_FLOATING_WORKSPACE_DIALOG_H_
diff --git a/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_handler.cc b/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_handler.cc
deleted file mode 100644
index 421a619..0000000
--- a/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_handler.cc
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_handler.h"
-
-#include <string>
-
-#include "base/functional/bind.h"
-#include "base/memory/weak_ptr.h"
-#include "base/values.h"
-#include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.h"
-#include "chrome/browser/ui/webui/ash/internet/internet_config_dialog.h"
-#include "chrome/browser/ui/webui/ash/internet/internet_detail_dialog.h"
-#include "chromeos/ash/components/network/network_state_handler.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/web_ui.h"
-#include "content/public/browser/web_ui_data_source.h"
-#include "content/public/browser/web_ui_message_handler.h"
-#include "mojo/public/cpp/bindings/remote.h"
-
-namespace ash {
-
-namespace {
-constexpr char kInitialize[] = "initialize";
-constexpr char kAddNetwork[] = "addNetwork";
-constexpr char kShowNetworkDetails[] = "showNetworkDetails";
-constexpr char kShowNetworkConfig[] = "showNetworkConfig";
-constexpr char kGetHostname[] = "getHostname";
-
-const char kFloatingWorkspaceDialog[] = "$(\'floating-workspace-dialog\').";
-}  // namespace
-
-FloatingWorkspaceDialogHandler::FloatingWorkspaceDialogHandler() = default;
-
-FloatingWorkspaceDialogHandler::~FloatingWorkspaceDialogHandler() = default;
-
-void FloatingWorkspaceDialogHandler::RegisterMessages() {
-  web_ui()->RegisterMessageCallback(
-      kInitialize,
-      base::BindRepeating(&FloatingWorkspaceDialogHandler::Initialize,
-                          weak_ptr_factory_.GetWeakPtr()));
-  web_ui()->RegisterMessageCallback(
-      kAddNetwork,
-      base::BindRepeating(&FloatingWorkspaceDialogHandler::AddNetwork,
-                          weak_ptr_factory_.GetWeakPtr()));
-  web_ui()->RegisterMessageCallback(
-      kShowNetworkDetails,
-      base::BindRepeating(&FloatingWorkspaceDialogHandler::ShowNetworkDetails,
-                          weak_ptr_factory_.GetWeakPtr()));
-  web_ui()->RegisterMessageCallback(
-      kShowNetworkConfig,
-      base::BindRepeating(&FloatingWorkspaceDialogHandler::ShowNetworkConfig,
-                          weak_ptr_factory_.GetWeakPtr()));
-  web_ui()->RegisterMessageCallback(
-      kGetHostname,
-      base::BindRepeating(&FloatingWorkspaceDialogHandler::GetHostname,
-                          weak_ptr_factory_.GetWeakPtr()));
-}
-
-void FloatingWorkspaceDialogHandler::Initialize(const base::Value::List& args) {
-  AllowJavascript();
-  switch (state_) {
-    case FloatingWorkspaceDialog::State::kDefault:
-      CallJavascriptFunction(std::string(kFloatingWorkspaceDialog) +
-                             "showDefaultScreen");
-      break;
-    case FloatingWorkspaceDialog::State::kNetwork:
-      CallJavascriptFunction(std::string(kFloatingWorkspaceDialog) +
-                             "showNetworkScreen");
-      break;
-    case FloatingWorkspaceDialog::State::kError:
-      CallJavascriptFunction(std::string(kFloatingWorkspaceDialog) +
-                             "showErrorScreen");
-      break;
-  }
-}
-
-void FloatingWorkspaceDialogHandler::ShowDefaultScreen() {
-  if (state_ != FloatingWorkspaceDialog::State::kDefault &&
-      IsJavascriptAllowed()) {
-    CallJavascriptFunction(std::string(kFloatingWorkspaceDialog) +
-                           "showDefaultScreen");
-  }
-  state_ = FloatingWorkspaceDialog::State::kDefault;
-}
-
-void FloatingWorkspaceDialogHandler::ShowNetworkScreen() {
-  if (state_ != FloatingWorkspaceDialog::State::kNetwork &&
-      IsJavascriptAllowed()) {
-    CallJavascriptFunction(std::string(kFloatingWorkspaceDialog) +
-                           "showNetworkScreen");
-  }
-  state_ = FloatingWorkspaceDialog::State::kNetwork;
-}
-
-void FloatingWorkspaceDialogHandler::ShowErrorScreen() {
-  if (state_ != FloatingWorkspaceDialog::State::kError &&
-      IsJavascriptAllowed()) {
-    CallJavascriptFunction(std::string(kFloatingWorkspaceDialog) +
-                           "showErrorScreen");
-  }
-  state_ = FloatingWorkspaceDialog::State::kError;
-}
-
-void FloatingWorkspaceDialogHandler::ShowNetworkDetails(
-    const base::Value::List& args) {
-  CHECK_EQ(1u, args.size());
-  std::string guid = args[0].GetString();
-
-  // We need to pass NativeWindow to the network dialog here, because otherwise
-  // the network dialog would be shown behind our main modal dialog.
-  auto dialog = ash::FloatingWorkspaceDialog::GetNativeWindow();
-  if (dialog) {
-    InternetDetailDialog::ShowDialog(guid, dialog);
-  }
-}
-
-void FloatingWorkspaceDialogHandler::ShowNetworkConfig(
-    const base::Value::List& args) {
-  CHECK_EQ(1u, args.size());
-  std::string guid = args[0].GetString();
-
-  // We need to pass NativeWindow to the network dialog here, because otherwise
-  // the network dialog would be shown behind our main modal dialog.
-  auto dialog = ash::FloatingWorkspaceDialog::GetNativeWindow();
-  if (dialog) {
-    InternetConfigDialog::ShowDialogForNetworkId(guid, dialog);
-  }
-}
-
-void FloatingWorkspaceDialogHandler::AddNetwork(const base::Value::List& args) {
-  CHECK_EQ(1u, args.size());
-  std::string onc_type = args[0].GetString();
-
-  // We need to pass NativeWindow to the network dialog here, because otherwise
-  // the network dialog would be shown behind our main modal dialog.
-  auto dialog = ash::FloatingWorkspaceDialog::GetNativeWindow();
-  if (dialog) {
-    InternetConfigDialog::ShowDialogForNetworkType(onc_type, dialog);
-  }
-}
-
-// This is needed for proxy connection.
-void FloatingWorkspaceDialogHandler::GetHostname(
-    const base::Value::List& args) {
-  CHECK_EQ(1u, args.size());
-  std::string callback_id = args[0].GetString();
-  std::string hostname =
-      NetworkHandler::Get()->network_state_handler()->hostname();
-
-  ResolveJavascriptCallback(callback_id, hostname);
-}
-
-}  // namespace ash
diff --git a/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_handler.h b/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_handler.h
deleted file mode 100644
index 9dfd0b8..0000000
--- a/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_handler.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_ASH_FLOATING_WORKSPACE_FLOATING_WORKSPACE_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_ASH_FLOATING_WORKSPACE_FLOATING_WORKSPACE_HANDLER_H_
-
-#include "base/memory/weak_ptr.h"
-#include "base/values.h"
-#include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.h"
-#include "content/public/browser/web_ui_message_handler.h"
-
-namespace ash {
-
-class FloatingWorkspaceDialogHandler : public content::WebUIMessageHandler {
- public:
-  FloatingWorkspaceDialogHandler();
-  ~FloatingWorkspaceDialogHandler() override;
-
-  // WebUIMessageHandler implementation.
-  void RegisterMessages() override;
-
-  void ShowDefaultScreen();
-  void ShowNetworkScreen();
-  void ShowErrorScreen();
-
-  FloatingWorkspaceDialog::State state() { return state_; }
-
- private:
-  void Initialize(const base::Value::List& args);
-  void ShowNetworkDetails(const base::Value::List& args);
-  void ShowNetworkConfig(const base::Value::List& args);
-  void AddNetwork(const base::Value::List& args);
-  void GetHostname(const base::Value::List& args);
-  void Respond(const std::string& callback_id, base::ValueView response);
-
-  FloatingWorkspaceDialog::State state_;
-  base::WeakPtrFactory<FloatingWorkspaceDialogHandler> weak_ptr_factory_{this};
-};
-
-}  // namespace ash
-
-#endif  // CHROME_BROWSER_UI_WEBUI_ASH_FLOATING_WORKSPACE_FLOATING_WORKSPACE_HANDLER_H_
diff --git a/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_ui.cc b/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_ui.cc
index ba62832..f98e44d 100644
--- a/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_ui.cc
+++ b/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_ui.cc
@@ -6,10 +6,8 @@
 
 #include <memory>
 
-#include "ash/public/cpp/network_config_service.h"
 #include "ash/webui/common/trusted_types_util.h"
 #include "build/build_config.h"
-#include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/common/webui_url_constants.h"
@@ -18,7 +16,6 @@
 #include "chrome/grit/generated_resources.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
-#include "ui/chromeos/strings/network/network_element_localized_strings_provider.h"
 #include "ui/webui/webui_util.h"
 
 namespace ash {
@@ -28,11 +25,7 @@
                           chrome::kChromeUIFloatingWorkspaceDialogHost) {}
 
 FloatingWorkspaceUI::FloatingWorkspaceUI(content::WebUI* web_ui)
-    : MojoWebDialogUI(web_ui) {
-  auto main_handler = std::make_unique<FloatingWorkspaceDialogHandler>();
-  main_handler_ = main_handler.get();
-  web_ui->AddMessageHandler(std::move(main_handler));
-
+    : WebDialogUI(web_ui) {
   content::BrowserContext* browser_context =
       web_ui->GetWebContents()->GetBrowserContext();
   content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
@@ -41,55 +34,24 @@
   webui::SetupWebUIDataSource(source, kFloatingWorkspaceResources,
                               IDR_FLOATING_WORKSPACE_FLOATING_WORKSPACE_HTML);
 
+  // Reuse animation from the OOBE consumer update screen.
+  static constexpr webui::LocalizedString kAnimationMessage[] = {
+      {"pauseAnimationAriaLabel", IDS_OOBE_PAUSE_ANIMATION_MESSAGE}};
+  source->AddLocalizedStrings(kAnimationMessage);
+
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
       {"floatingWorkspaceStartupDialogTitle",
        IDS_FLOATING_WORKSPACE_STARTUP_DIALOG_TITLE},
       {"floatingWorkspaceStartupDialogLongResponseTitle",
        IDS_FLOATING_WORKSPACE_STARTUP_DIALOG_LONG_RESPONSE_TITLE},
       {"floatingWorkspaceStartupDialogButton",
-       IDS_FLOATING_WORKSPACE_STARTUP_DIALOG_BUTTON},
-      {"floatingWorkspaceNetworkDialogTitle",
-       IDS_FLOATING_WORKSPACE_NETWORK_DIALOG_TITLE},
-      {"floatingWorkspaceNetworkDialogSubtitle",
-       IDS_FLOATING_WORKSPACE_NETWORK_DIALOG_SUBTITLE},
-      {"floatingWorkspaceErrorDialogTitle",
-       IDS_FLOATING_WORKSPACE_ERROR_DIALOG_TITLE},
-      {"floatingWorkspaceErrorDialogSubtitle",
-       IDS_FLOATING_WORKSPACE_ERROR_DIALOG_SUBTITLE},
-      {"floatingWorkspaceErrorDialogButton",
-       IDS_FLOATING_WORKSPACE_ERROR_DIALOG_BUTTON},
-  };
+       IDS_FLOATING_WORKSPACE_STARTUP_DIALOG_BUTTON}};
   source->AddLocalizedStrings(kLocalizedStrings);
 
-  // Since we reuse animation from the OOBE consumer update screen, we
-  // need to add label for the pause/play button. Also reuse a string for the
-  // "Add WiFi" button on the network screen.
-  static constexpr webui::LocalizedString kExtraStrings[] = {
-      {"pauseAnimationAriaLabel", IDS_OOBE_PAUSE_ANIMATION_MESSAGE},
-      {"playAnimationAriaLabel", IDS_OOBE_PLAY_ANIMATION_MESSAGE},
-      {"addWiFiListItemName", IDS_NETWORK_ADD_WI_FI_LIST_ITEM_NAME},
-  };
-  source->AddLocalizedStrings(kExtraStrings);
-
-  // Add strings for the additional dialogs on the network screen.
-  ui::network_element::AddLocalizedStrings(source);
-  ui::network_element::AddOncLocalizedStrings(source);
-
   OobeUI::AddOobeComponents(source);
   ash::EnableTrustedTypesCSP(source);
 }
 
 FloatingWorkspaceUI::~FloatingWorkspaceUI() = default;
 
-FloatingWorkspaceDialogHandler* FloatingWorkspaceUI::GetMainHandler() {
-  return main_handler_;
-}
-
-void FloatingWorkspaceUI::BindInterface(
-    mojo::PendingReceiver<chromeos::network_config::mojom::CrosNetworkConfig>
-        receiver) {
-  GetNetworkConfigService(std::move(receiver));
-}
-
-WEB_UI_CONTROLLER_TYPE_IMPL(FloatingWorkspaceUI)
 }  // namespace ash
diff --git a/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_ui.h b/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_ui.h
index 8fed390..20e4a965 100644
--- a/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_ui.h
+++ b/chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_ui.h
@@ -8,15 +8,13 @@
 #include "ash/webui/common/chrome_os_webui_config.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/common/webui_url_constants.h"
-#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom-forward.h"
 #include "content/public/common/url_constants.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "ui/web_dialogs/web_dialog_ui.h"
-#include "ui/webui/mojo_web_ui_controller.h"
 
 namespace ash {
+
 class FloatingWorkspaceUI;
-class FloatingWorkspaceDialogHandler;
+
 // The WebUIConfig for the FloatingWorkspaceUI class.
 class FloatingWorkspaceUIConfig
     : public ChromeOSWebUIConfig<FloatingWorkspaceUI> {
@@ -24,26 +22,17 @@
   FloatingWorkspaceUIConfig();
 };
 
-class FloatingWorkspaceUI : public ui::MojoWebDialogUI {
+class FloatingWorkspaceUI : public ui::WebDialogUI {
  public:
   explicit FloatingWorkspaceUI(content::WebUI* web_ui);
   FloatingWorkspaceUI(const FloatingWorkspaceUI&) = delete;
   FloatingWorkspaceUI& operator=(const FloatingWorkspaceUI&) = delete;
   ~FloatingWorkspaceUI() override;
 
-  // Instantiates implementation of the mojom::CrosNetworkConfig mojo interface
-  // passing the pending receiver that will be internally bound.
-  void BindInterface(
-      mojo::PendingReceiver<chromeos::network_config::mojom::CrosNetworkConfig>
-          receiver);
-  FloatingWorkspaceDialogHandler* GetMainHandler();
-
  private:
-  raw_ptr<FloatingWorkspaceDialogHandler> main_handler_;
   base::WeakPtrFactory<FloatingWorkspaceUI> weak_factory_{this};
-
-  WEB_UI_CONTROLLER_TYPE_DECL();
 };
+
 }  // namespace ash
 
 #endif  // CHROME_BROWSER_UI_WEBUI_ASH_FLOATING_WORKSPACE_FLOATING_WORKSPACE_UI_H_
diff --git a/chrome/browser/ui/webui/signin/history_sync_optin/history_sync_optin.mojom b/chrome/browser/ui/webui/signin/history_sync_optin/history_sync_optin.mojom
index cfcfc44..17490c8b 100644
--- a/chrome/browser/ui/webui/signin/history_sync_optin/history_sync_optin.mojom
+++ b/chrome/browser/ui/webui/signin/history_sync_optin/history_sync_optin.mojom
@@ -29,6 +29,10 @@
   Reject();
   // Requests the signed in user's account info.
   RequestAccountInfo();
+  // Updates the native view of the dialog based on the current `height` of the
+  // web content view measured in pixels. Height of the content view is variable
+  // based on its contents.
+  UpdateDialogHeight(uint32 height);
 };
 
 // Called from C++ side of chrome://history-sync-optin (Browser -> Renderer)
diff --git a/chrome/browser/ui/webui/signin/history_sync_optin/history_sync_optin_handler.cc b/chrome/browser/ui/webui/signin/history_sync_optin/history_sync_optin_handler.cc
index 77d27c4..9ede713 100644
--- a/chrome/browser/ui/webui/signin/history_sync_optin/history_sync_optin_handler.cc
+++ b/chrome/browser/ui/webui/signin/history_sync_optin/history_sync_optin_handler.cc
@@ -69,6 +69,12 @@
   }
 }
 
+void HistorySyncOptinHandler::UpdateDialogHeight(uint32_t height) {
+  if (browser_) {
+    browser_->signin_view_controller()->SetModalSigninHeight(height);
+  }
+}
+
 void HistorySyncOptinHandler::FinishAndCloseDialog() {
   // TODO(crbug.com/404806506): Add metrics.
   if (browser_) {
diff --git a/chrome/browser/ui/webui/signin/history_sync_optin/history_sync_optin_handler.h b/chrome/browser/ui/webui/signin/history_sync_optin/history_sync_optin_handler.h
index 349256c6..44bd1ec5 100644
--- a/chrome/browser/ui/webui/signin/history_sync_optin/history_sync_optin_handler.h
+++ b/chrome/browser/ui/webui/signin/history_sync_optin/history_sync_optin_handler.h
@@ -37,6 +37,7 @@
   void Accept() override;
   void Reject() override;
   void RequestAccountInfo() override;
+  void UpdateDialogHeight(uint32_t height) override;
 
  private:
   // Gets the account info of the signed-in user if there is one, or
diff --git a/chrome/browser/ui/webui/tab_strip/BUILD.gn b/chrome/browser/ui/webui/tab_strip/BUILD.gn
index d38209a..0a23c5e 100644
--- a/chrome/browser/ui/webui/tab_strip/BUILD.gn
+++ b/chrome/browser/ui/webui/tab_strip/BUILD.gn
@@ -13,17 +13,4 @@
     "//url/mojom:url_mojom_gurl",
   ]
   webui_module_path = "/"
-
-  cpp_typemaps = [
-    {
-      types = [
-        {
-          mojom = "tab_strip.mojom.TabNetworkState"
-          cpp = "::TabNetworkState"
-        },
-      ]
-      traits_headers = [ "tab_strip_mojom_traits.h" ]
-      traits_public_deps = [ "//chrome/browser/ui/tabs:tabs_public" ]
-    },
-  ]
 }
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip.mojom b/chrome/browser/ui/webui/tab_strip/tab_strip.mojom
index ff8b4ed..4a2ed2b 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip.mojom
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip.mojom
@@ -4,17 +4,8 @@
 
 module tab_strip.mojom;
 
-import "url/mojom/url.mojom";
 import "chrome/browser/ui/webui/tabs/tabs.mojom";
-
-// Must be kept in sync with TabNetworkState from
-// chrome/browser/ui/tabs/tab_network_state.h
-enum TabNetworkState {
-  kNone,
-  kWaiting,
-  kLoading,
-  kError
-};
+import "url/mojom/url.mojom";
 
 // Information of an open tab.
 struct Tab {
@@ -50,7 +41,7 @@
   bool is_default_favicon;
 
   // The network state of the tab.
-  TabNetworkState network_state;
+  tabs.mojom.TabNetworkState network_state;
 
   // Whether the tab is pinned.
   bool pinned;
@@ -83,8 +74,8 @@
 // Used by the WebUI page to bootstrap bidirectional communication.
 interface PageHandlerFactory {
   // The WebUI calls this method when the page is first initialized.
-  CreatePageHandler(pending_remote<Page> page,
-                    pending_receiver<PageHandler> handler);
+  CreatePageHandler(
+      pending_remote<Page> page, pending_receiver<PageHandler> handler);
 };
 
 // Browser-side handler for requests from WebUI page.
@@ -117,8 +108,11 @@
   GetLayout() => (map<string, string> layout);
 
   // Show tab group edit dialog.
-  ShowEditDialogForGroup(string group_id, int32 location_x, int32 location_y,
-                         int32 width, int32 height);
+  ShowEditDialogForGroup(string group_id,
+                         int32 location_x,
+                         int32 location_y,
+                         int32 width,
+                         int32 height);
 
   // Show tab context menu.
   ShowTabContextMenu(int32 tab_id, int32 location_x, int32 location_y);
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_mojom_traits.h b/chrome/browser/ui/webui/tab_strip/tab_strip_mojom_traits.h
deleted file mode 100644
index 7f1e6bc..0000000
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_mojom_traits.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_TAB_STRIP_TAB_STRIP_MOJOM_TRAITS_H_
-#define CHROME_BROWSER_UI_WEBUI_TAB_STRIP_TAB_STRIP_MOJOM_TRAITS_H_
-
-#include "base/containers/fixed_flat_map.h"
-#include "chrome/browser/ui/tabs/tab_network_state.h"
-#include "chrome/browser/ui/webui/tab_strip/tab_strip.mojom.h"
-
-namespace mojo {
-
-template <>
-struct EnumTraits<tab_strip::mojom::TabNetworkState, TabNetworkState> {
-  static tab_strip::mojom::TabNetworkState ToMojom(TabNetworkState input) {
-    static constexpr auto network_state_map = base::MakeFixedFlatMap<
-        TabNetworkState, tab_strip::mojom::TabNetworkState>(
-        {{TabNetworkState::kNone, tab_strip::mojom::TabNetworkState::kNone},
-         {TabNetworkState::kWaiting,
-          tab_strip::mojom::TabNetworkState::kWaiting},
-         {TabNetworkState::kLoading,
-          tab_strip::mojom::TabNetworkState::kLoading},
-         {TabNetworkState::kError, tab_strip::mojom::TabNetworkState::kError}});
-    return network_state_map.at(input);
-  }
-
-  static bool FromMojom(tab_strip::mojom::TabNetworkState input,
-                        TabNetworkState* out) {
-    static constexpr auto network_state_map = base::MakeFixedFlatMap<
-        tab_strip::mojom::TabNetworkState, TabNetworkState>(
-        {{tab_strip::mojom::TabNetworkState::kNone, TabNetworkState::kNone},
-         {tab_strip::mojom::TabNetworkState::kWaiting,
-          TabNetworkState::kWaiting},
-         {tab_strip::mojom::TabNetworkState::kLoading,
-          TabNetworkState::kLoading},
-         {tab_strip::mojom::TabNetworkState::kError, TabNetworkState::kError}});
-    *out = network_state_map.at(input);
-    return true;
-  }
-};
-
-}  // namespace mojo
-
-#endif  // CHROME_BROWSER_UI_WEBUI_TAB_STRIP_TAB_STRIP_MOJOM_TRAITS_H_
diff --git a/chrome/browser/ui/webui/tabs/BUILD.gn b/chrome/browser/ui/webui/tabs/BUILD.gn
index 8d3eb41..89d3bea 100644
--- a/chrome/browser/ui/webui/tabs/BUILD.gn
+++ b/chrome/browser/ui/webui/tabs/BUILD.gn
@@ -16,12 +16,19 @@
           mojom = "tabs.mojom.TabAlertState"
           cpp = "::tabs::TabAlert"
         },
+        {
+          mojom = "tabs.mojom.TabNetworkState"
+          cpp = "::TabNetworkState"
+        },
       ]
-      traits_headers = [
-        "tabs_mojom_traits.h",
-        "//chrome/browser/ui/tabs/alert/tab_alert.h",
+      traits_headers = [ "tabs_mojom_traits.h" ]
+      traits_sources = [ "tabs_mojom_traits.cc" ]
+      traits_public_deps = [
+        "//base",
+        "//chrome/browser/ui/tabs:tab_enums",
+        "//chrome/browser/ui/tabs:tabs_public",
+        "//chrome/browser/ui/tabs/alert:tab_alert",
       ]
-      traits_public_deps = [ "//base" ]
     },
   ]
 }
diff --git a/chrome/browser/ui/webui/tabs/tabs.mojom b/chrome/browser/ui/webui/tabs/tabs.mojom
index 4335825c..30671ba 100644
--- a/chrome/browser/ui/webui/tabs/tabs.mojom
+++ b/chrome/browser/ui/webui/tabs/tabs.mojom
@@ -5,12 +5,13 @@
 module tabs.mojom;
 
 // Must contain the same values as `TabAlertState` in
-// chrome/browser/ui/tabs/tab_enums.h. Matching order is not important.
+// chrome/browser/ui/tabs/alert/tab_alert.h Matching order is not important.
 enum TabAlertState {
   kAudioMuting,
   kAudioRecording,
   kAudioPlaying,
   kBluetoothConnected,
+  kBluetoothScanActive,
   kDesktopCapturing,
   kGlicAccessing,
   kHidConnected,
@@ -20,5 +21,13 @@
   kTabCapturing,
   kUsbConnected,
   kVideoRecording,
-  kVrPresentingInHeadset
+  kVrPresentingInHeadset,
+};
+
+// Typemapped to "//chrome/browser/ui/tabs/tab_network_state.h".
+enum TabNetworkState {
+  kNone,
+  kWaiting,
+  kLoading,
+  kError,
 };
diff --git a/chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.cc b/chrome/browser/ui/webui/tabs/tabs_mojom_traits.cc
similarity index 91%
rename from chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.cc
rename to chrome/browser/ui/webui/tabs/tabs_mojom_traits.cc
index 7f91e41..e033c76 100644
--- a/chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.cc
+++ b/chrome/browser/ui/webui/tabs/tabs_mojom_traits.cc
@@ -2,10 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.h"
+#include "chrome/browser/ui/webui/tabs/tabs_mojom_traits.h"
+
+namespace mojo {
 
 MojoTabNetworkState
-mojo::EnumTraits<MojoTabNetworkState, NativeTabNetworkState>::ToMojom(
+EnumTraits<MojoTabNetworkState, NativeTabNetworkState>::ToMojom(
     NativeTabNetworkState input) {
   switch (input) {
     case NativeTabNetworkState::kNone:
@@ -17,11 +19,10 @@
     case NativeTabNetworkState::kError:
       return MojoTabNetworkState::kError;
   }
-
   NOTREACHED();
 }
 
-bool mojo::EnumTraits<MojoTabNetworkState, NativeTabNetworkState>::FromMojom(
+bool EnumTraits<MojoTabNetworkState, NativeTabNetworkState>::FromMojom(
     MojoTabNetworkState in,
     NativeTabNetworkState* out) {
   switch (in) {
@@ -38,12 +39,10 @@
       *out = NativeTabNetworkState::kError;
       return true;
   }
-
   NOTREACHED();
 }
 
-MojoTabAlertState
-mojo::EnumTraits<MojoTabAlertState, NativeTabAlertState>::ToMojom(
+MojoTabAlertState EnumTraits<MojoTabAlertState, NativeTabAlertState>::ToMojom(
     NativeTabAlertState input) {
   switch (input) {
     case NativeTabAlertState::MEDIA_RECORDING:
@@ -77,11 +76,10 @@
     case NativeTabAlertState::GLIC_ACCESSING:
       return MojoTabAlertState::kGlicAccessing;
   }
-
   NOTREACHED();
 }
 
-bool mojo::EnumTraits<MojoTabAlertState, NativeTabAlertState>::FromMojom(
+bool EnumTraits<MojoTabAlertState, NativeTabAlertState>::FromMojom(
     MojoTabAlertState in,
     NativeTabAlertState* out) {
   switch (in) {
@@ -131,6 +129,7 @@
       *out = NativeTabAlertState::GLIC_ACCESSING;
       return true;
   }
-
   NOTREACHED();
 }
+
+}  // namespace mojo
diff --git a/chrome/browser/ui/webui/tabs/tabs_mojom_traits.h b/chrome/browser/ui/webui/tabs/tabs_mojom_traits.h
index 8984bcc..7f0cefe 100644
--- a/chrome/browser/ui/webui/tabs/tabs_mojom_traits.h
+++ b/chrome/browser/ui/webui/tabs/tabs_mojom_traits.h
@@ -7,80 +7,30 @@
 
 #include "base/containers/fixed_flat_map.h"
 #include "chrome/browser/ui/tabs/alert/tab_alert.h"
+#include "chrome/browser/ui/tabs/tab_network_state.h"
 #include "chrome/browser/ui/webui/tabs/tabs.mojom.h"
 
+// TODO(crbug.com/403572608) Autogenerate traits for simple enum cases.
 namespace mojo {
 
-template <>
-struct EnumTraits<tabs::mojom::TabAlertState, tabs::TabAlert> {
-  static tabs::mojom::TabAlertState ToMojom(tabs::TabAlert input) {
-    static constexpr auto alert_state_map =
-        base::MakeFixedFlatMap<tabs::TabAlert, tabs::mojom::TabAlertState>(
-            {{tabs::TabAlert::MEDIA_RECORDING,
-              tabs::mojom::TabAlertState::kMediaRecording},
-             {tabs::TabAlert::TAB_CAPTURING,
-              tabs::mojom::TabAlertState::kTabCapturing},
-             {tabs::TabAlert::AUDIO_PLAYING,
-              tabs::mojom::TabAlertState::kAudioPlaying},
-             {tabs::TabAlert::AUDIO_MUTING,
-              tabs::mojom::TabAlertState::kAudioMuting},
-             {tabs::TabAlert::BLUETOOTH_CONNECTED,
-              tabs::mojom::TabAlertState::kBluetoothConnected},
-             {tabs::TabAlert::BLUETOOTH_SCAN_ACTIVE,
-              tabs::mojom::TabAlertState::kBluetoothConnected},
-             {tabs::TabAlert::USB_CONNECTED,
-              tabs::mojom::TabAlertState::kUsbConnected},
-             {tabs::TabAlert::HID_CONNECTED,
-              tabs::mojom::TabAlertState::kHidConnected},
-             {tabs::TabAlert::SERIAL_CONNECTED,
-              tabs::mojom::TabAlertState::kSerialConnected},
-             {tabs::TabAlert::PIP_PLAYING,
-              tabs::mojom::TabAlertState::kPipPlaying},
-             {tabs::TabAlert::DESKTOP_CAPTURING,
-              tabs::mojom::TabAlertState::kDesktopCapturing},
-             {tabs::TabAlert::VR_PRESENTING_IN_HEADSET,
-              tabs::mojom::TabAlertState::kVrPresentingInHeadset},
-             {tabs::TabAlert::AUDIO_RECORDING,
-              tabs::mojom::TabAlertState::kAudioRecording},
-             {tabs::TabAlert::VIDEO_RECORDING,
-              tabs::mojom::TabAlertState::kVideoRecording},
-             {tabs::TabAlert::GLIC_ACCESSING,
-              tabs::mojom::TabAlertState::kGlicAccessing}});
-    return alert_state_map.at(input);
-  }
+using MojoTabNetworkState = tabs::mojom::TabNetworkState;
+using NativeTabNetworkState = enum TabNetworkState;
 
-  static bool FromMojom(tabs::mojom::TabAlertState input, tabs::TabAlert* out) {
-    static constexpr auto alert_state_map =
-        base::MakeFixedFlatMap<tabs::mojom::TabAlertState, tabs::TabAlert>(
-            {{tabs::mojom::TabAlertState::kMediaRecording,
-              tabs::TabAlert::MEDIA_RECORDING},
-             {tabs::mojom::TabAlertState::kTabCapturing,
-              tabs::TabAlert::TAB_CAPTURING},
-             {tabs::mojom::TabAlertState::kAudioPlaying,
-              tabs::TabAlert::AUDIO_PLAYING},
-             {tabs::mojom::TabAlertState::kAudioMuting,
-              tabs::TabAlert::AUDIO_MUTING},
-             {tabs::mojom::TabAlertState::kBluetoothConnected,
-              tabs::TabAlert::BLUETOOTH_CONNECTED},
-             {tabs::mojom::TabAlertState::kUsbConnected,
-              tabs::TabAlert::USB_CONNECTED},
-             {tabs::mojom::TabAlertState::kHidConnected,
-              tabs::TabAlert::HID_CONNECTED},
-             {tabs::mojom::TabAlertState::kSerialConnected,
-              tabs::TabAlert::SERIAL_CONNECTED},
-             {tabs::mojom::TabAlertState::kPipPlaying,
-              tabs::TabAlert::PIP_PLAYING},
-             {tabs::mojom::TabAlertState::kDesktopCapturing,
-              tabs::TabAlert::DESKTOP_CAPTURING},
-             {tabs::mojom::TabAlertState::kVrPresentingInHeadset,
-              tabs::TabAlert::VR_PRESENTING_IN_HEADSET},
-             {tabs::mojom::TabAlertState::kAudioRecording,
-              tabs::TabAlert::AUDIO_RECORDING},
-             {tabs::mojom::TabAlertState::kVideoRecording,
-              tabs::TabAlert::VIDEO_RECORDING}});
-    *out = alert_state_map.at(input);
-    return true;
-  }
+// TabNetworkState Enum mapping.
+template <>
+struct EnumTraits<MojoTabNetworkState, NativeTabNetworkState> {
+  static MojoTabNetworkState ToMojom(NativeTabNetworkState input);
+  static bool FromMojom(MojoTabNetworkState in, NativeTabNetworkState* out);
+};
+
+using MojoTabAlertState = tabs::mojom::TabAlertState;
+using NativeTabAlertState = enum tabs::TabAlert;
+
+// TabAlertState Enum mapping.
+template <>
+struct EnumTraits<MojoTabAlertState, NativeTabAlertState> {
+  static MojoTabAlertState ToMojom(NativeTabAlertState input);
+  static bool FromMojom(MojoTabAlertState in, NativeTabAlertState* out);
 };
 
 }  // namespace mojo
diff --git a/chrome/browser/web_applications/web_app_link_capturing_parameterized_browsertest.cc b/chrome/browser/web_applications/web_app_link_capturing_parameterized_browsertest.cc
index 68e28057..c16196e8 100644
--- a/chrome/browser/web_applications/web_app_link_capturing_parameterized_browsertest.cc
+++ b/chrome/browser/web_applications/web_app_link_capturing_parameterized_browsertest.cc
@@ -16,6 +16,11 @@
 #include "base/files/file_util.h"
 #include "base/json/json_file_value_serializer.h"
 #include "base/json/json_reader.h"
+
+#if BUILDFLAG(IS_MAC)
+#include "base/mac/mac_util.h"
+#endif
+
 #include "base/memory/weak_ptr.h"
 #include "base/notreached.h"
 #include "base/path_service.h"
@@ -1766,6 +1771,13 @@
       return false;
     }
 
+#if BUILDFLAG(IS_MAC)
+    //TODO(crbug.com/415092020): remove after Mac12 flakiness is fixed.
+    if (base::mac::MacOSMajorVersion() == 12) {
+      return true;
+    }
+#endif
+
     testing::TestParamInfo<LinkCaptureTestParam> param(GetParam(), 0);
     const base::Value::Dict& test_case = GetTestCaseDataFromParam();
 
diff --git a/chrome/build/android-desktop-x64.pgo.txt b/chrome/build/android-desktop-x64.pgo.txt
index c7178b1..9ad2cbd7 100644
--- a/chrome/build/android-desktop-x64.pgo.txt
+++ b/chrome/build/android-desktop-x64.pgo.txt
@@ -1 +1 @@
-chrome-android-desktop-x64-main-1747201333-545f82db812eeb4ab2370c1e788c7399b73b251a-f9d72e4c8f13fe21ce8711117fd25e6f2d721beb.profdata
+chrome-android-desktop-x64-main-1747363107-ffb0f18fe2657b06cd45193e753ba5438e8a9742-2b35bf22bd0880940599f0d677a45891d252ef4a.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index bb96988..ef4dd5d5 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1747346366-66fac611ba9a7fbdca06bc473cc999fce2a2bb73-22c697a97e231716bdb9af23875cb8f38172c885.profdata
+chrome-mac-arm-main-1747374511-873d40b203157bb587a78c389277c556875cd341-660faa3b0ebb9895db20bec2f6180ea9c5263dae.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 928fd00..ff34a86 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1747331992-1566835832fe172532ee633651b2a60a89454521-6e0befebc90f982694346073119820ff61e213b5.profdata
+chrome-win-arm64-main-1747353456-9f1c873cf73dbb87df41df6553e873913afd554e-8aadc7dc60c995b652d20cd2c49876d781401a34.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 1152deb..d3c8a5b 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1747321161-659d053ab23d0b0ccd3a9caa01492730f72f43d9-07c11b66274d8bd394bb7d3fb13e0b42536a5da3.profdata
+chrome-win32-main-1747353456-3d8b846748d86871b0470db3525e26e3dd02b1fb-8aadc7dc60c995b652d20cd2c49876d781401a34.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 46365843e..cb15d2a 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1747309957-a942fecc18a1f5581400a8b4739432473a9af7ce-66a9943b59a237f7184cce1a9b3e45218c19da49.profdata
+chrome-win64-main-1747342758-b8a02635080b7fdbde431500675279645d4850ce-644ae5871e7c761c7462789541e21d6ac379ddd9.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 3f9b2f23..b5a711fb 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -367,6 +367,8 @@
       "../browser/extensions/mixin_based_extension_apitest.h",
       "../browser/extensions/permissions/permissions_test_util.cc",
       "../browser/extensions/permissions/permissions_test_util.h",
+      "../browser/extensions/test_extension_environment.cc",
+      "../browser/extensions/test_extension_environment.h",
       "../browser/extensions/test_extension_prefs.cc",
       "../browser/extensions/test_extension_prefs.h",
       "../common/extensions/manifest_tests/chrome_manifest_test.cc",
@@ -389,8 +391,6 @@
       "../browser/extensions/extension_api_unittest.h",
       "../browser/extensions/test_extension_action_dispatcher_observer.cc",
       "../browser/extensions/test_extension_action_dispatcher_observer.h",
-      "../browser/extensions/test_extension_environment.cc",
-      "../browser/extensions/test_extension_environment.h",
       "../browser/extensions/test_extension_menu_icon_loader.cc",
       "../browser/extensions/test_extension_menu_icon_loader.h",
       "../browser/extensions/user_scripts_test_util.cc",
@@ -1730,7 +1730,6 @@
       "//chrome/browser/optimization_guide:optimization_guide",
       "//chrome/browser/password_manager/android:test_support",
       "//chrome/browser/payments:browser_tests",
-      "//chrome/browser/plus_addresses",
       "//chrome/browser/policy:browser_tests",
       "//chrome/browser/policy:test_support",
       "//chrome/browser/preloading/prefetch:browser_tests",
@@ -1796,9 +1795,6 @@
       "//components/page_load_metrics/browser:test_support",
       "//components/page_load_metrics/browser/observers/ad_metrics:test_support",
       "//components/password_manager/content/browser:browser",
-      "//components/plus_addresses",
-      "//components/plus_addresses:test_support",
-      "//components/plus_addresses/settings:test_support",
       "//components/policy:chrome_settings_proto_generated_compile",
       "//components/policy/core/browser:pref_mapping_test_support",
       "//components/policy/core/browser:test_support",
@@ -2516,9 +2512,7 @@
       "//components/payments/content:utils",
       "//components/performance_manager",
       "//components/performance_manager/test_support:test_support_common",
-      "//components/plus_addresses",
       "//components/plus_addresses:test_support",
-      "//components/plus_addresses/resources/strings",
       "//components/policy:chrome_settings_proto_generated_compile",
       "//components/policy/content",
       "//components/policy/core/browser:pref_mapping_test_support",
@@ -6631,7 +6625,6 @@
     "//chrome/browser/persisted_state_db:persisted_state_db",
     "//chrome/browser/picture_in_picture",
     "//chrome/browser/picture_in_picture:unit_tests",
-    "//chrome/browser/plus_addresses",
     "//chrome/browser/policy:test_support",
     "//chrome/browser/policy/messaging_layer/proto:log_upload_event_proto",
     "//chrome/browser/policy/messaging_layer/storage_selector",
@@ -6847,12 +6840,7 @@
     "//components/performance_manager",
     "//components/performance_manager/public/mojom",
     "//components/performance_manager/test_support:test_support_common",
-    "//components/plus_addresses",
-    "//components/plus_addresses:prefs",
-    "//components/plus_addresses:test_support",
-    "//components/plus_addresses/metrics",
     "//components/plus_addresses/resources/strings",
-    "//components/plus_addresses/settings:test_support",
     "//components/policy:generated",
     "//components/policy/core/browser:test_support",
     "//components/policy/core/common:common_constants",
@@ -7627,8 +7615,6 @@
       "//components/offline_pages/task",
       "//components/page_image_service:page_image_service",
       "//components/password_manager/content/browser:test_support",
-      "//components/plus_addresses:hats_utils",
-      "//components/plus_addresses/settings:test_support",
       "//components/pref_registry",
       "//components/services/unzip:in_process",
       "//components/signin/core/browser",
@@ -8938,6 +8924,8 @@
     sources += [
       "../browser/extensions/api/bookmarks/bookmarks_api_unittest.cc",
       "../browser/extensions/api/chrome_extensions_api_client_unittest.cc",
+      "../browser/extensions/api/declarative/rules_registry_service_unittest.cc",
+      "../browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc",
       "../browser/extensions/api/declarative_net_request/action_tracker_unittest.cc",
       "../browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc",
       "../browser/extensions/api/declarative_net_request/dnr_test_base.cc",
@@ -9054,8 +9042,6 @@
       "../browser/extensions/api/browsing_data/browsing_data_unittest.cc",
       "../browser/extensions/api/cookies/cookies_helpers_unittest.cc",
       "../browser/extensions/api/cookies/cookies_unittest.cc",
-      "../browser/extensions/api/declarative/rules_registry_service_unittest.cc",
-      "../browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc",
       "../browser/extensions/api/declarative_content/chrome_content_rules_registry_unittest.cc",
       "../browser/extensions/api/declarative_content/content_action_unittest.cc",
       "../browser/extensions/api/declarative_content/content_condition_unittest.cc",
@@ -11033,7 +11019,6 @@
       "//chrome/browser/permissions",
       "//chrome/browser/permissions:interactive_ui_tests",
       "//chrome/browser/picture_in_picture",
-      "//chrome/browser/plus_addresses",
       "//chrome/browser/policy:test_support",
       "//chrome/browser/prefs",
       "//chrome/browser/prefs:util",
@@ -11130,11 +11115,6 @@
       "//components/passage_embeddings",
       "//components/passage_embeddings:test_support",
       "//components/password_manager/content/browser",
-      "//components/plus_addresses",
-      "//components/plus_addresses:test_support",
-      "//components/plus_addresses/resources/strings",
-      "//components/plus_addresses/settings",
-      "//components/plus_addresses/settings:test_support",
       "//components/privacy_sandbox:privacy_sandbox",
       "//components/privacy_sandbox:privacy_sandbox_prefs",
       "//components/privacy_sandbox:tracking_protection_prefs",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/mostvisited/FakeMostVisitedSites.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/mostvisited/FakeMostVisitedSites.java
index 0ce4253..62a2db6 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/mostvisited/FakeMostVisitedSites.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/mostvisited/FakeMostVisitedSites.java
@@ -35,7 +35,7 @@
 
     // CustomLinkOperations -> MostVisitedSites implementation.
     @Override
-    public boolean addCustomLink(String name, @Nullable GURL url) {
+    public boolean addCustomLink(String name, @Nullable GURL url, @Nullable Integer pos) {
         // TODO (crbug.com/397421764): Implement when needed by tests.
         return false;
     }
diff --git a/chrome/test/data/webui/print_preview/BUILD.gn b/chrome/test/data/webui/print_preview/BUILD.gn
index 559aef4..fb664d86 100644
--- a/chrome/test/data/webui/print_preview/BUILD.gn
+++ b/chrome/test/data/webui/print_preview/BUILD.gn
@@ -55,7 +55,6 @@
     "restore_state_test.ts",
     "scaling_settings_interactive_test.ts",
     "scaling_settings_test.ts",
-    "select_mixin_lit_test.ts",
     "select_mixin_test.ts",
     "settings_select_test.ts",
     "test_plugin_proxy.ts",
diff --git a/chrome/test/data/webui/print_preview/print_preview_browsertest.cc b/chrome/test/data/webui/print_preview/print_preview_browsertest.cc
index eed45ba..860b1b4b 100644
--- a/chrome/test/data/webui/print_preview/print_preview_browsertest.cc
+++ b/chrome/test/data/webui/print_preview/print_preview_browsertest.cc
@@ -70,10 +70,6 @@
   RunTest("print_preview/select_mixin_test.js", "mocha.run()");
 }
 
-IN_PROC_BROWSER_TEST_F(PrintPreviewTest, SelectMixinLit) {
-  RunTest("print_preview/select_mixin_lit_test.js", "mocha.run()");
-}
-
 IN_PROC_BROWSER_TEST_F(PrintPreviewTest, SettingsSelect) {
   RunTest("print_preview/settings_select_test.js", "mocha.run()");
 }
diff --git a/chrome/test/data/webui/print_preview/select_mixin_lit_test.ts b/chrome/test/data/webui/print_preview/select_mixin_lit_test.ts
deleted file mode 100644
index ba8d06c..0000000
--- a/chrome/test/data/webui/print_preview/select_mixin_lit_test.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {SelectMixinLit} from 'chrome://print/print_preview.js';
-import {CrLitElement, html} from 'chrome://resources/lit/v3_0/lit.rollup.js';
-import {assertEquals} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
-
-suite('SelectMixinLitTest', function() {
-  let testSelect: TestSelectElement;
-
-  const TestSelectElementBase = SelectMixinLit(CrLitElement);
-
-  class TestSelectElement extends TestSelectElementBase {
-    static get is() {
-      return 'test-select';
-    }
-
-    override render() {
-      return html`
-        <select .value="${this.selectedValue}" @change="${this.onSelectChange}">
-          <option value="0" selected>0</option>
-          <option value="1">1</option>
-          <option value="2">2</option>
-        </select>
-      `;
-    }
-
-    selectChanges: string[] = [];
-
-    override onProcessSelectChange(value: string) {
-      this.selectChanges.push(value);
-      this.fire('process-select-change-called', value);
-    }
-  }
-
-  customElements.define(TestSelectElement.is, TestSelectElement);
-
-  setup(function() {
-    document.body.innerHTML = window.trustedTypes!.emptyHTML;
-    testSelect = document.createElement('test-select') as TestSelectElement;
-    document.body.appendChild(testSelect);
-    testSelect.selectedValue = '0';
-    return microtasksFinished();
-  });
-
-  // Tests that onProcessSelectChange() is called when the select value is
-  // by changing the select element but not when it is set programmatically.
-  test('call process select change', async () => {
-    const select = testSelect.shadowRoot.querySelector('select')!;
-    assertEquals('0', testSelect.selectedValue);
-    assertEquals('0', select.value);
-
-    // Programmatically update `selectedValue`. This should update the
-    // <select>'s value via the binding, but should not trigger
-    // onProcessSelectChange().
-    testSelect.selectedValue = '1';
-    await microtasksFinished();
-    assertEquals('1', select.value);
-
-    // Debounces by 100ms. Wait a bit longer to make sure nothing happens.
-    await new Promise(resolve => setTimeout(resolve, 120));
-    assertEquals(0, testSelect.selectChanges.length);
-
-    // Change the value from the UI.
-    select.value = '0';
-    select.dispatchEvent(new CustomEvent('change'));
-    await eventToPromise('process-select-change-called', testSelect);
-
-    assertEquals(1, testSelect.selectChanges.length);
-    assertEquals('0', testSelect.selectChanges[0]);
-  });
-});
diff --git a/chrome/test/data/webui/print_preview/select_mixin_test.ts b/chrome/test/data/webui/print_preview/select_mixin_test.ts
index a95e9c13..ca2df90 100644
--- a/chrome/test/data/webui/print_preview/select_mixin_test.ts
+++ b/chrome/test/data/webui/print_preview/select_mixin_test.ts
@@ -1,25 +1,25 @@
-// Copyright 2018 The Chromium Authors
+// Copyright 2025 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 import {SelectMixin} from 'chrome://print/print_preview.js';
-import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {CrLitElement, html} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise} from 'chrome://webui-test/test_util.js';
+import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 suite('SelectMixinTest', function() {
   let testSelect: TestSelectElement;
 
-  const TestSelectElementBase = SelectMixin(PolymerElement);
+  const TestSelectElementBase = SelectMixin(CrLitElement);
 
   class TestSelectElement extends TestSelectElementBase {
     static get is() {
       return 'test-select';
     }
 
-    static get template() {
+    override render() {
       return html`
-        <select value="[[selectedValue]]" on-change="onSelectChange">
+        <select .value="${this.selectedValue}" @change="${this.onSelectChange}">
           <option value="0" selected>0</option>
           <option value="1">1</option>
           <option value="2">2</option>
@@ -31,9 +31,7 @@
 
     override onProcessSelectChange(value: string) {
       this.selectChanges.push(value);
-      this.dispatchEvent(new CustomEvent(
-          'process-select-change-called',
-          {bubbles: true, composed: true, detail: value}));
+      this.fire('process-select-change-called', value);
     }
   }
 
@@ -44,12 +42,13 @@
     testSelect = document.createElement('test-select') as TestSelectElement;
     document.body.appendChild(testSelect);
     testSelect.selectedValue = '0';
+    return microtasksFinished();
   });
 
   // Tests that onProcessSelectChange() is called when the select value is
   // by changing the select element but not when it is set programmatically.
   test('call process select change', async () => {
-    const select = testSelect.shadowRoot!.querySelector('select')!;
+    const select = testSelect.shadowRoot.querySelector('select')!;
     assertEquals('0', testSelect.selectedValue);
     assertEquals('0', select.value);
 
@@ -57,6 +56,7 @@
     // <select>'s value via the binding, but should not trigger
     // onProcessSelectChange().
     testSelect.selectedValue = '1';
+    await microtasksFinished();
     assertEquals('1', select.value);
 
     // Debounces by 100ms. Wait a bit longer to make sure nothing happens.
diff --git a/chrome/test/data/webui/tab_strip/tab_test.ts b/chrome/test/data/webui/tab_strip/tab_test.ts
index aec8681..7c11940a 100644
--- a/chrome/test/data/webui/tab_strip/tab_test.ts
+++ b/chrome/test/data/webui/tab_strip/tab_test.ts
@@ -8,7 +8,7 @@
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import type {TabElement} from 'chrome://tab-strip.top-chrome/tab.js';
 import type {Tab} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
-import {TabNetworkState} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
+import {TabNetworkState} from 'chrome://tab-strip.top-chrome/tabs.mojom-webui.js';
 import {CloseTabAction, TabsApiProxyImpl} from 'chrome://tab-strip.top-chrome/tabs_api_proxy.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.ts b/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.ts
index 0aafe75..fc13f19 100644
--- a/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.ts
+++ b/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.ts
@@ -5,7 +5,7 @@
 import type {PageRemote} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
 import {PageCallbackRouter} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
 import type {Tab, TabGroupVisualData} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
-import {TabNetworkState} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
+import {TabNetworkState} from 'chrome://tab-strip.top-chrome/tabs.mojom-webui.js';
 import type {CloseTabAction, TabsApiProxy} from 'chrome://tab-strip.top-chrome/tabs_api_proxy.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 932e523..3cda97fe 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-16285.0.0-1068890
\ No newline at end of file
+16285.0.0-1068905
\ No newline at end of file
diff --git a/chromeos/ash/components/boca/boca_session_manager.h b/chromeos/ash/components/boca/boca_session_manager.h
index 16888564..0e772611 100644
--- a/chromeos/ash/components/boca/boca_session_manager.h
+++ b/chromeos/ash/components/boca/boca_session_manager.h
@@ -230,6 +230,14 @@
     return session_duration_timer_;
   }
 
+  base::OnceClosure& end_session_callback_for_testing() {
+    return end_session_callback_for_testing_;
+  }
+
+  void set_end_session_callback_for_testing(base::OnceClosure cb) {
+    end_session_callback_for_testing_ = std::move(cb);
+  }
+
  private:
   SEQUENCE_CHECKER(sequence_checker_);
 
@@ -265,6 +273,7 @@
   void CloseAllCaptions();
 
   const bool is_producer_;
+  base::OnceClosure end_session_callback_for_testing_;
   base::TimeDelta in_session_polling_interval_;
   base::TimeDelta indefinite_polling_interval_;
   base::ObserverList<Observer> observers_;
diff --git a/chromeos/ash/components/dbus/cicerone/BUILD.gn b/chromeos/ash/components/dbus/cicerone/BUILD.gn
index 7bdf263..18505805 100644
--- a/chromeos/ash/components/dbus/cicerone/BUILD.gn
+++ b/chromeos/ash/components/dbus/cicerone/BUILD.gn
@@ -42,7 +42,7 @@
   sources = [
     "//third_party/cros_system_api/dbus/vm_cicerone/cicerone_service.proto",
   ]
-  proto_deps = [ "//chromeos/ash/components/dbus:vm_applications_apps_proto" ]
+  deps = [ "//chromeos/ash/components/dbus:vm_applications_apps_proto" ]
   import_dirs = [ "//third_party/cros_system_api/dbus" ]
   proto_out_dir = "chromeos/ash/components/dbus/cicerone"
 
diff --git a/chromeos/profiles/arm.afdo.newest.txt b/chromeos/profiles/arm.afdo.newest.txt
index 580d8be..704c993 100644
--- a/chromeos/profiles/arm.afdo.newest.txt
+++ b/chromeos/profiles/arm.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-none-138-7151.8-1747016258-benchmark-138.0.7171.0-r1-redacted.afdo.xz
+chromeos-chrome-arm-none-138-7151.8-1747016258-benchmark-138.0.7180.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index 3f85456e..f01496f 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-138-7151.17-1747032091-benchmark-138.0.7171.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-138-7151.17-1747032091-benchmark-138.0.7180.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index 8529cda..f1d9d79 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-138-7151.8-1747020746-benchmark-138.0.7171.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-138-7151.8-1747020746-benchmark-138.0.7180.0-r1-redacted.afdo.xz
diff --git a/clank b/clank
index 6dc6dcf..b98a5841 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 6dc6dcf374257e18cd794bba4500e3a4ea9fd307
+Subproject commit b98a5841d71a8998413b6811f80897742ea8b9e1
diff --git a/components/autofill/core/browser/ml_model/autofill_ai/autofill_ai_model_executor_impl.cc b/components/autofill/core/browser/ml_model/autofill_ai/autofill_ai_model_executor_impl.cc
index 63ccb31..9a7cbd4 100644
--- a/components/autofill/core/browser/ml_model/autofill_ai/autofill_ai_model_executor_impl.cc
+++ b/components/autofill/core/browser/ml_model/autofill_ai/autofill_ai_model_executor_impl.cc
@@ -11,6 +11,7 @@
 
 #include "base/check_deref.h"
 #include "base/containers/to_vector.h"
+#include "base/feature_list.h"
 #include "base/functional/callback.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/browser/form_processing/optimization_guide_proto_util.h"
@@ -33,9 +34,11 @@
 
 AutofillAiModelExecutorImpl::AutofillAiModelExecutorImpl(
     AutofillAiModelCache* model_cache,
-    optimization_guide::OptimizationGuideModelExecutor* model_executor)
+    optimization_guide::OptimizationGuideModelExecutor* model_executor,
+    optimization_guide::ModelQualityLogsUploaderService* mqls_uploader)
     : model_cache_(CHECK_DEREF(model_cache)),
-      model_executor_(CHECK_DEREF(model_executor)) {}
+      model_executor_(CHECK_DEREF(model_executor)),
+      mqls_uploader_(CHECK_DEREF(mqls_uploader)) {}
 
 AutofillAiModelExecutorImpl::~AutofillAiModelExecutorImpl() = default;
 
@@ -91,6 +94,7 @@
     optimization_guide::OptimizationGuideModelExecutionResult execution_result,
     std::unique_ptr<optimization_guide::proto::FormsClassificationsLoggingData>
         logging_data) {
+  LogModelPredictions(std::move(logging_data));
   const FormSignature form_signature = CalculateFormSignature(form_data);
   ongoing_queries_.erase(form_signature);
 
@@ -157,4 +161,31 @@
                        std::move(relevant_field_identifiers));
 }
 
+void AutofillAiModelExecutorImpl::LogModelPredictions(
+    std::unique_ptr<optimization_guide::proto::FormsClassificationsLoggingData>
+        logging_data) {
+  if (!base::FeatureList::IsEnabled(
+          autofill::features::kAutofillAiUploadModelRequestAndResponse)) {
+    return;
+  }
+  // Note that the logging happens when `log_entry` goes out of scope.
+  // Since the user was allowed to run the model, logging is ok.
+  optimization_guide::ModelQualityLogEntry log_entry(
+      mqls_uploader_->GetWeakPtr());
+  optimization_guide::proto::FormsClassificationsLoggingData* data =
+      log_entry.log_ai_data_request()->mutable_forms_classifications();
+  // Only log the form and field signatures of the request, and the response.
+  const optimization_guide::proto::FormData& request_form =
+      logging_data->request().form_data();
+  AutofillAiTypeRequest* stripped_request = data->mutable_request();
+  optimization_guide::proto::FormData* stripped_form =
+      stripped_request->mutable_form_data();
+  stripped_form->set_form_signature(request_form.form_signature());
+  for (const optimization_guide::proto::FormFieldData& field :
+       request_form.fields()) {
+    stripped_form->add_fields()->set_field_signature(field.field_signature());
+  }
+  *data->mutable_response() = std::move(logging_data->response());
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/ml_model/autofill_ai/autofill_ai_model_executor_impl.h b/components/autofill/core/browser/ml_model/autofill_ai/autofill_ai_model_executor_impl.h
index 752cf99d..0c085e3 100644
--- a/components/autofill/core/browser/ml_model/autofill_ai/autofill_ai_model_executor_impl.h
+++ b/components/autofill/core/browser/ml_model/autofill_ai/autofill_ai_model_executor_impl.h
@@ -30,7 +30,8 @@
  public:
   AutofillAiModelExecutorImpl(
       AutofillAiModelCache* model_cache,
-      optimization_guide::OptimizationGuideModelExecutor* model_executor);
+      optimization_guide::OptimizationGuideModelExecutor* model_executor,
+      optimization_guide::ModelQualityLogsUploaderService* mqls_uploader);
   ~AutofillAiModelExecutorImpl() override;
 
   // AutofillAiModelExecutor:
@@ -51,11 +52,19 @@
           optimization_guide::proto::FormsClassificationsLoggingData>
           logging_data);
 
+  // Uploads a stripped request and the response of a model run to MQLS.
+  void LogModelPredictions(
+      std::unique_ptr<
+          optimization_guide::proto::FormsClassificationsLoggingData>
+          logging_data);
+
   // The cache into which the model responses are written.
   const raw_ref<AutofillAiModelCache> model_cache_;
 
   const raw_ref<optimization_guide::OptimizationGuideModelExecutor>
       model_executor_;
+  const raw_ref<optimization_guide::ModelQualityLogsUploaderService>
+      mqls_uploader_;
 
   // Form signatures for which a query is currently ongoing. The goal is to
   // avoid multiple queries for the same form at the same time.
diff --git a/components/autofill/core/browser/ml_model/autofill_ai/autofill_ai_model_executor_impl_unittest.cc b/components/autofill/core/browser/ml_model/autofill_ai/autofill_ai_model_executor_impl_unittest.cc
index f5283f8..71ea937 100644
--- a/components/autofill/core/browser/ml_model/autofill_ai/autofill_ai_model_executor_impl_unittest.cc
+++ b/components/autofill/core/browser/ml_model/autofill_ai/autofill_ai_model_executor_impl_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/test/gmock_move_support.h"
 #include "base/test/mock_callback.h"
 #include "base/test/protobuf_matchers.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "components/autofill/core/browser/field_types.h"
@@ -18,10 +19,13 @@
 #include "components/autofill/core/browser/ml_model/autofill_ai/autofill_ai_model_executor.h"
 #include "components/autofill/core/browser/ml_model/autofill_ai/mock_autofill_ai_model_cache.h"
 #include "components/autofill/core/browser/test_utils/autofill_form_test_utils.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_test_utils.h"
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/signatures.h"
+#include "components/optimization_guide/core/feature_registry/feature_registration.h"
 #include "components/optimization_guide/core/mock_optimization_guide_model_executor.h"
+#include "components/optimization_guide/core/model_quality/test_model_quality_logs_uploader_service.h"
 #include "components/optimization_guide/core/optimization_guide_proto_util.h"
 #include "components/optimization_guide/proto/features/common_quality_data.pb.h"
 #include "components/optimization_guide/proto/features/forms_classifications.pb.h"
@@ -33,10 +37,12 @@
 namespace autofill {
 namespace {
 
+using base::test::EqualsProto;
 using FieldIdentifier = AutofillAiModelCache::FieldIdentifier;
 using optimization_guide::OptimizationGuideModelExecutionError;
 using optimization_guide::OptimizationGuideModelExecutionResult;
 using optimization_guide::OptimizationGuideModelExecutionResultCallback;
+using optimization_guide::proto::AutofillAiTypeRequest;
 using optimization_guide::proto::AutofillAiTypeResponse;
 using ::testing::_;
 using ::testing::An;
@@ -47,9 +53,13 @@
 
 class AutofillAiModelExecutorImplTest : public testing::Test {
  public:
-  void SetUp() override {
-    engine_ = std::make_unique<AutofillAiModelExecutorImpl>(&model_cache_,
-                                                            &model_executor_);
+  AutofillAiModelExecutorImplTest() : mqls_uploader_(&local_state_) {
+    optimization_guide::model_execution::prefs::RegisterLocalStatePrefs(
+        local_state_.registry());
+    optimization_guide::model_execution::prefs::RegisterProfilePrefs(
+        local_state_.registry());
+    engine_ = std::make_unique<AutofillAiModelExecutorImpl>(
+        &model_cache_, &model_executor_, &mqls_uploader_);
   }
 
   AutofillAiModelExecutor* engine() { return engine_.get(); }
@@ -60,12 +70,18 @@
     return &model_executor_;
   }
 
+  optimization_guide::TestModelQualityLogsUploaderService& mqls_uploader() {
+    return mqls_uploader_;
+  }
+
  private:
   base::test::TaskEnvironment task_environment_;
+  TestingPrefServiceSimple local_state_;
   test::AutofillUnitTestEnvironment autofill_test_env_;
   MockAutofillAiModelCache model_cache_;
   testing::NiceMock<optimization_guide::MockOptimizationGuideModelExecutor>
       model_executor_;
+  optimization_guide::TestModelQualityLogsUploaderService mqls_uploader_;
   std::unique_ptr<AutofillAiModelExecutor> engine_;
 };
 
@@ -92,7 +108,7 @@
           /*log_entry=*/nullptr));
   EXPECT_CALL(
       model_cache(),
-      Update(CalculateFormSignature(form), base::test::EqualsProto(response),
+      Update(CalculateFormSignature(form), EqualsProto(response),
              ElementsAre(FieldIdentifier{
                  .signature = CalculateFieldSignatureForField(form.fields()[0]),
                  .rank_in_signature_group = 0})));
@@ -124,10 +140,9 @@
               optimization_guide::AnyWrapProto(response),
               /*execution_info=*/nullptr),
           /*log_entry=*/nullptr));
-  EXPECT_CALL(
-      model_cache(),
-      Update(CalculateFormSignature(form),
-             base::test::EqualsProto(AutofillAiTypeResponse()), IsEmpty()));
+  EXPECT_CALL(model_cache(),
+              Update(CalculateFormSignature(form),
+                     EqualsProto(AutofillAiTypeResponse()), IsEmpty()));
   EXPECT_CALL(on_model_executed, Run(form.global_id()));
 
   engine()->GetPredictions(form, on_model_executed.Get(), std::nullopt);
@@ -156,10 +171,9 @@
               optimization_guide::AnyWrapProto(response),
               /*execution_info=*/nullptr),
           /*log_entry=*/nullptr));
-  EXPECT_CALL(
-      model_cache(),
-      Update(CalculateFormSignature(form),
-             base::test::EqualsProto(AutofillAiTypeResponse()), IsEmpty()));
+  EXPECT_CALL(model_cache(),
+              Update(CalculateFormSignature(form),
+                     EqualsProto(AutofillAiTypeResponse()), IsEmpty()));
   EXPECT_CALL(on_model_executed, Run(form.global_id()));
 
   engine()->GetPredictions(form, on_model_executed.Get(), std::nullopt);
@@ -194,10 +208,9 @@
               optimization_guide::AnyWrapProto(response),
               /*execution_info=*/nullptr),
           /*log_entry=*/nullptr));
-  EXPECT_CALL(
-      model_cache(),
-      Update(CalculateFormSignature(form),
-             base::test::EqualsProto(AutofillAiTypeResponse()), IsEmpty()));
+  EXPECT_CALL(model_cache(),
+              Update(CalculateFormSignature(form),
+                     EqualsProto(AutofillAiTypeResponse()), IsEmpty()));
   EXPECT_CALL(on_model_executed, Run(form.global_id()));
 
   engine()->GetPredictions(form, on_model_executed.Get(), std::nullopt);
@@ -232,20 +245,18 @@
       .Times(2)
       .WillOnce(MoveArg<3>(&model_callback1))
       .WillOnce(MoveArg<3>(&model_callback2));
-  EXPECT_CALL(
-      model_cache(),
-      Update(
-          CalculateFormSignature(form2), base::test::EqualsProto(response2),
-          ElementsAre(FieldIdentifier{
-              .signature = CalculateFieldSignatureForField(form2.fields()[0]),
-              .rank_in_signature_group = 0})));
-  EXPECT_CALL(
-      model_cache(),
-      Update(
-          CalculateFormSignature(form1), base::test::EqualsProto(response1),
-          ElementsAre(FieldIdentifier{
-              .signature = CalculateFieldSignatureForField(form1.fields()[0]),
-              .rank_in_signature_group = 0})));
+  EXPECT_CALL(model_cache(),
+              Update(CalculateFormSignature(form2), EqualsProto(response2),
+                     ElementsAre(FieldIdentifier{
+                         .signature =
+                             CalculateFieldSignatureForField(form2.fields()[0]),
+                         .rank_in_signature_group = 0})));
+  EXPECT_CALL(model_cache(),
+              Update(CalculateFormSignature(form1), EqualsProto(response1),
+                     ElementsAre(FieldIdentifier{
+                         .signature =
+                             CalculateFieldSignatureForField(form1.fields()[0]),
+                         .rank_in_signature_group = 0})));
 
   engine()->GetPredictions(form1, base::DoNothing(), std::nullopt);
 
@@ -288,10 +299,9 @@
                           ModelExecutionError::kGenericFailure)),
               /*execution_info=*/nullptr),
           /*log_entry=*/nullptr));
-  EXPECT_CALL(
-      model_cache(),
-      Update(CalculateFormSignature(form),
-             base::test::EqualsProto(AutofillAiTypeResponse()), IsEmpty()));
+  EXPECT_CALL(model_cache(),
+              Update(CalculateFormSignature(form),
+                     EqualsProto(AutofillAiTypeResponse()), IsEmpty()));
   EXPECT_CALL(on_model_executed, Run(form.global_id()));
 
   engine()->GetPredictions(form, on_model_executed.Get(), std::nullopt);
@@ -311,14 +321,55 @@
           OptimizationGuideModelExecutionResult(
               optimization_guide::proto::Any(), /*execution_info=*/nullptr),
           /*log_entry=*/nullptr));
-  EXPECT_CALL(
-      model_cache(),
-      Update(CalculateFormSignature(form),
-             base::test::EqualsProto(AutofillAiTypeResponse()), IsEmpty()));
+  EXPECT_CALL(model_cache(),
+              Update(CalculateFormSignature(form),
+                     EqualsProto(AutofillAiTypeResponse()), IsEmpty()));
   EXPECT_CALL(on_model_executed, Run(form.global_id()));
 
   engine()->GetPredictions(form, on_model_executed.Get(), std::nullopt);
 }
 
+TEST_F(AutofillAiModelExecutorImplTest, MQLSUpload) {
+  base::test::ScopedFeatureList features;
+  features.InitWithFeatures(
+      /*enabled_features=*/
+      {optimization_guide::features::kFormsClassificationsMqlsLogging,
+       autofill::features::kAutofillAiUploadModelRequestAndResponse},
+      /*disabled_features=*/{});
+
+  const FormData form =
+      test::GetFormData({.fields = {{.name = u"Passport number"}}});
+
+  AutofillAiTypeRequest expected_request;
+  optimization_guide::proto::FormData* stripped_form =
+      expected_request.mutable_form_data();
+  stripped_form->set_form_signature(*CalculateFormSignature(form));
+  stripped_form->add_fields()->set_field_signature(
+      *CalculateFieldSignatureForField(form.fields()[0]));
+  AutofillAiTypeResponse response;
+  optimization_guide::proto::FieldTypeResponse* field_response =
+      response.add_field_responses();
+  field_response->set_field_type(PASSPORT_NUMBER);
+  field_response->set_field_index(0);
+
+  MockOnModelExecutedCallback on_model_executed;
+  EXPECT_CALL(*model_executor(), ExecuteModel)
+      .WillOnce(base::test::RunOnceCallback<3>(
+          OptimizationGuideModelExecutionResult(
+              optimization_guide::AnyWrapProto(response),
+              /*execution_info=*/nullptr),
+          /*log_entry=*/nullptr));
+  engine()->GetPredictions(form, on_model_executed.Get(), std::nullopt);
+
+  const std::vector<
+      std::unique_ptr<optimization_guide::proto::LogAiDataRequest>>&
+      uploaded_logs = mqls_uploader().uploaded_logs();
+  ASSERT_EQ(uploaded_logs.size(), 1u);
+  const optimization_guide::proto::FormsClassificationsLoggingData& log =
+      uploaded_logs[0]->forms_classifications();
+  EXPECT_THAT(log.request(), EqualsProto(expected_request));
+  EXPECT_THAT(log.response(), EqualsProto(response));
+}
+
 }  // namespace
 }  // namespace autofill
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index a19ac92..16191b1b 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -142,6 +142,12 @@
 const base::FeatureParam<int> kAutofillAiWithDataSchemaServerExperimentId{
     &kAutofillAiWithDataSchema, "autofill_ai_server_experiment_id", 0};
 
+// When enabled, requests and responses of client-triggered Autofill AI model
+// runs are uploaded to MQLS.
+BASE_FEATURE(kAutofillAiUploadModelRequestAndResponse,
+             "AutofillAiUploadModelRequestAndResponse",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Guards the refactoring to allow showing Autofill and Password suggestions in
 // the same surface instead of being mutually exclusive.
 BASE_FEATURE(kAutofillAndPasswordsInSameSurface,
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index d4d89cc..a31237bc 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -54,6 +54,8 @@
 extern const base::FeatureParam<int>
     kAutofillAiWithDataSchemaServerExperimentId;
 COMPONENT_EXPORT(AUTOFILL)
+BASE_DECLARE_FEATURE(kAutofillAiUploadModelRequestAndResponse);
+COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillAndPasswordsInSameSurface);
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillCreditCardUserPerceptionSurvey);
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index 90512359..a39bec4 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -403,6 +403,15 @@
   deps = [ "//third_party/androidx:androidx_annotation_annotation_java" ]
 }
 
+# CronetStatsLog uses `android.os.SystemProperties` which is not in the public
+# SDK, hence this separate target pointed at the system SDK.
+android_library("cronet_android_os_system_properties_java") {
+  sources = [ "java/src/org/chromium/net/impl/AndroidOsSystemProperties.java" ]
+  alternative_android_sdk_dep =
+      "//third_party/android_sdk:public_framework_system_java"
+  deps = [ "//third_party/androidx:androidx_annotation_annotation_java" ]
+}
+
 cronet_impl_common_java_srcjar_deps = [
   ":effective_connection_type_java",
   ":http_cache_type_java",
@@ -547,16 +556,19 @@
 cronet_impl_native_java_deps_to_package = [
   ":cronet_urlconnection_impl_java",
   ":request_context_config_java_proto",
+  ":cronet_android_os_system_properties_java",
   "//base/version_info/android:version_constants_java",
   "//base:base_java",
   "//third_party/jni_zero:jni_zero_java",
   "//net/android:net_java",
   "//url:url_java",
+  "//net/log:net_log_capture_mode_java",
 ]
 
 # cronet_impl_native_java.jar - native implementation of the Cronet engine.
 android_library("cronet_impl_native_java") {
   sources = [
+    "java/src/org/chromium/net/impl/AndroidOsBuild.java",
     "java/src/org/chromium/net/impl/BidirectionalStreamNetworkException.java",
     "java/src/org/chromium/net/impl/CronetBidirectionalStream.java",
     "java/src/org/chromium/net/impl/CronetLibraryLoader.java",
@@ -1299,6 +1311,7 @@
 # This must not contain buildInfo as `cronet_perf_tests` APK generates
 # another buildInfo and both of them starts colliding showing buildError.
 cronet_javatests_deps_to_package = [
+  ":cronet_android_os_system_properties_java",
   ":cronet_common_javatests",
   ":cronet_test_apk_java",
   ":flags_java_proto",
@@ -1319,6 +1332,7 @@
   "//net/android:embedded_test_server_aidl_java",
   "//net/android:net_java",
   "//net/android:net_java_test_support",
+  "//net/log:net_log_capture_mode_java",
   "//url:url_java",
   "//third_party/jni_zero:generate_jni_java",
   "//third_party/jni_zero:jni_zero_java",
diff --git a/components/cronet/android/cronet_combined_impl_native_proguard_golden.cfg b/components/cronet/android/cronet_combined_impl_native_proguard_golden.cfg
index d197959..2e98c32 100644
--- a/components/cronet/android/cronet_combined_impl_native_proguard_golden.cfg
+++ b/components/cronet/android/cronet_combined_impl_native_proguard_golden.cfg
@@ -182,6 +182,10 @@
 -keepclassmembers class org.chromium.** extends com.google.protobuf.GeneratedMessageLite {
   <fields>;
 }
+
+# Part of the Android System SDK; false positive when pointing ProGuard to the
+# public SDK.
+-dontwarn android.os.SystemProperties
 # -------- Config Path: components/cronet/android/cronet_shared_proguard.cfg --------
 # Proguard config for apps that depend on cronet_shared_java.jar (which should
 # be all apps that depend on any part of Cronet)
diff --git a/components/cronet/android/cronet_impl_native_proguard.cfg b/components/cronet/android/cronet_impl_native_proguard.cfg
index 49295ab..b10b1a40 100644
--- a/components/cronet/android/cronet_impl_native_proguard.cfg
+++ b/components/cronet/android/cronet_impl_native_proguard.cfg
@@ -60,3 +60,7 @@
 -keepclassmembers class org.chromium.** extends com.google.protobuf.GeneratedMessageLite {
   <fields>;
 }
+
+# Part of the Android System SDK; false positive when pointing ProGuard to the
+# public SDK.
+-dontwarn android.os.SystemProperties
diff --git a/components/cronet/android/cronet_library_loader.cc b/components/cronet/android/cronet_library_loader.cc
index 4daaf0e9..faad5e5 100644
--- a/components/cronet/android/cronet_library_loader.cc
+++ b/components/cronet/android/cronet_library_loader.cc
@@ -45,6 +45,7 @@
 #include "net/android/network_change_notifier_factory_android.h"
 #include "net/base/network_change_notifier.h"
 #include "net/log/net_log.h"
+#include "net/log/net_log_capture_mode.h"
 #include "net/log/trace_net_log_observer.h"
 #include "net/proxy_resolution/configured_proxy_resolution_service.h"
 #include "net/proxy_resolution/proxy_config_service_android.h"
@@ -80,6 +81,8 @@
     base::WaitableEvent::ResetPolicy::MANUAL,
     base::WaitableEvent::InitialState::NOT_SIGNALED);
 
+std::optional<net::NetLogCaptureMode> g_trace_net_log_capture_mode;
+
 ::org::chromium::net::httpflags::BaseFeatureOverrides GetBaseFeatureOverrides(
     JNIEnv* env) {
   const auto serializedProto =
@@ -212,7 +215,8 @@
 
 void JNI_CronetLibraryLoader_CronetInitOnInitThread(
     JNIEnv* env,
-    jboolean updateNetworkStateFromNative) {
+    jboolean updateNetworkStateFromNative,
+    net::NetLogCaptureMode trace_net_log_capture_mode) {
   // Initialize SingleThreadTaskExecutor for init thread.
   DCHECK(!base::CurrentThread::IsSet());
   DCHECK(!g_init_task_executor);
@@ -221,10 +225,12 @@
 
   static base::NoDestructor<net::TraceNetLogObserver> trace_net_log_observer(
       net::TraceNetLogObserver::Options{
-          // TODO: https://crbug.com/410018349 - make it possible to select a
-          // a different capture mode, if running in a debug scenario.
-          .capture_mode = net::NetLogCaptureMode::kHeavilyRedacted,
+          .capture_mode = trace_net_log_capture_mode,
+          .use_sensitive_category = trace_net_log_capture_mode !=
+                                    net::NetLogCaptureMode::kHeavilyRedacted,
       });
+  CHECK(!g_trace_net_log_capture_mode.has_value());
+  g_trace_net_log_capture_mode = trace_net_log_capture_mode;
   // Note we do this on the init thread, as opposed to a user thread, because
   // this eventually calls
   // base::trace_event::TraceLog::AddAsyncEnabledStateObserver(), which
@@ -248,6 +254,11 @@
   g_init_thread_init_done.Signal();
 }
 
+net::NetLogCaptureMode
+JNI_CronetLibraryLoader_GetTraceNetLogCaptureModeForTesting(JNIEnv* env) {
+  return g_trace_net_log_capture_mode.value();
+}
+
 ScopedJavaLocalRef<jstring> JNI_CronetLibraryLoader_GetCronetVersion(
     JNIEnv* env) {
 #if defined(ARCH_CPU_ARM64)
diff --git a/components/cronet/android/dependencies.txt b/components/cronet/android/dependencies.txt
index 55ecba8..95e4717 100644
--- a/components/cronet/android/dependencies.txt
+++ b/components/cronet/android/dependencies.txt
@@ -38,6 +38,7 @@
 //net/dns
 //net/dns/public
 //net/http
+//net/log
 //net/third_party/quiche
 //net/third_party/uri_template
 //net/traffic_annotation
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/AndroidOsBuild.java b/components/cronet/android/java/src/org/chromium/net/impl/AndroidOsBuild.java
new file mode 100644
index 0000000..d0354ea9
--- /dev/null
+++ b/components/cronet/android/java/src/org/chromium/net/impl/AndroidOsBuild.java
@@ -0,0 +1,42 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.net.impl;
+
+import android.os.Build;
+
+import androidx.annotation.VisibleForTesting;
+
+@VisibleForTesting
+public final class AndroidOsBuild {
+    private static AndroidOsBuild sOverrideForTesting;
+
+    private final String mType;
+
+    public AndroidOsBuild(String type) {
+        mType = type;
+    }
+
+    public static AndroidOsBuild get() {
+        return sOverrideForTesting != null ? sOverrideForTesting : new AndroidOsBuild(Build.TYPE);
+    }
+
+    /** See {@link android.os.Build#TYPE} */
+    public String getType() {
+        return mType;
+    }
+
+    public static final class WithOverrideForTesting implements AutoCloseable {
+        public WithOverrideForTesting(AndroidOsBuild override) {
+            assert sOverrideForTesting == null;
+            sOverrideForTesting = override;
+        }
+
+        @Override
+        public void close() {
+            assert sOverrideForTesting != null;
+            sOverrideForTesting = null;
+        }
+    }
+}
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/AndroidOsSystemProperties.java b/components/cronet/android/java/src/org/chromium/net/impl/AndroidOsSystemProperties.java
new file mode 100644
index 0000000..644331c
--- /dev/null
+++ b/components/cronet/android/java/src/org/chromium/net/impl/AndroidOsSystemProperties.java
@@ -0,0 +1,37 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.net.impl;
+
+import android.os.SystemProperties;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.Map;
+
+@VisibleForTesting
+public final class AndroidOsSystemProperties {
+    private static Map<String, String> sOverridesForTesting;
+
+    /** See {@link android.os.SystemProperties#get} */
+    public static String get(String key, String def) {
+        if (sOverridesForTesting == null) return SystemProperties.get(key, def);
+
+        var overrideForTesting = sOverridesForTesting.get(key);
+        return overrideForTesting != null ? overrideForTesting : def;
+    }
+
+    public static final class WithOverridesForTesting implements AutoCloseable {
+        public WithOverridesForTesting(Map<String, String> overrides) {
+            assert sOverridesForTesting == null;
+            sOverridesForTesting = overrides;
+        }
+
+        @Override
+        public void close() {
+            assert sOverridesForTesting != null;
+            sOverridesForTesting = null;
+        }
+    }
+}
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java
index 24170c0..32aa8dc 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java
@@ -5,6 +5,7 @@
 package org.chromium.net.impl;
 
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -15,6 +16,7 @@
 
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
+import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
 
 import org.chromium.base.BuildInfo;
@@ -23,6 +25,7 @@
 import org.chromium.base.Log;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.metrics.ScopedSysTraceEvent;
+import org.chromium.net.NetLogCaptureMode;
 import org.chromium.net.NetworkChangeNotifier;
 import org.chromium.net.RegistrationPolicyAlwaysRegister;
 import org.chromium.net.httpflags.BaseFeature;
@@ -54,6 +57,9 @@
     private static final ConditionVariable sHttpFlagsLoaded = new ConditionVariable();
 
     @VisibleForTesting
+    public static final String TRACE_NET_LOG_SYSTEM_PROPERTY_KEY = "debug.cronet.trace_netlog";
+
+    @VisibleForTesting
     public static final String UPDATE_NETWORK_STATE_ONCE_ON_STARTUP_FLAG_NAME =
             "Cronet_UpdateNetworkStateOnlyOnceOnStartup";
 
@@ -208,6 +214,49 @@
         return sInitThread.getLooper() == Looper.myLooper();
     }
 
+    private static @NetLogCaptureMode int getTraceNetLogCaptureMode() {
+        @NetLogCaptureMode int traceNetLogCaptureMode = NetLogCaptureMode.HEAVILY_REDACTED;
+        var requestedTraceNetLogCaptureMode =
+                AndroidOsSystemProperties.get(
+                        TRACE_NET_LOG_SYSTEM_PROPERTY_KEY, "heavily_redacted");
+        if (requestedTraceNetLogCaptureMode.equals("heavily_redacted")) {
+            traceNetLogCaptureMode = NetLogCaptureMode.HEAVILY_REDACTED;
+        } else if (requestedTraceNetLogCaptureMode.equals("on")) {
+            // Note DEFAULT is mapped to "on", not "default", to avoid confusion with regard to
+            // the default value of the system property.
+            traceNetLogCaptureMode = NetLogCaptureMode.DEFAULT;
+        } else if (requestedTraceNetLogCaptureMode.equals("include_sensitive")) {
+            traceNetLogCaptureMode = NetLogCaptureMode.INCLUDE_SENSITIVE;
+        } else if (requestedTraceNetLogCaptureMode.equals("everything")) {
+            traceNetLogCaptureMode = NetLogCaptureMode.EVERYTHING;
+        } else {
+            Log.w(
+                    TAG,
+                    "Unknown value for %s system property, ignoring: %s",
+                    TRACE_NET_LOG_SYSTEM_PROPERTY_KEY,
+                    requestedTraceNetLogCaptureMode);
+        }
+
+        if (traceNetLogCaptureMode > NetLogCaptureMode.HEAVILY_REDACTED) {
+            final var buildType = AndroidOsBuild.get().getType();
+            if (!buildType.equals("userdebug")
+                    && !buildType.equals("eng")
+                    && (ContextUtils.getApplicationContext().getApplicationInfo().flags
+                                    & ApplicationInfo.FLAG_DEBUGGABLE)
+                            == 0) {
+                Log.w(
+                        TAG,
+                        "Ignoring requested Cronet trace netlog capture mode (%s=%s) because"
+                                + " neither the device nor app are debuggable",
+                        TRACE_NET_LOG_SYSTEM_PROPERTY_KEY,
+                        requestedTraceNetLogCaptureMode);
+                traceNetLogCaptureMode = NetLogCaptureMode.HEAVILY_REDACTED;
+            }
+        }
+
+        return traceNetLogCaptureMode;
+    }
+
     /**
      * Runs Cronet initialization tasks on the init thread. Ensures that HTTP flags are loaded, the
      * NetworkChangeNotifier is initialzied and the init thread native MessageLoop is initialized.
@@ -257,6 +306,8 @@
                     new RegistrationPolicyAlwaysRegister(),
                     /* forceUpdateNetworkState= */ !updateNetworkStateOnce);
 
+            final var traceNetLogCaptureMode = getTraceNetLogCaptureMode();
+
             try (var libLoadTraceEvent =
                     ScopedSysTraceEvent.scoped(
                             "CronetLibraryLoader#initializeOnInitThread waiting on library load")) {
@@ -272,11 +323,16 @@
                 // NetworkChangeNotifierAndroid is created, so as to avoid receiving
                 // the undesired initial network change observer notification, which
                 // will cause active requests to fail with ERR_NETWORK_CHANGED.
-                CronetLibraryLoaderJni.get().cronetInitOnInitThread(!updateNetworkStateOnce);
+                CronetLibraryLoaderJni.get()
+                        .cronetInitOnInitThread(!updateNetworkStateOnce, traceNetLogCaptureMode);
             }
         }
     }
 
+    public static @NetLogCaptureMode int getTraceNetLogCaptureModeForTesting() {
+        return CronetLibraryLoaderJni.get().getTraceNetLogCaptureModeForTesting(); // IN-TEST
+    }
+
     /** Run {@code r} on the initialization thread. */
     public static void postToInitThread(Runnable r) {
         if (onInitThread()) {
@@ -349,7 +405,13 @@
         // Native methods are implemented in cronet_library_loader.cc.
         void nativeInit(boolean initializePerfetto);
 
-        void cronetInitOnInitThread(boolean updateNetworkStateFromNative);
+        void cronetInitOnInitThread(
+                boolean updateNetworkStateFromNative,
+                @NetLogCaptureMode @JniType("net::NetLogCaptureMode") int traceNetLogCaptureMode);
+
+        @NetLogCaptureMode
+        @JniType("net::NetLogCaptureMode")
+        int getTraceNetLogCaptureModeForTesting(); // IN-TEST
 
         String getCronetVersion();
 
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
index c5b6c7e..6e420424 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
@@ -13,6 +13,9 @@
 import static org.chromium.net.CronetTestRule.getTestStorage;
 import static org.chromium.net.truth.UrlResponseInfoSubject.assertThat;
 
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ApplicationInfo;
 import android.net.Network;
 import android.os.Build;
 import android.os.Bundle;
@@ -49,6 +52,8 @@
 import org.chromium.net.httpflags.BaseFeature;
 import org.chromium.net.httpflags.FlagValue;
 import org.chromium.net.httpflags.HttpFlagsLoader;
+import org.chromium.net.impl.AndroidOsBuild;
+import org.chromium.net.impl.AndroidOsSystemProperties;
 import org.chromium.net.impl.CronetEngineBuilderImpl;
 import org.chromium.net.impl.CronetExceptionImpl;
 import org.chromium.net.impl.CronetLibraryLoader;
@@ -65,6 +70,7 @@
 import java.io.FileReader;
 import java.net.URL;
 import java.util.Arrays;
+import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Executor;
@@ -412,6 +418,144 @@
         runRequestWhileExpectingLog(marker, /* shouldBeLogged= */ false);
     }
 
+    @NetLogCaptureMode
+    int getTraceNetLogCaptureMode(
+            String traceNetLogSystemPropertyValue, String buildType, boolean appIsDebuggable) {
+        mTestRule
+                .getTestFramework()
+                .interceptContext(
+                        new ContextInterceptor() {
+                            @Override
+                            public Context interceptContext(Context context) {
+                                return new ContextWrapper(context) {
+                                    @Override
+                                    public ApplicationInfo getApplicationInfo() {
+                                        var applicationInfo =
+                                                new ApplicationInfo(context.getApplicationInfo());
+                                        applicationInfo.flags &= ~ApplicationInfo.FLAG_DEBUGGABLE;
+                                        if (appIsDebuggable) {
+                                            applicationInfo.flags |=
+                                                    ApplicationInfo.FLAG_DEBUGGABLE;
+                                        }
+                                        return applicationInfo;
+                                    }
+                                };
+                            }
+                        });
+        try (var withSystemPropertyOverrides =
+                        new AndroidOsSystemProperties.WithOverridesForTesting(
+                                traceNetLogSystemPropertyValue == null
+                                        ? Map.of()
+                                        : Map.of(
+                                                CronetLibraryLoader
+                                                        .TRACE_NET_LOG_SYSTEM_PROPERTY_KEY,
+                                                traceNetLogSystemPropertyValue));
+                var withBuildOverride =
+                        new AndroidOsBuild.WithOverrideForTesting(
+                                new AndroidOsBuild(/* type= */ buildType))) {
+            // Make sure Cronet is ready, so that we don't race against the init thread which is
+            // where trace netlog is initialized.
+            runOneRequest();
+            return CronetLibraryLoader.getTraceNetLogCaptureModeForTesting();
+        }
+    }
+
+    @Test
+    @SmallTest
+    @IgnoreFor(
+            implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM},
+            reason =
+                    "FALLBACK: does not support netlog. "
+                            + "AOSP_PLATFORM: emulator image does not have this code yet.")
+    public void testDefaultTraceNetLogCaptureMode() throws Exception {
+        assertThat(
+                        getTraceNetLogCaptureMode(
+                                /* traceNetLogSystemPropertyValue= */ null,
+                                /* buildType= */ "user",
+                                /* appIsDebuggable= */ false))
+                .isEqualTo(NetLogCaptureMode.HEAVILY_REDACTED);
+    }
+
+    @Test
+    @SmallTest
+    @IgnoreFor(
+            implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM},
+            reason =
+                    "FALLBACK: does not support netlog. "
+                            + "AOSP_PLATFORM: emulator image does not have this code yet.")
+    public void testSetTraceNetLogCaptureModeToHeavilyRedacted() throws Exception {
+        assertThat(
+                        getTraceNetLogCaptureMode(
+                                /* traceNetLogSystemPropertyValue= */ "heavily_redacted",
+                                /* buildType= */ "user",
+                                /* appIsDebuggable= */ false))
+                .isEqualTo(NetLogCaptureMode.HEAVILY_REDACTED);
+    }
+
+    @Test
+    @SmallTest
+    @IgnoreFor(
+            implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM},
+            reason =
+                    "FALLBACK: does not support netlog. "
+                            + "AOSP_PLATFORM: emulator image does not have this code yet.")
+    public void testSetTraceNetLogCaptureModeOnDebuggableApp() throws Exception {
+        assertThat(
+                        getTraceNetLogCaptureMode(
+                                /* traceNetLogSystemPropertyValue= */ "on",
+                                /* buildType= */ "user",
+                                /* appIsDebuggable= */ true))
+                .isEqualTo(NetLogCaptureMode.DEFAULT);
+    }
+
+    @Test
+    @SmallTest
+    @IgnoreFor(
+            implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM},
+            reason =
+                    "FALLBACK: does not support netlog. "
+                            + "AOSP_PLATFORM: emulator image does not have this code yet.")
+    public void testSetTraceNetLogCaptureModeOnDebugBuild() throws Exception {
+        assertThat(
+                        getTraceNetLogCaptureMode(
+                                /* traceNetLogSystemPropertyValue= */ "on",
+                                /* buildType= */ "userdebug",
+                                /* appIsDebuggable= */ false))
+                .isEqualTo(NetLogCaptureMode.DEFAULT);
+    }
+
+    @Test
+    @SmallTest
+    @IgnoreFor(
+            implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM},
+            reason =
+                    "FALLBACK: does not support netlog. "
+                            + "AOSP_PLATFORM: emulator image does not have this code yet.")
+    public void testCannotSetTraceNetLogCaptureModeOnNonDebug() throws Exception {
+        assertThat(
+                        getTraceNetLogCaptureMode(
+                                /* traceNetLogSystemPropertyValue= */ "on",
+                                /* buildType= */ "user",
+                                /* appIsDebuggable= */ false))
+                .isEqualTo(NetLogCaptureMode.HEAVILY_REDACTED);
+    }
+
+    @Test
+    @SmallTest
+    @IgnoreFor(
+            implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM},
+            reason =
+                    "FALLBACK: does not support netlog. "
+                            + "AOSP_PLATFORM: emulator image does not have this code yet.")
+    public void testIgnoresUnknownTraceNetLogSystemPropertyValue() throws Exception {
+        assertThat(
+                        getTraceNetLogCaptureMode(
+                                /* traceNetLogSystemPropertyValue= */ "invalid",
+                                /* buildType= */ "userdebug",
+                                /* appIsDebuggable= */ true))
+                .isEqualTo(NetLogCaptureMode.HEAVILY_REDACTED);
+    }
+
     @Test
     @SmallTest
     @SuppressWarnings("deprecation")
diff --git a/components/error_page/content/browser/net_error_auto_reloader.cc b/components/error_page/content/browser/net_error_auto_reloader.cc
index 3f5650a..99af619 100644
--- a/components/error_page/content/browser/net_error_auto_reloader.cc
+++ b/components/error_page/content/browser/net_error_auto_reloader.cc
@@ -82,9 +82,9 @@
   using ShouldSuppressCallback =
       base::OnceCallback<bool(content::NavigationHandle*)>;
 
-  IgnoreDuplicateErrorThrottle(content::NavigationHandle* handle,
+  IgnoreDuplicateErrorThrottle(content::NavigationThrottleRegistry& registry,
                                ShouldSuppressCallback should_suppress)
-      : content::NavigationThrottle(handle),
+      : content::NavigationThrottle(registry),
         should_suppress_(std::move(should_suppress)) {
     DCHECK(should_suppress_);
   }
@@ -96,8 +96,9 @@
   // content::NavigationThrottle:
   content::NavigationThrottle::ThrottleCheckResult WillFailRequest() override {
     DCHECK(should_suppress_);
-    if (std::move(should_suppress_).Run(navigation_handle()))
+    if (std::move(should_suppress_).Run(navigation_handle())) {
       return content::NavigationThrottle::ThrottleAction::CANCEL;
+    }
     return content::NavigationThrottle::ThrottleAction::PROCEED;
   }
 
@@ -135,28 +136,31 @@
 NetErrorAutoReloader::~NetErrorAutoReloader() {
   // NOTE: Tests may call `DisableConnectionChangeObservationForTesting` to null
   // this out.
-  if (connection_tracker_)
+  if (connection_tracker_) {
     connection_tracker_->RemoveNetworkConnectionObserver(this);
+  }
 }
 
 // static
-std::unique_ptr<content::NavigationThrottle>
-NetErrorAutoReloader::MaybeCreateThrottleFor(
-    content::NavigationHandle* handle) {
-  if (!handle->IsInPrimaryMainFrame())
-    return nullptr;
+void NetErrorAutoReloader::MaybeCreateAndAddNavigationThrottle(
+    content::NavigationThrottleRegistry& registry) {
+  content::NavigationHandle& handle = registry.GetNavigationHandle();
+  if (!handle.IsInPrimaryMainFrame()) {
+    return;
+  }
 
   // Note that `CreateForWebContents` is a no-op if `contents` already has a
   // NetErrorAutoReloader. See WebContentsUserData.
-  content::WebContents* contents = handle->GetWebContents();
+  content::WebContents* contents = handle.GetWebContents();
   CreateForWebContents(contents);
-  return FromWebContents(contents)->MaybeCreateThrottle(handle);
+  FromWebContents(contents)->MaybeCreateAndAdd(registry);
 }
 
 void NetErrorAutoReloader::DidStartNavigation(
     content::NavigationHandle* handle) {
-  if (!handle->IsInPrimaryMainFrame())
+  if (!handle->IsInPrimaryMainFrame()) {
     return;
+  }
 
   // Suppress automatic reload as long as any navigations are pending.
   PauseAutoReloadTimerIfRunning();
@@ -165,16 +169,18 @@
 
 void NetErrorAutoReloader::DidFinishNavigation(
     content::NavigationHandle* handle) {
-  if (!handle->IsInPrimaryMainFrame())
+  if (!handle->IsInPrimaryMainFrame()) {
     return;
+  }
 
   pending_navigations_.erase(handle);
   if (!handle->HasCommitted()) {
     // This navigation was cancelled and not committed. If there are still other
     // pending navigations, or we aren't sitting on a error page which allows
     // auto-reload, there's nothing to do.
-    if (!pending_navigations_.empty() || !current_reloadable_error_page_info_)
+    if (!pending_navigations_.empty() || !current_reloadable_error_page_info_) {
       return;
+    }
 
     // The last pending navigation was just cancelled and we're sitting on an
     // error page which allows auto-reload. Schedule the next auto-reload
@@ -208,8 +214,9 @@
   // We only schedule a reload if there are no other pending navigations.
   // If there are and they end up getting terminated without a commit, we
   // will schedule the next auto-reload at that time.
-  if (pending_navigations_.empty())
+  if (pending_navigations_.empty()) {
     ScheduleNextAutoReload();
+  }
 }
 
 void NetErrorAutoReloader::NavigationStopped() {
@@ -257,8 +264,9 @@
     network::mojom::ConnectionType type) {
   // NOTE: Tests may call `DisableConnectionChangeObservationForTesting` to null
   // this out.
-  if (connection_tracker_)
+  if (connection_tracker_) {
     OnConnectionChanged(type);
+  }
 }
 
 bool NetErrorAutoReloader::IsWebContentsVisible() {
@@ -277,14 +285,16 @@
 }
 
 void NetErrorAutoReloader::ResumeAutoReloadIfPaused() {
-  if (current_reloadable_error_page_info_ && !next_reload_timer_)
+  if (current_reloadable_error_page_info_ && !next_reload_timer_) {
     ScheduleNextAutoReload();
+  }
 }
 
 void NetErrorAutoReloader::ScheduleNextAutoReload() {
   DCHECK(current_reloadable_error_page_info_);
-  if (!is_online_ || !IsWebContentsVisible())
+  if (!is_online_ || !IsWebContentsVisible()) {
     return;
+  }
 
   // Note that Unretained is safe here because base::OneShotTimer will never
   // run its callback once destructed.
@@ -297,26 +307,28 @@
 
 void NetErrorAutoReloader::ReloadMainFrame() {
   DCHECK(current_reloadable_error_page_info_);
-  if (!is_online_ || !IsWebContentsVisible())
+  if (!is_online_ || !IsWebContentsVisible()) {
     return;
+  }
 
   ++num_reloads_for_current_error_;
   is_auto_reload_in_progress_ = true;
   web_contents()->GetPrimaryMainFrame()->Reload();
 }
 
-std::unique_ptr<content::NavigationThrottle>
-NetErrorAutoReloader::MaybeCreateThrottle(content::NavigationHandle* handle) {
-  DCHECK(handle->IsInPrimaryMainFrame());
+void NetErrorAutoReloader::MaybeCreateAndAdd(
+    content::NavigationThrottleRegistry& registry) {
+  content::NavigationHandle& handle = registry.GetNavigationHandle();
+  DCHECK(handle.IsInPrimaryMainFrame());
   if (!current_reloadable_error_page_info_ ||
-      current_reloadable_error_page_info_->url != handle->GetURL() ||
+      current_reloadable_error_page_info_->url != handle.GetURL() ||
       !is_auto_reload_in_progress_) {
-    return nullptr;
+    return;
   }
 
-  return std::make_unique<IgnoreDuplicateErrorThrottle>(
-      handle, base::BindOnce(&NetErrorAutoReloader::ShouldSuppressErrorPage,
-                             base::Unretained(this)));
+  registry.AddThrottle(std::make_unique<IgnoreDuplicateErrorThrottle>(
+      registry, base::BindOnce(&NetErrorAutoReloader::ShouldSuppressErrorPage,
+                               base::Unretained(this))));
 }
 
 bool NetErrorAutoReloader::ShouldSuppressErrorPage(
diff --git a/components/error_page/content/browser/net_error_auto_reloader.h b/components/error_page/content/browser/net_error_auto_reloader.h
index 2f493d81..616cc32b 100644
--- a/components/error_page/content/browser/net_error_auto_reloader.h
+++ b/components/error_page/content/browser/net_error_auto_reloader.h
@@ -24,7 +24,7 @@
 
 namespace content {
 class NavigationHandle;
-class NavigationThrottle;
+class NavigationThrottleRegistry;
 class WebContents;
 }  // namespace content
 
@@ -35,8 +35,8 @@
 // excludes errors that aren't connectivity related since a reload doesn't
 // generally fix them (e.g. SSL errors or when the client blocked the request).
 // To use this behavior as a Content embedder, simply call the static
-// `MaybeCreateNavigationThrottle()` method from within your implementation of
-// ContentBrowserClient::CreateThrottlesForNavigation.
+// `MaybeCreateAndAddNavigationThrottle()` method from within your
+// implementation of ContentBrowserClient::CreateThrottlesForNavigation.
 class NetErrorAutoReloader
     : public content::WebContentsObserver,
       public content::WebContentsUserData<NetErrorAutoReloader>,
@@ -51,8 +51,8 @@
   // embedders wanting to use NetErrorAutoReload's behavior, it's sufficient to
   // call this from ContentBrowserClient::CreateThrottlesForNavigation for each
   // navigation processed.
-  static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
-      content::NavigationHandle* handle);
+  static void MaybeCreateAndAddNavigationThrottle(
+      content::NavigationThrottleRegistry& registry);
 
   // content::WebContentsObserver:
   void DidStartNavigation(content::NavigationHandle* handle) override;
@@ -89,8 +89,8 @@
   void ResumeAutoReloadIfPaused();
   void ScheduleNextAutoReload();
   void ReloadMainFrame();
-  std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottle(
-      content::NavigationHandle* handle);
+  void MaybeCreateAndAdd(
+      content::NavigationThrottleRegistry& registry);
   bool ShouldSuppressErrorPage(content::NavigationHandle* handle);
 
   struct ErrorPageInfo {
diff --git a/components/error_page/content/browser/net_error_auto_reloader_browsertest.cc b/components/error_page/content/browser/net_error_auto_reloader_browsertest.cc
index 819a3b2..51d4dd93 100644
--- a/components/error_page/content/browser/net_error_auto_reloader_browsertest.cc
+++ b/components/error_page/content/browser/net_error_auto_reloader_browsertest.cc
@@ -175,9 +175,8 @@
     content::ShellContentBrowserClient::Get()
         ->set_create_throttles_for_navigation_callback(base::BindRepeating(
             [](content::NavigationThrottleRegistry& registry) -> void {
-              registry.MaybeAddThrottle(
-                  NetErrorAutoReloader::MaybeCreateThrottleFor(
-                      &registry.GetNavigationHandle()));
+              NetErrorAutoReloader::MaybeCreateAndAddNavigationThrottle(
+                  registry);
             }));
   }
 
diff --git a/components/ntp_tiles/custom_links_manager.h b/components/ntp_tiles/custom_links_manager.h
index 086eaa1..8a8e0c6 100644
--- a/components/ntp_tiles/custom_links_manager.h
+++ b/components/ntp_tiles/custom_links_manager.h
@@ -47,8 +47,10 @@
   // history is cleared. Returns false and does nothing if custom links has
   // already been initialized.
   virtual bool Initialize(const NTPTilesVector& tiles) = 0;
+
   // Uninitializes custom links and clears the current links from storage.
   virtual void Uninitialize() = 0;
+
   // True if custom links is initialized and Most Visited tiles have been
   // replaced by custom links.
   virtual bool IsInitialized() const = 0;
@@ -56,11 +58,20 @@
   // Returns the current links.
   virtual const std::vector<Link>& GetLinks() const = 0;
 
+  // Adds a link to position |pos|. This link will not be deleted when history
+  // is cleared. Returns false and does nothing if custom links is not
+  // initialized, |url| is invalid, we're at the maximum number of links, or
+  // |url| already exists in the list.
+  virtual bool AddLinkTo(const GURL& url,
+                         const std::u16string& title,
+                         size_t pos) = 0;
+
   // Adds a link to the end of the list. This link will not be deleted when
   // history is cleared. Returns false and does nothing if custom links is not
   // initialized, |url| is invalid, we're at the maximum number of links, or
   // |url| already exists in the list.
   virtual bool AddLink(const GURL& url, const std::u16string& title) = 0;
+
   // Updates the URL and/or title of the link specified by |url|. The link will
   // no longer be considered Most Visited. Returns false and does nothing if
   // custom links is not initialized, either URL is invalid, |url| does not
@@ -69,15 +80,18 @@
   virtual bool UpdateLink(const GURL& url,
                           const GURL& new_url,
                           const std::u16string& new_title) = 0;
+
   // Moves the specified link from its current index and inserts it at
   // |new_pos|. Returns false and does nothing if custom links is not
   // initialized, |url| is invalid, |url| does not exist in the list, or
   // |new_pos| is invalid/already the current index.
   virtual bool ReorderLink(const GURL& url, size_t new_pos) = 0;
+
   // Deletes the link with the specified |url|. Returns false and does nothing
   // if custom links is not initialized, |url| is invalid, or |url| does not
   // exist in the list.
   virtual bool DeleteLink(const GURL& url) = 0;
+
   // Restores the previous state of the list of links. Used to undo the previous
   // action (add, edit, delete, etc.). Returns false and does nothing if custom
   // links is not initialized or there is no previous state to restore.
diff --git a/components/ntp_tiles/custom_links_manager_impl.cc b/components/ntp_tiles/custom_links_manager_impl.cc
index 4ba4ec5b..3dba3fa 100644
--- a/components/ntp_tiles/custom_links_manager_impl.cc
+++ b/components/ntp_tiles/custom_links_manager_impl.cc
@@ -79,8 +79,9 @@
   return current_links_;
 }
 
-bool CustomLinksManagerImpl::AddLink(const GURL& url,
-                                     const std::u16string& title) {
+bool CustomLinksManagerImpl::AddLinkTo(const GURL& url,
+                                       const std::u16string& title,
+                                       size_t pos) {
   if (!IsInitialized() || !url.is_valid() ||
       current_links_.size() == ntp_tiles::kMaxNumCustomLinks) {
     return false;
@@ -90,12 +91,19 @@
     return false;
   }
 
+  pos = std::min(pos, current_links_.size());
+
   previous_links_ = current_links_;
-  current_links_.emplace_back(Link{url, title, false});
+  current_links_.insert(current_links_.begin() + pos, Link{url, title, false});
   StoreLinks();
   return true;
 }
 
+bool CustomLinksManagerImpl::AddLink(const GURL& url,
+                                     const std::u16string& title) {
+  return AddLinkTo(url, title, current_links_.size());
+}
+
 bool CustomLinksManagerImpl::UpdateLink(const GURL& url,
                                         const GURL& new_url,
                                         const std::u16string& new_title) {
diff --git a/components/ntp_tiles/custom_links_manager_impl.h b/components/ntp_tiles/custom_links_manager_impl.h
index eafe674..8cf8f99c 100644
--- a/components/ntp_tiles/custom_links_manager_impl.h
+++ b/components/ntp_tiles/custom_links_manager_impl.h
@@ -49,6 +49,9 @@
 
   const std::vector<Link>& GetLinks() const override;
 
+  bool AddLinkTo(const GURL& url,
+                 const std::u16string& title,
+                 size_t pos) override;
   bool AddLink(const GURL& url, const std::u16string& title) override;
   bool UpdateLink(const GURL& url,
                   const GURL& new_url,
diff --git a/components/ntp_tiles/custom_links_manager_impl_unittest.cc b/components/ntp_tiles/custom_links_manager_impl_unittest.cc
index a637cbd..77ada66 100644
--- a/components/ntp_tiles/custom_links_manager_impl_unittest.cc
+++ b/components/ntp_tiles/custom_links_manager_impl_unittest.cc
@@ -180,6 +180,20 @@
   EXPECT_EQ(expected_links, custom_links_->GetLinks());
 }
 
+TEST_F(CustomLinksManagerImplTest, AddLinkTo) {
+  // Initialize.
+  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
+  ASSERT_EQ(initial_links, custom_links_->GetLinks());
+
+  // Add link in front.
+  std::vector<Link> expected_links = initial_links;
+  expected_links.insert(expected_links.begin(),
+                        Link{GURL(kTestUrl), kTestTitle16, false});
+  EXPECT_TRUE(custom_links_->AddLinkTo(GURL(kTestUrl), kTestTitle16, 0U));
+  EXPECT_EQ(expected_links, custom_links_->GetLinks());
+}
+
 TEST_F(CustomLinksManagerImplTest, AddLinkWhenAtMaxLinks) {
   // Initialize.
   std::vector<Link> initial_links = FillTestLinks(kTestCaseMax);
diff --git a/components/ntp_tiles/most_visited_sites.cc b/components/ntp_tiles/most_visited_sites.cc
index a3ccafc..2e7a96a6 100644
--- a/components/ntp_tiles/most_visited_sites.cc
+++ b/components/ntp_tiles/most_visited_sites.cc
@@ -359,6 +359,14 @@
   return is_shortcuts_visible_;
 }
 
+bool MostVisitedSites::AddCustomLinkTo(const GURL& url,
+                                       const std::u16string& title,
+                                       size_t pos) {
+  return ApplyCustomLinksAction(base::BindOnce(
+      &CustomLinksManager::AddLinkTo,
+      base::Unretained(custom_links_manager_.get()), url, title, pos));
+}
+
 bool MostVisitedSites::AddCustomLink(const GURL& url,
                                      const std::u16string& title) {
   return ApplyCustomLinksAction(base::BindOnce(
diff --git a/components/ntp_tiles/most_visited_sites.h b/components/ntp_tiles/most_visited_sites.h
index 1f73b4cc..c620743 100644
--- a/components/ntp_tiles/most_visited_sites.h
+++ b/components/ntp_tiles/most_visited_sites.h
@@ -203,10 +203,17 @@
   // Returns whether NTP tiles should be shown.
   bool IsShortcutsVisible() const;
 
-  // Adds a custom link. If the number of current links is maxed, returns false
-  // and does nothing. Will initialize custom links if they have not been
-  // initialized yet, unless the action fails. Custom links must be enabled.
+  // Adds a custom link at position |pos|, bumping existing links. If the number
+  // of current links is maxed, returns false and does nothing. Will initialize
+  // custom links if they have not been initialized yet, unless the action
+  // fails. Custom links must be enabled.
+  bool AddCustomLinkTo(const GURL& url,
+                       const std::u16string& title,
+                       size_t pos);
+
+  // Similar to AddCustomLinkTo(), but add to end of list.
   bool AddCustomLink(const GURL& url, const std::u16string& title);
+
   // Updates the URL and/or title of the custom link specified by |url|. If
   // |url| does not exist or |new_url| already exists in the custom link list,
   // returns false and does nothing. Will initialize custom links if they have
diff --git a/components/ntp_tiles/most_visited_sites_unittest.cc b/components/ntp_tiles/most_visited_sites_unittest.cc
index 3c8c8a0..681836f 100644
--- a/components/ntp_tiles/most_visited_sites_unittest.cc
+++ b/components/ntp_tiles/most_visited_sites_unittest.cc
@@ -227,6 +227,8 @@
   MOCK_METHOD0(Uninitialize, void());
   MOCK_CONST_METHOD0(IsInitialized, bool());
   MOCK_CONST_METHOD0(GetLinks, const std::vector<CustomLinksManager::Link>&());
+  MOCK_METHOD3(AddLinkTo,
+               bool(const GURL& url, const std::u16string& title, size_t pos));
   MOCK_METHOD2(AddLink, bool(const GURL& url, const std::u16string& title));
   MOCK_METHOD3(UpdateLink,
                bool(const GURL& url,
@@ -1634,6 +1636,20 @@
       .WillOnce(ReturnRef(expected_links));
   most_visited_sites_->UndoCustomLinkAction();
   base::RunLoop().RunUntilIdle();
+
+  // Exercise AddCustomLinkTo().
+  EXPECT_CALL(*mock_custom_links_manager_, Initialize(_))
+      .WillOnce(Return(true));
+  EXPECT_CALL(*mock_custom_links_manager_, AddLinkTo(_, _, _))
+      .WillOnce(Return(true));
+  EXPECT_CALL(*mock_custom_links_manager_, IsInitialized())
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(*mock_custom_links_manager_, GetLinks())
+      .WillRepeatedly(ReturnRef(expected_links));
+  EXPECT_CALL(mock_observer_, OnURLsAvailable(_))
+      .WillOnce(SaveArg<0>(&sections));
+  most_visited_sites_->AddCustomLinkTo(GURL("test2.com"), u"test2", 0);
+  base::RunLoop().RunUntilIdle();
 }
 
 TEST_F(MostVisitedSitesWithCustomLinksTest,
diff --git a/components/offline_items_collection/core/offline_item.cc b/components/offline_items_collection/core/offline_item.cc
index 8e26782f..8bda413 100644
--- a/components/offline_items_collection/core/offline_item.cc
+++ b/components/offline_items_collection/core/offline_item.cc
@@ -21,19 +21,6 @@
 
 ContentId::~ContentId() = default;
 
-bool ContentId::operator==(const ContentId& content_id) const {
-  return name_space == content_id.name_space && id == content_id.id;
-}
-
-bool ContentId::operator!=(const ContentId& content_id) const {
-  return !(content_id == *this);
-}
-
-bool ContentId::operator<(const ContentId& content_id) const {
-  return std::tie(name_space, id) <
-         std::tie(content_id.name_space, content_id.id);
-}
-
 // -----------------------------------------------------------------------------
 // OfflineItem.
 OfflineItem::Progress::Progress()
diff --git a/components/offline_items_collection/core/offline_item.h b/components/offline_items_collection/core/offline_item.h
index 088e782..04ce3a9a 100644
--- a/components/offline_items_collection/core/offline_item.h
+++ b/components/offline_items_collection/core/offline_item.h
@@ -38,11 +38,8 @@
 
   ~ContentId();
 
-  bool operator==(const ContentId& content_id) const;
-
-  bool operator!=(const ContentId& content_id) const;
-
-  bool operator<(const ContentId& content_id) const;
+  friend bool operator==(const ContentId&, const ContentId&) = default;
+  friend auto operator<=>(const ContentId&, const ContentId&) = default;
 };
 
 // A Java counterpart will be generated for this enum.
diff --git a/components/optimization_guide/core/model_execution/aqa_response_parser.cc b/components/optimization_guide/core/model_execution/aqa_response_parser.cc
index b7a5dfb..98c3862 100644
--- a/components/optimization_guide/core/model_execution/aqa_response_parser.cc
+++ b/components/optimization_guide/core/model_execution/aqa_response_parser.cc
@@ -108,6 +108,11 @@
     : config_(config) {}
 AqaResponseParser::~AqaResponseParser() = default;
 
+// static
+bool AqaResponseParser::CanParse(std::string_view proto_type) {
+  return proto_type == "optimization_guide.proto.HistoryAnswerResponse";
+}
+
 void AqaResponseParser::ParseAsync(const std::string& redacted_output,
                                    ResultCallback result_callback) const {
   std::move(result_callback).Run(ParseAqaResponse(redacted_output));
@@ -118,16 +123,4 @@
   return true;
 }
 
-AqaResponseParserFactory::AqaResponseParserFactory() = default;
-AqaResponseParserFactory::~AqaResponseParserFactory() = default;
-
-std::unique_ptr<ResponseParser> AqaResponseParserFactory::CreateParser(
-    const proto::OnDeviceModelExecutionOutputConfig& config) {
-  // Can only parse to HistoryAnswerResponse.
-  if (config.proto_type() != "optimization_guide.proto.HistoryAnswerResponse") {
-    return nullptr;
-  }
-  return std::make_unique<AqaResponseParser>(config);
-}
-
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/aqa_response_parser.h b/components/optimization_guide/core/model_execution/aqa_response_parser.h
index c820ac31..6052f13 100644
--- a/components/optimization_guide/core/model_execution/aqa_response_parser.h
+++ b/components/optimization_guide/core/model_execution/aqa_response_parser.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_AQA_RESPONSE_PARSER_H_
 #define COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_AQA_RESPONSE_PARSER_H_
 
+#include <string_view>
+
 #include "base/functional/callback_forward.h"
 #include "components/optimization_guide/core/model_execution/response_parser.h"
 #include "components/optimization_guide/proto/on_device_model_execution_config.pb.h"
@@ -16,6 +18,8 @@
       const proto::OnDeviceModelExecutionOutputConfig& config);
   ~AqaResponseParser() override;
 
+  static bool CanParse(std::string_view proto_type);
+
   // Parse redacted model output, returns parsed data via result_callback.
   void ParseAsync(const std::string& model_output,
                   ResultCallback result_callback) const override;
@@ -26,16 +30,6 @@
   proto::OnDeviceModelExecutionOutputConfig config_;
 };
 
-class AqaResponseParserFactory : public ResponseParserFactory {
- public:
-  AqaResponseParserFactory();
-  ~AqaResponseParserFactory() override;
-
-  // Constructs a parser for the given config.
-  std::unique_ptr<ResponseParser> CreateParser(
-      const proto::OnDeviceModelExecutionOutputConfig& config) override;
-};
-
 }  // namespace optimization_guide
 
 #endif  // COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_AQA_RESPONSE_PARSER_H_
diff --git a/components/optimization_guide/core/model_execution/aqa_response_parser_unittest.cc b/components/optimization_guide/core/model_execution/aqa_response_parser_unittest.cc
index 722cbbb..7db5fff 100644
--- a/components/optimization_guide/core/model_execution/aqa_response_parser_unittest.cc
+++ b/components/optimization_guide/core/model_execution/aqa_response_parser_unittest.cc
@@ -20,10 +20,9 @@
 TEST(AqaResponseParserTest, Valid) {
   proto::OnDeviceModelExecutionOutputConfig cfg;
   cfg.set_proto_type("optimization_guide.proto.HistoryAnswerResponse");
-  auto parser = AqaResponseParserFactory().CreateParser(cfg);
 
   ParseResponseFuture response_future;
-  parser->ParseAsync(
+  AqaResponseParser(cfg).ParseAsync(
       "0001,0003 has the answer. The answer is The fox jumps over the dog",
       response_future.GetCallback());
   auto maybe_response = response_future.Get();
@@ -39,10 +38,10 @@
 TEST(AqaResponseParserTest, Unanswerable) {
   proto::OnDeviceModelExecutionOutputConfig cfg;
   cfg.set_proto_type("optimization_guide.proto.HistoryAnswerResponse");
-  auto parser = AqaResponseParserFactory().CreateParser(cfg);
 
   ParseResponseFuture response_future;
-  parser->ParseAsync("unanswerable.", response_future.GetCallback());
+  AqaResponseParser(cfg).ParseAsync("unanswerable.",
+                                    response_future.GetCallback());
   auto maybe_response = response_future.Get();
 
   ASSERT_TRUE(maybe_response.has_value());
@@ -54,10 +53,9 @@
 TEST(AqaResponseParserTest, RecursiveAnswer) {
   proto::OnDeviceModelExecutionOutputConfig cfg;
   cfg.set_proto_type("optimization_guide.proto.HistoryAnswerResponse");
-  auto parser = AqaResponseParserFactory().CreateParser(cfg);
 
   ParseResponseFuture response_future;
-  parser->ParseAsync(
+  AqaResponseParser(cfg).ParseAsync(
       "0001,0003 has the answer. The answer is ID:0002 has the answer. The "
       "answer is the fox jumps over the dog.",
       response_future.GetCallback());
@@ -70,11 +68,11 @@
 TEST(AqaResponseParserTest, RecursiveUnanswerable) {
   proto::OnDeviceModelExecutionOutputConfig cfg;
   cfg.set_proto_type("optimization_guide.proto.HistoryAnswerResponse");
-  auto parser = AqaResponseParserFactory().CreateParser(cfg);
 
   ParseResponseFuture response_future;
-  parser->ParseAsync("0001,0003 has the answer. The answer is unanswerable.",
-                     response_future.GetCallback());
+  AqaResponseParser(cfg).ParseAsync(
+      "0001,0003 has the answer. The answer is unanswerable.",
+      response_future.GetCallback());
   auto maybe_response = response_future.Get();
 
   ASSERT_TRUE(maybe_response.has_value());
@@ -87,9 +85,8 @@
   proto::OnDeviceModelExecutionOutputConfig cfg;
   cfg.set_proto_type("optimization_guide.proto.HistoryAnswerResponse");
   cfg.set_suppress_parsing_incomplete_output(false);
-  auto parser = AqaResponseParserFactory().CreateParser(cfg);
 
-  EXPECT_TRUE(parser->SuppressParsingIncompleteResponse());
+  EXPECT_TRUE(AqaResponseParser(cfg).SuppressParsingIncompleteResponse());
 }
 
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/json_response_parser.cc b/components/optimization_guide/core/model_execution/json_response_parser.cc
index f00dc55..8cb6a71 100644
--- a/components/optimization_guide/core/model_execution/json_response_parser.cc
+++ b/components/optimization_guide/core/model_execution/json_response_parser.cc
@@ -50,12 +50,4 @@
   return true;
 }
 
-JsonResponseParserFactory::JsonResponseParserFactory() = default;
-JsonResponseParserFactory::~JsonResponseParserFactory() = default;
-
-std::unique_ptr<ResponseParser> JsonResponseParserFactory::CreateParser(
-    const proto::OnDeviceModelExecutionOutputConfig& config) {
-  return std::make_unique<JsonResponseParser>(config);
-}
-
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/json_response_parser.h b/components/optimization_guide/core/model_execution/json_response_parser.h
index 98645e4..bce94c70 100644
--- a/components/optimization_guide/core/model_execution/json_response_parser.h
+++ b/components/optimization_guide/core/model_execution/json_response_parser.h
@@ -33,17 +33,6 @@
   proto::OnDeviceModelExecutionOutputConfig config_;
 };
 
-// Constructs JsonResponseParsers based on a config.
-class JsonResponseParserFactory : public ResponseParserFactory {
- public:
-  JsonResponseParserFactory();
-  ~JsonResponseParserFactory() override;
-
-  // Constructs a parser for the given config.
-  std::unique_ptr<ResponseParser> CreateParser(
-      const proto::OnDeviceModelExecutionOutputConfig& config) override;
-};
-
 }  // namespace optimization_guide
 
 #endif  // COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_JSON_RESPONSE_PARSER_H_
diff --git a/components/optimization_guide/core/model_execution/json_response_parser_unittest.cc b/components/optimization_guide/core/model_execution/json_response_parser_unittest.cc
index 2a025e8..7fc8916 100644
--- a/components/optimization_guide/core/model_execution/json_response_parser_unittest.cc
+++ b/components/optimization_guide/core/model_execution/json_response_parser_unittest.cc
@@ -21,9 +21,6 @@
 
 class JsonResponseParserTest : public testing::Test {
  public:
-  JsonResponseParserTest() = default;
-  ~JsonResponseParserTest() override = default;
-
   base::test::TaskEnvironment task_environment_;
   data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
 };
@@ -32,7 +29,7 @@
   base::test::TestFuture<ResponseParser::Result> response_future;
   proto::OnDeviceModelExecutionOutputConfig config;
   config.set_proto_type("optimization_guide.proto.TabOrganizationResponse");
-  JsonResponseParserFactory().CreateParser(config)->ParseAsync(
+  JsonResponseParser(config).ParseAsync(
       R"({
         "tabGroups": [
           {
diff --git a/components/optimization_guide/core/model_execution/response_parser.cc b/components/optimization_guide/core/model_execution/response_parser.cc
index 349a196..0e1d924 100644
--- a/components/optimization_guide/core/model_execution/response_parser.cc
+++ b/components/optimization_guide/core/model_execution/response_parser.cc
@@ -6,7 +6,7 @@
 
 namespace optimization_guide {
 
+ResponseParser::ResponseParser() = default;
 ResponseParser::~ResponseParser() = default;
-ResponseParserFactory::~ResponseParserFactory() = default;
 
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/response_parser.h b/components/optimization_guide/core/model_execution/response_parser.h
index 3730c881..aa0a406 100644
--- a/components/optimization_guide/core/model_execution/response_parser.h
+++ b/components/optimization_guide/core/model_execution/response_parser.h
@@ -26,7 +26,12 @@
 // A method for converting model responses to structured data.
 class ResponseParser {
  public:
+  ResponseParser();
   virtual ~ResponseParser() = 0;
+
+  ResponseParser(const ResponseParser&) = delete;
+  ResponseParser& operator=(const ResponseParser&) = delete;
+
   using Result = base::expected<proto::Any, ResponseParsingError>;
   using ResultCallback = base::OnceCallback<void(Result)>;
 
@@ -35,18 +40,6 @@
                           ResultCallback result_callback) const = 0;
 
   virtual bool SuppressParsingIncompleteResponse() const = 0;
-
- protected:
-};
-
-// Constructs response parsers for a registered type.
-class ResponseParserFactory {
- public:
-  virtual ~ResponseParserFactory() = 0;
-
-  // Constructs a parser for the given config.
-  virtual std::unique_ptr<ResponseParser> CreateParser(
-      const proto::OnDeviceModelExecutionOutputConfig& config) = 0;
 };
 
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/response_parser_registry.cc b/components/optimization_guide/core/model_execution/response_parser_registry.cc
index b46f6bd..28622834 100644
--- a/components/optimization_guide/core/model_execution/response_parser_registry.cc
+++ b/components/optimization_guide/core/model_execution/response_parser_registry.cc
@@ -15,15 +15,36 @@
 namespace optimization_guide {
 
 ResponseParserRegistry::ResponseParserRegistry() {
+  const auto simple_response_parser_factory = base::BindRepeating(
+      [](const proto::OnDeviceModelExecutionOutputConfig& config)
+          -> std::unique_ptr<ResponseParser> {
+        return std::make_unique<SimpleResponseParser>(config);
+      });
+
   factories_.emplace(proto::PARSER_KIND_UNSPECIFIED,
-                     std::make_unique<SimpleResponseParserFactory>());
-  factories_.emplace(proto::PARSER_KIND_SIMPLE,
-                     std::make_unique<SimpleResponseParserFactory>());
-  factories_.emplace(proto::PARSER_KIND_JSON,
-                     std::make_unique<JsonResponseParserFactory>());
-  factories_.emplace(proto::PARSER_KIND_AQA,
-                     std::make_unique<AqaResponseParserFactory>());
+                     simple_response_parser_factory);
+  factories_.emplace(proto::PARSER_KIND_SIMPLE, simple_response_parser_factory);
+
+  factories_.emplace(
+      proto::PARSER_KIND_JSON,
+      base::BindRepeating(
+          [](const proto::OnDeviceModelExecutionOutputConfig& config)
+              -> std::unique_ptr<ResponseParser> {
+            return std::make_unique<JsonResponseParser>(config);
+          }));
+
+  factories_.emplace(
+      proto::PARSER_KIND_AQA,
+      base::BindRepeating(
+          [](const proto::OnDeviceModelExecutionOutputConfig& config)
+              -> std::unique_ptr<ResponseParser> {
+            if (!AqaResponseParser::CanParse(config.proto_type())) {
+              return nullptr;
+            }
+            return std::make_unique<AqaResponseParser>(config);
+          }));
 }
+
 ResponseParserRegistry::~ResponseParserRegistry() = default;
 
 const ResponseParserRegistry& ResponseParserRegistry::Get() {
@@ -37,7 +58,7 @@
   if (it == factories_.end()) {
     return nullptr;
   }
-  return it->second->CreateParser(config);
+  return it->second.Run(config);
 }
 
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/response_parser_registry.h b/components/optimization_guide/core/model_execution/response_parser_registry.h
index 348da94..5e32b41 100644
--- a/components/optimization_guide/core/model_execution/response_parser_registry.h
+++ b/components/optimization_guide/core/model_execution/response_parser_registry.h
@@ -32,8 +32,11 @@
       const proto::OnDeviceModelExecutionOutputConfig& config) const;
 
  private:
-  std::map<proto::ParserKind, std::unique_ptr<ResponseParserFactory>>
-      factories_;
+  using ResponseParserFactory =
+      base::RepeatingCallback<std::unique_ptr<ResponseParser>(
+          const proto::OnDeviceModelExecutionOutputConfig&)>;
+
+  std::map<proto::ParserKind, ResponseParserFactory> factories_;
 };
 
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/simple_response_parser.cc b/components/optimization_guide/core/model_execution/simple_response_parser.cc
index 33f1328bf..d75001e 100644
--- a/components/optimization_guide/core/model_execution/simple_response_parser.cc
+++ b/components/optimization_guide/core/model_execution/simple_response_parser.cc
@@ -34,12 +34,4 @@
   return config_.suppress_parsing_incomplete_output();
 }
 
-SimpleResponseParserFactory::SimpleResponseParserFactory() = default;
-SimpleResponseParserFactory::~SimpleResponseParserFactory() = default;
-
-std::unique_ptr<ResponseParser> SimpleResponseParserFactory::CreateParser(
-    const proto::OnDeviceModelExecutionOutputConfig& config) {
-  return std::make_unique<SimpleResponseParser>(config);
-}
-
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/simple_response_parser.h b/components/optimization_guide/core/model_execution/simple_response_parser.h
index b172ebc..dc96478 100644
--- a/components/optimization_guide/core/model_execution/simple_response_parser.h
+++ b/components/optimization_guide/core/model_execution/simple_response_parser.h
@@ -32,17 +32,6 @@
   proto::OnDeviceModelExecutionOutputConfig config_;
 };
 
-// Constructs SimpleResponseParsers based on a config.
-class SimpleResponseParserFactory : public ResponseParserFactory {
- public:
-  SimpleResponseParserFactory();
-  ~SimpleResponseParserFactory() override;
-
-  // Constructs a parser for the given config.
-  std::unique_ptr<ResponseParser> CreateParser(
-      const proto::OnDeviceModelExecutionOutputConfig& config) override;
-};
-
 }  // namespace optimization_guide
 
 #endif  // COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_SIMPLE_RESPONSE_PARSER_H_
diff --git a/components/optimization_guide/core/model_execution/simple_response_parser_unittest.cc b/components/optimization_guide/core/model_execution/simple_response_parser_unittest.cc
index 2d9e8d09..14c2cc9e 100644
--- a/components/optimization_guide/core/model_execution/simple_response_parser_unittest.cc
+++ b/components/optimization_guide/core/model_execution/simple_response_parser_unittest.cc
@@ -20,10 +20,9 @@
   proto::OnDeviceModelExecutionOutputConfig cfg;
   cfg.set_proto_type("optimization_guide.proto.ComposeResponse");
   cfg.mutable_proto_field()->add_proto_descriptors()->set_tag_number(1);
-  auto parser = SimpleResponseParserFactory().CreateParser(cfg);
 
   ParseResponseFuture response_future;
-  parser->ParseAsync("output", response_future.GetCallback());
+  SimpleResponseParser(cfg).ParseAsync("output", response_future.GetCallback());
   auto maybe_metadata = response_future.Get();
 
   ASSERT_TRUE(maybe_metadata.has_value());
@@ -36,10 +35,9 @@
   proto::OnDeviceModelExecutionOutputConfig cfg;
   cfg.set_proto_type("garbage type");
   cfg.mutable_proto_field()->add_proto_descriptors()->set_tag_number(1);
-  auto parser = SimpleResponseParserFactory().CreateParser(cfg);
 
   ParseResponseFuture response_future;
-  parser->ParseAsync("output", response_future.GetCallback());
+  SimpleResponseParser(cfg).ParseAsync("output", response_future.GetCallback());
   auto maybe_metadata = response_future.Get();
 
   EXPECT_FALSE(maybe_metadata.has_value());
@@ -51,10 +49,9 @@
   proto::OnDeviceModelExecutionOutputConfig cfg;
   cfg.set_proto_type("optimization_guide.proto.ComposeResponse");
   cfg.mutable_proto_field()->add_proto_descriptors()->set_tag_number(7);
-  auto parser = SimpleResponseParserFactory().CreateParser(cfg);
 
   ParseResponseFuture response_future;
-  parser->ParseAsync("output", response_future.GetCallback());
+  SimpleResponseParser(cfg).ParseAsync("output", response_future.GetCallback());
   auto maybe_metadata = response_future.Get();
 
   EXPECT_FALSE(maybe_metadata.has_value());
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index 97235e2..9793b53 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit 97235e2248772e44aa4ea07928d220def6a00f66
+Subproject commit 9793b5313489e7bedaba7b2eb051eb37a6cccc2b
diff --git a/components/payments/content/payment_handler_navigation_throttle.cc b/components/payments/content/payment_handler_navigation_throttle.cc
index 27ac418c..35658afbb 100644
--- a/components/payments/content/payment_handler_navigation_throttle.cc
+++ b/components/payments/content/payment_handler_navigation_throttle.cc
@@ -19,8 +19,8 @@
 constexpr char kApplicationJson[] = "application/json";
 
 PaymentHandlerNavigationThrottle::PaymentHandlerNavigationThrottle(
-    content::NavigationHandle* navigation_handle)
-    : content::NavigationThrottle(navigation_handle) {}
+    content::NavigationThrottleRegistry& registry)
+    : content::NavigationThrottle(registry) {}
 
 PaymentHandlerNavigationThrottle::~PaymentHandlerNavigationThrottle() = default;
 
@@ -31,32 +31,35 @@
 // static
 void PaymentHandlerNavigationThrottle::MarkPaymentHandlerWebContents(
     content::WebContents* web_contents) {
-  if (!web_contents)
+  if (!web_contents) {
     return;
+  }
   web_contents->SetUserData(kPaymentHandlerWebContentsUserDataKey,
                             std::make_unique<base::SupportsUserData::Data>());
 }
 
 // static
-std::unique_ptr<PaymentHandlerNavigationThrottle>
-PaymentHandlerNavigationThrottle::MaybeCreateThrottleFor(
-    content::NavigationHandle* handle) {
-  if (!handle || !handle->GetWebContents() ||
-      !handle->GetWebContents()->GetUserData(
-          kPaymentHandlerWebContentsUserDataKey)) {
-    return nullptr;
+void PaymentHandlerNavigationThrottle::MaybeCreateAndAdd(
+    content::NavigationThrottleRegistry& registry) {
+  content::NavigationHandle& handle = registry.GetNavigationHandle();
+  if (!handle.GetWebContents() || !handle.GetWebContents()->GetUserData(
+                                      kPaymentHandlerWebContentsUserDataKey)) {
+    return;
   }
-  return std::make_unique<PaymentHandlerNavigationThrottle>(handle);
+  registry.AddThrottle(
+      std::make_unique<PaymentHandlerNavigationThrottle>(registry));
 }
 
 content::NavigationThrottle::ThrottleCheckResult
 PaymentHandlerNavigationThrottle::WillProcessResponse() {
-  if (!navigation_handle())
+  if (!navigation_handle()) {
     return PROCEED;
+  }
   const net::HttpResponseHeaders* response_headers =
       navigation_handle()->GetResponseHeaders();
-  if (!response_headers)
+  if (!response_headers) {
     return PROCEED;
+  }
 
   std::string mime_type;
   response_headers->GetMimeType(&mime_type);
diff --git a/components/payments/content/payment_handler_navigation_throttle.h b/components/payments/content/payment_handler_navigation_throttle.h
index 062849a..40fc443 100644
--- a/components/payments/content/payment_handler_navigation_throttle.h
+++ b/components/payments/content/payment_handler_navigation_throttle.h
@@ -8,17 +8,13 @@
 #include "content/public/browser/navigation_throttle.h"
 #include "content/public/browser/web_contents.h"
 
-namespace content {
-class NavigationHandle;
-}  // namespace content
-
 namespace payments {
 // The navigation throttle for the payment handler WebContents, used to
 // prevent the WebContents from openning pages of certain categories, e.g. pdf.
 class PaymentHandlerNavigationThrottle : public content::NavigationThrottle {
  public:
   explicit PaymentHandlerNavigationThrottle(
-      content::NavigationHandle* navigation_handle);
+      content::NavigationThrottleRegistry& registry);
   ~PaymentHandlerNavigationThrottle() override;
 
   PaymentHandlerNavigationThrottle(const PaymentHandlerNavigationThrottle&) =
@@ -30,8 +26,7 @@
   // web_contents.
   static void MarkPaymentHandlerWebContents(content::WebContents* web_contents);
 
-  static std::unique_ptr<PaymentHandlerNavigationThrottle>
-  MaybeCreateThrottleFor(content::NavigationHandle* handle);
+  static void MaybeCreateAndAdd(content::NavigationThrottleRegistry& registry);
 
   // content::NavigationThrottle implementation:
   ThrottleCheckResult WillProcessResponse() override;
diff --git a/components/reporting/OWNERS b/components/reporting/OWNERS
index 01c986b..3f77ffb8 100644
--- a/components/reporting/OWNERS
+++ b/components/reporting/OWNERS
@@ -1,5 +1,4 @@
 lbaraz@chromium.org
-jrhilke@google.com
 anasr@google.com
 albertojuarez@google.com
 vshenvi@google.com
diff --git a/components/safe_browsing/content/browser/safe_browsing_navigation_throttle.cc b/components/safe_browsing/content/browser/safe_browsing_navigation_throttle.cc
index cfd36aac..f3683fad 100644
--- a/components/safe_browsing/content/browser/safe_browsing_navigation_throttle.cc
+++ b/components/safe_browsing/content/browser/safe_browsing_navigation_throttle.cc
@@ -12,31 +12,34 @@
 #include "components/security_interstitials/content/security_interstitial_tab_helper.h"
 #include "components/security_interstitials/core/unsafe_resource.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/navigation_throttle_registry.h"
 
 namespace safe_browsing {
 
 // static
-std::unique_ptr<content::NavigationThrottle>
-SafeBrowsingNavigationThrottle::MaybeCreateThrottleFor(
-    content::NavigationHandle* handle,
+void SafeBrowsingNavigationThrottle::MaybeCreateAndAdd(
+    content::NavigationThrottleRegistry& registry,
     SafeBrowsingUIManager* ui_manager) {
-  if (!ui_manager)
-    return nullptr;
+  if (!ui_manager) {
+    return;
+  }
 
   // Only outer-most main frames show the interstitial through the navigation
   // throttle. In other cases, the interstitial is shown via
   // BaseUIManager::DisplayBlockingPage.
-  if (!handle->IsInPrimaryMainFrame() && !handle->IsInPrerenderedMainFrame())
-    return nullptr;
+  content::NavigationHandle& handle = registry.GetNavigationHandle();
+  if (!handle.IsInPrimaryMainFrame() && !handle.IsInPrerenderedMainFrame()) {
+    return;
+  }
 
-  return base::WrapUnique(
-      new SafeBrowsingNavigationThrottle(handle, ui_manager));
+  registry.AddThrottle(base::WrapUnique(
+      new SafeBrowsingNavigationThrottle(registry, ui_manager)));
 }
 
 SafeBrowsingNavigationThrottle::SafeBrowsingNavigationThrottle(
-    content::NavigationHandle* handle,
+    content::NavigationThrottleRegistry& registry,
     SafeBrowsingUIManager* manager)
-    : content::NavigationThrottle(handle), manager_(manager) {}
+    : content::NavigationThrottle(registry), manager_(manager) {}
 
 const char* SafeBrowsingNavigationThrottle::GetNameForLogging() {
   return "SafeBrowsingNavigationThrottle";
diff --git a/components/safe_browsing/content/browser/safe_browsing_navigation_throttle.h b/components/safe_browsing/content/browser/safe_browsing_navigation_throttle.h
index 41ac4c5..82f3b200 100644
--- a/components/safe_browsing/content/browser/safe_browsing_navigation_throttle.h
+++ b/components/safe_browsing/content/browser/safe_browsing_navigation_throttle.h
@@ -8,10 +8,6 @@
 #include "base/memory/raw_ptr.h"
 #include "content/public/browser/navigation_throttle.h"
 
-namespace content {
-class NavigationHandle;
-}  // namespace content
-
 namespace safe_browsing {
 
 class SafeBrowsingUIManager;
@@ -30,8 +26,8 @@
 class SafeBrowsingNavigationThrottle : public content::NavigationThrottle {
  public:
   // |ui_manager| may be null, in which case no throttle is created.
-  static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
-      content::NavigationHandle* handle,
+  static void MaybeCreateAndAdd(
+      content::NavigationThrottleRegistry& registry,
       SafeBrowsingUIManager* ui_manager);
   ~SafeBrowsingNavigationThrottle() override = default;
   const char* GetNameForLogging() override;
@@ -39,7 +35,7 @@
   content::NavigationThrottle::ThrottleCheckResult WillFailRequest() override;
 
  private:
-  SafeBrowsingNavigationThrottle(content::NavigationHandle* handle,
+  SafeBrowsingNavigationThrottle(content::NavigationThrottleRegistry& registry,
                                  SafeBrowsingUIManager* ui_manager);
 
   raw_ptr<SafeBrowsingUIManager> manager_;
diff --git a/components/security_interstitials/content/insecure_form_navigation_throttle.cc b/components/security_interstitials/content/insecure_form_navigation_throttle.cc
index 8cd59d07..6e03801 100644
--- a/components/security_interstitials/content/insecure_form_navigation_throttle.cc
+++ b/components/security_interstitials/content/insecure_form_navigation_throttle.cc
@@ -37,9 +37,9 @@
 namespace security_interstitials {
 
 InsecureFormNavigationThrottle::InsecureFormNavigationThrottle(
-    content::NavigationHandle* navigation_handle,
+    content::NavigationThrottleRegistry& registry,
     std::unique_ptr<SecurityBlockingPageFactory> blocking_page_factory)
-    : content::NavigationThrottle(navigation_handle),
+    : content::NavigationThrottle(registry),
       blocking_page_factory_(std::move(blocking_page_factory)) {}
 
 InsecureFormNavigationThrottle::~InsecureFormNavigationThrottle() = default;
@@ -72,15 +72,15 @@
 }
 
 // static
-std::unique_ptr<InsecureFormNavigationThrottle>
-InsecureFormNavigationThrottle::MaybeCreateNavigationThrottle(
-    content::NavigationHandle* navigation_handle,
+void InsecureFormNavigationThrottle::MaybeCreateAndAdd(
+    content::NavigationThrottleRegistry& registry,
     std::unique_ptr<SecurityBlockingPageFactory> blocking_page_factory,
     PrefService* prefs) {
-  if (prefs && !prefs->GetBoolean(prefs::kMixedFormsWarningsEnabled))
-    return nullptr;
-  return std::make_unique<InsecureFormNavigationThrottle>(
-      navigation_handle, std::move(blocking_page_factory));
+  if (prefs && !prefs->GetBoolean(prefs::kMixedFormsWarningsEnabled)) {
+    return;
+  }
+  registry.AddThrottle(std::make_unique<InsecureFormNavigationThrottle>(
+      registry, std::move(blocking_page_factory)));
 }
 
 content::NavigationThrottle::ThrottleCheckResult
@@ -107,18 +107,21 @@
   // There's an exception to this: Reloading a GET form will proceed since a
   // prerender shouldn't check the InsecureFormTabStorage, which is a per-tab
   // object. This is done in the check above.
-  if (handle->IsInPrerenderedMainFrame())
+  if (handle->IsInPrerenderedMainFrame()) {
     return content::NavigationThrottle::CANCEL;
+  }
 
   // If user has just chosen to proceed on an interstitial, we don't show
   // another one.
-  if (tab_storage && tab_storage->IsProceeding())
+  if (tab_storage && tab_storage->IsProceeding()) {
     return content::NavigationThrottle::PROCEED;
+  }
 
   // Do not set special error page HTML for insecure forms in subframes; those
   // are already hard blocked.
-  if (!handle->IsInOutermostMainFrame())
+  if (!handle->IsInOutermostMainFrame()) {
     return content::NavigationThrottle::PROCEED;
+  }
 
   url::Origin form_originating_origin =
       handle->GetInitiatorOrigin().value_or(url::Origin());
@@ -160,8 +163,9 @@
   std::string interstitial_html = blocking_page->GetHTMLContents();
   SecurityInterstitialTabHelper::AssociateBlockingPage(
       handle, std::move(blocking_page));
-  if (!tab_storage)
+  if (!tab_storage) {
     tab_storage = InsecureFormTabStorage::GetOrCreate(contents);
+  }
   tab_storage->SetInterstitialShown(true);
   return content::NavigationThrottle::ThrottleCheckResult(
       CANCEL, net::ERR_BLOCKED_BY_CLIENT, std::move(interstitial_html));
diff --git a/components/security_interstitials/content/insecure_form_navigation_throttle.h b/components/security_interstitials/content/insecure_form_navigation_throttle.h
index c79d64b3..3abca5f 100644
--- a/components/security_interstitials/content/insecure_form_navigation_throttle.h
+++ b/components/security_interstitials/content/insecure_form_navigation_throttle.h
@@ -8,10 +8,6 @@
 #include "components/security_interstitials/content/security_blocking_page_factory.h"
 #include "content/public/browser/navigation_throttle.h"
 
-namespace content {
-class NavigationHandle;
-}  // namespace content
-
 class PrefService;
 
 namespace security_interstitials {
@@ -29,7 +25,7 @@
   };
 
   InsecureFormNavigationThrottle(
-      content::NavigationHandle* navigation_handle,
+      content::NavigationThrottleRegistry& registry,
       std::unique_ptr<SecurityBlockingPageFactory> blocking_page_factory);
   ~InsecureFormNavigationThrottle() override;
 
@@ -39,9 +35,8 @@
   ThrottleCheckResult WillProcessResponse() override;
   const char* GetNameForLogging() override;
 
-  static std::unique_ptr<InsecureFormNavigationThrottle>
-  MaybeCreateNavigationThrottle(
-      content::NavigationHandle* navigation_handle,
+  static void MaybeCreateAndAdd(
+      content::NavigationThrottleRegistry& registry,
       std::unique_ptr<SecurityBlockingPageFactory> blocking_page_factory,
       PrefService* prefs);
 
diff --git a/components/viz/service/layers/layer_context_impl.cc b/components/viz/service/layers/layer_context_impl.cc
index 53485a9..7244133d 100644
--- a/components/viz/service/layers/layer_context_impl.cc
+++ b/components/viz/service/layers/layer_context_impl.cc
@@ -436,7 +436,6 @@
 
 void UpdateTextureLayerExtra(const mojom::TextureLayerExtraPtr& extra,
                              cc::TextureLayerImpl& layer) {
-  layer.SetPremultipliedAlpha(extra->premultiplied_alpha);
   layer.SetBlendBackgroundColor(extra->blend_background_color);
   layer.SetForceTextureToOpaque(extra->force_texture_to_opaque);
   layer.SetUVTopLeft(extra->uv_top_left);
diff --git a/content/browser/back_forward_cache_limit_browsertest.cc b/content/browser/back_forward_cache_limit_browsertest.cc
index bb5a75c5..d193a20 100644
--- a/content/browser/back_forward_cache_limit_browsertest.cc
+++ b/content/browser/back_forward_cache_limit_browsertest.cc
@@ -194,7 +194,8 @@
 }  // namespace
 
 class BackForwardCacheLimitForPrioritizedPagesBrowserTest
-    : public BackgroundForegroundProcessLimitBackForwardCacheBrowserTest {
+    : public BackgroundForegroundProcessLimitBackForwardCacheBrowserTest,
+      public testing::WithParamInterface<std::string> {
  protected:
   // Mock subclass of ContentBrowserClient that will determine if the url is
   // prioritized by checking against `kPrioritizedPageURL`.
@@ -216,25 +217,59 @@
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
+    EnableFeatureAndSetParams(kBackForwardCachePrioritizedEntry, "level",
+                              GetParam());
     BackgroundForegroundProcessLimitBackForwardCacheBrowserTest::
         SetUpCommandLine(command_line);
-    feature_list_.InitAndEnableFeature(
-        content::kBackForwardCachePrioritizedEntry);
+  }
+
+  bool ShouldPrioritizeWhenClearAllUnlessNoEviction() {
+    return GetParam() == "prioritize-unless-should-clear-all-and-no-eviction";
   }
 
  private:
   std::unique_ptr<MockContentBrowserClientWithPrioritizedBackForwardCacheEntry>
       test_client_;
-  base::test::ScopedFeatureList feature_list_;
 };
 
-// Test that both prioritized entries and non-prioritized entries would be
-// evicted when pruning with size 0.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
-                       PruneToZero) {
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    BackForwardCacheLimitForPrioritizedPagesBrowserTest,
+    testing::Values("prioritize-unless-should-clear-all",
+                    "prioritize-unless-should-clear-all-and-no-eviction"));
+
+// Test that both when pruning with size 0, if no other eviction happens, the
+// prioritized entry would be evicted.
+IN_PROC_BROWSER_TEST_P(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
+                       PruneToZero_NoOtherEvictionHappens) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
-  // We need at least 4 entries in the BFCache list for this test.
+  // We need at least 1 entries in the BFCache list for this test.
+  CHECK_GE(kBackForwardCacheSize, 1u);
+
+  ASSERT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
+                                         kPrioritizedPageURL, "/title1.html")));
+  ASSERT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("b.test", "/title1.html")));
+
+  // Now the BFCache entry list is: [pp, b].
+  // Prune the BFCache entries to 0.
+  web_contents()->GetController().GetBackForwardCache().Prune(0, kPruneReason);
+  // All the entries should be evicted
+  ASSERT_TRUE(HistoryGoBack(web_contents()));
+  ExpectNotRestored({kPruneReason}, {}, {}, {}, {}, FROM_HERE);
+}
+
+// Test that both when pruning with size 0, if there is another entry evicted,
+// the prioritized entry would be:
+// - evicted if the level is prioritize-unless-should-clear-all
+// - not evicted if the level is
+// prioritize-unless-should-clear-all-and-no-eviction.
+IN_PROC_BROWSER_TEST_P(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
+                       PruneToZero_OtherEvictionHappens) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  // We need at least 2 entries in the BFCache list for this test.
   CHECK_GE(kBackForwardCacheSize, 2u);
 
   ASSERT_TRUE(NavigateToURL(
@@ -247,16 +282,24 @@
   // Now the BFCache entry list is: [a, pp, b].
   // Prune the BFCache entries to 0.
   web_contents()->GetController().GetBackForwardCache().Prune(0, kPruneReason);
-  // All the entries should be evicted
-  for (size_t i = 0; i < 2; ++i) {
-    ASSERT_TRUE(HistoryGoBack(web_contents()));
+  ASSERT_TRUE(HistoryGoBack(web_contents()));
+  if (ShouldPrioritizeWhenClearAllUnlessNoEviction()) {
+    // If the level is prioritize-unless-should-clear-all-and-no-eviction, the
+    // prioritized entry should be restored since the some other eviction
+    // happens.
+    ExpectRestored(FROM_HERE);
+  } else {
+    // Otherwise the prioritized entry should be evicted as well.
     ExpectNotRestored({kPruneReason}, {}, {}, {}, {}, FROM_HERE);
   }
+  // The non-prioritized entry should always be evicted.
+  ASSERT_TRUE(HistoryGoBack(web_contents()));
+  ExpectNotRestored({kPruneReason}, {}, {}, {}, {}, FROM_HERE);
 }
 
 // Test that when pruning with a positive number size, the last prioritized
 // entry outside the limit will not be evicted.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
+IN_PROC_BROWSER_TEST_P(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
                        PruneToNonZero_PrioritizedEntryOutsideLimit) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -296,7 +339,7 @@
 
 // Test that when pruning with a positive number size, the last prioritized
 // entry inside the limit should be counted as the regular cache.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
+IN_PROC_BROWSER_TEST_P(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
                        PruneToNonZero_PrioritizedEntryInsideLimit) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -323,7 +366,7 @@
 // Test that when pruning with a positive number size while there is already an
 // old prioritized entry kept in cache before, it will be replaced by the newer
 // prioritized entry.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
+IN_PROC_BROWSER_TEST_P(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
                        PruneToNonZeroTwice) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -382,7 +425,7 @@
 
 // Test that the prioritized BFCache entry will not be evicted even when another
 // entry is stored and exceeds the limit.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
+IN_PROC_BROWSER_TEST_P(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
                        CacheLimitReached) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
diff --git a/content/browser/broadcast_channel/OWNERS b/content/browser/broadcast_channel/OWNERS
index 864f112..ed27d8089 100644
--- a/content/browser/broadcast_channel/OWNERS
+++ b/content/browser/broadcast_channel/OWNERS
@@ -1,2 +1,2 @@
 mek@chromium.org
-wanderview@chromium.org
+wanderview@meta.com
diff --git a/content/browser/cache_storage/OWNERS b/content/browser/cache_storage/OWNERS
index 485d375..ed17959 100644
--- a/content/browser/cache_storage/OWNERS
+++ b/content/browser/cache_storage/OWNERS
@@ -5,7 +5,7 @@
 leimy@chromium.org
 mych@chromium.org
 rakina@chromium.org
-wanderview@chromium.org
+wanderview@meta.com
 
 # Emeritus
 ayui@chromium.org  #{LAST_RESORT_SUGGESTION}
diff --git a/content/browser/locks/OWNERS b/content/browser/locks/OWNERS
index a961b4f9..a688641 100644
--- a/content/browser/locks/OWNERS
+++ b/content/browser/locks/OWNERS
@@ -5,4 +5,4 @@
 rakina@chromium.org
 
 # Emeritus
-wanderview@chromium.org
+wanderview@meta.com
diff --git a/content/browser/preloading/prerender/prerender_browsertest.cc b/content/browser/preloading/prerender/prerender_browsertest.cc
index 0f8d4a2..10e9b3c 100644
--- a/content/browser/preloading/prerender/prerender_browsertest.cc
+++ b/content/browser/preloading/prerender/prerender_browsertest.cc
@@ -2839,6 +2839,47 @@
   EXPECT_EQ(web_contents()->GetLastCommittedURL(), initial_url);
 }
 
+// Tests that using window.open the prerendered url with a customized window
+// name cannot activate a prerender whose target_hint is "_blank".
+IN_PROC_BROWSER_TEST_F(
+    PrerenderTargetHintEnabledBrowserTest,
+    DoesNotActivateOnWindowOpen_WithCustomizedWindowName_WithTargetHintBlank) {
+  const GURL initial_url = GetUrl("/simple_links.html");
+  const GURL prerendering_url = GetUrl("/title2.html");
+
+  ASSERT_TRUE(NavigateToURL(shell(), initial_url));
+
+  // Start prerendering `prerendering_url`.
+  FrameTreeNodeId host_id = prerender_helper()->AddPrerender(
+      prerendering_url, /*eagerness=*/std::nullopt, "_blank");
+  auto* prerender_web_contents = WebContents::FromFrameTreeNodeId(host_id);
+  ASSERT_NE(prerender_web_contents, web_contents_impl());
+  ExpectWebContentsIsForNewTabPrerendering(*prerender_web_contents);
+
+  // Use window.open a customized window name should not activate
+  // `prerendering_url`.
+  TestNavigationObserver nav_observer(prerendering_url);
+  nav_observer.StartWatchingNewWebContents();
+  test::PrerenderHostObserver prerender_observer(*prerender_web_contents,
+                                                 host_id);
+
+  std::string window_open_script =
+      R"(window.open($1, 'WindowName', 'noopener');)";
+  EXPECT_TRUE(ExecJs(web_contents(),
+                     JsReplace(window_open_script, prerendering_url.spec())));
+
+  nav_observer.WaitForNavigationFinished();
+  EXPECT_FALSE(prerender_observer.was_activated());
+
+  // Navigating prerendered url with a customized window name should not cancel
+  // the prerender.
+  EXPECT_TRUE(prerender_helper()->HasNewTabHandle(host_id));
+
+  // The navigation occurred in a new WebContents, so the original WebContents
+  // should still be showing the initial trigger page.
+  EXPECT_EQ(web_contents()->GetLastCommittedURL(), initial_url);
+}
+
 // Tests that clicking a link annotated with "target=_blank" can activate a
 // prerender whose target_hint is "_blank" where the initiator page is in the
 // background when the speculation rules were added.
diff --git a/content/browser/renderer_host/back_forward_cache_impl.cc b/content/browser/renderer_host/back_forward_cache_impl.cc
index f5ef69b0..a642528 100644
--- a/content/browser/renderer_host/back_forward_cache_impl.cc
+++ b/content/browser/renderer_host/back_forward_cache_impl.cc
@@ -427,6 +427,53 @@
   return cache_control_no_store_ttl.Get();
 }
 
+// Describes the behavior of BackForwardCacheImpl when handling the prioritized
+// entry. The prioritized entry is the BFCache entry that will be kept alive
+// even if it is out of the cache limit.
+enum class BackForwardCachePrioritizedEntryExperimentLevel {
+  // No special prioritization logic.
+  kDoNotPrioritize = 0,
+  // Allow one extra prioritized entry to avoid eviction.
+  // If the cache limit enforced is 0 (e.g. due to critical memory pressure),
+  // the prioritized entry will be evicted as well.
+  kPrioritizeUnlessShouldClearAll = 1,
+  // Allow one extra prioritized entry to avoid eviction.
+  // If the cache limit enforced is 0 (e.g. due to critical memory pressure),
+  // and there is no other entry to evict, the prioritized entry will be evicted
+  // as well.
+  kPrioritizeUnlessShouldClearAllAndNoEviction = 2,
+};
+
+const char kBackForwardCachePrioritizedEntryExperimentLevelName[] = "level";
+
+static constexpr base::FeatureParam<
+    BackForwardCachePrioritizedEntryExperimentLevel>::Option
+    prioritized_entry_levels[] = {
+        {BackForwardCachePrioritizedEntryExperimentLevel::kDoNotPrioritize,
+         "do-not-prioritize"},
+        {BackForwardCachePrioritizedEntryExperimentLevel::
+             kPrioritizeUnlessShouldClearAll,
+         "prioritize-unless-should-clear-all"},
+        {BackForwardCachePrioritizedEntryExperimentLevel::
+             kPrioritizeUnlessShouldClearAllAndNoEviction,
+         "prioritize-unless-should-clear-all-and-no-eviction"},
+};
+const base::FeatureParam<BackForwardCachePrioritizedEntryExperimentLevel>
+    prioritized_entry_level{
+        &kBackForwardCachePrioritizedEntry,
+        kBackForwardCachePrioritizedEntryExperimentLevelName,
+        BackForwardCachePrioritizedEntryExperimentLevel::kDoNotPrioritize,
+        &prioritized_entry_levels};
+
+BackForwardCachePrioritizedEntryExperimentLevel
+GetBackForwardCachePrioritizedEntryExperimentLevel() {
+  if (!IsBackForwardCacheEnabled() ||
+      !base::FeatureList::IsEnabled(kBackForwardCachePrioritizedEntry)) {
+    return BackForwardCachePrioritizedEntryExperimentLevel::kDoNotPrioritize;
+  }
+  return prioritized_entry_level.Get();
+}
+
 bool IsSameOriginForTreeResult(RenderFrameHostImpl* rfh,
                                const url::Origin& main_document_origin) {
   // Treat any frame inside a fenced frame as cross origin so we don't leak
@@ -1257,7 +1304,21 @@
     size_t limit,
     BackForwardCacheMetrics::NotRestoredReason reason) {
   using NotRestoredReason = BackForwardCacheMetrics::NotRestoredReason;
+  using Level = BackForwardCachePrioritizedEntryExperimentLevel;
+
   size_t count = 0;
+  bool did_evict_any_entry = false;
+  auto prioritized_level = GetBackForwardCachePrioritizedEntryExperimentLevel();
+  // Indicates whether we should skip the initial rounds of eviction (the
+  // for-loop below). Note that even if this boolean value is true, the
+  // prioritized entry may still be evicted if the level is
+  // `kPrioritizeUnlessShouldClearAllAndNoEviction` and there is no other
+  // eviction.
+  bool should_skip_eviction_for_prioritized_entry =
+      (prioritized_level == Level::kPrioritizeUnlessShouldClearAll &&
+       limit > 0) ||
+      prioritized_level == Level::kPrioritizeUnlessShouldClearAllAndNoEviction;
+
   for (auto stored_entry_iter = entries_.begin();
        stored_entry_iter != entries_.end(); stored_entry_iter++) {
     Entry* stored_entry = stored_entry_iter->get();
@@ -1285,16 +1346,15 @@
         !AllRenderViewHostsReceivedAckFromRenderer(*stored_entry)) {
       continue;
     }
+
     if (++count > limit) {
-      if (base::FeatureList::IsEnabled(kBackForwardCachePrioritizedEntry)) {
-        // If it's not a pruning process that will clear all the entries, we
-        // should handle the latest prioritized entry outside the limit
-        // specially and keep it not-evicted.
-        if (limit > 0 &&
-            GetContentClient()->browser()->ShouldPrioritizeForBackForwardCache(
+      if (should_skip_eviction_for_prioritized_entry) {
+        // Handle the latest prioritized entry outside the limit specially and
+        // keep it not-evicted.
+        if (GetContentClient()->browser()->ShouldPrioritizeForBackForwardCache(
                 rfh->GetBrowserContext(), rfh->GetLastCommittedURL())) {
-          // If the prioritized_entry_ is already taken by some other entry, we
-          // have to evict the current one.
+          // If the prioritized_entry_ is already taken by some other entry,
+          // we have to evict the current one.
           if (prioritized_entry_ == entries_.end() ||
               prioritized_entry_ == stored_entry_iter) {
             prioritized_entry_ = stored_entry_iter;
@@ -1302,9 +1362,27 @@
           }
         }
       }
+      did_evict_any_entry = true;
       rfh->EvictFromBackForwardCacheWithReason(reason);
     }
   }
+
+  // The `prioritized_entry_` should be evicted if
+  if (
+      // the prioritized entry is set
+      prioritized_entry_ != entries_.end() &&
+      // the cache limit is 0 (e.g. due to critical memory pressure)
+      limit == 0 &&
+      // the level is `prioritize-unless-should-clear-all-and-no-eviction`
+      prioritized_level ==
+          Level::kPrioritizeUnlessShouldClearAllAndNoEviction &&
+      // no other entry was evicted
+      !did_evict_any_entry) {
+    prioritized_entry_->get()
+        ->render_frame_host()
+        ->EvictFromBackForwardCacheWithReason(reason);
+    prioritized_entry_ = entries_.end();
+  }
   return count;
 }
 
diff --git a/content/browser/renderer_host/debug_urls.cc b/content/browser/renderer_host/debug_urls.cc
index 93d8668..5dda8c3 100644
--- a/content/browser/renderer_host/debug_urls.cc
+++ b/content/browser/renderer_host/debug_urls.cc
@@ -52,6 +52,7 @@
 const char kAsanCorruptHeap[] = "/browser-corrupt-heap";
 #endif
 
+// The conditions here must stay in sync with |HandleAsanDebugURL|.
 bool IsAsanDebugURL(const GURL& url) {
   if (!(url.is_valid() && url.SchemeIs(kChromeUIScheme) &&
         url.DomainIs(kAsanCrashDomain) && url.has_path())) {
@@ -74,30 +75,35 @@
   return false;
 }
 
-bool HandleAsanDebugURL(const GURL& url) {
+// The URLs handled here must exactly match those that return true from
+// |IsAsanDebugURL|.
+void HandleAsanDebugURL(const GURL& url) {
+  CHECK(IsAsanDebugURL(url));
 #if defined(ADDRESS_SANITIZER) || BUILDFLAG(IS_HWASAN)
 #if BUILDFLAG(IS_WIN)
   if (url.path_piece() == kAsanCorruptHeapBlock) {
     base::debug::AsanCorruptHeapBlock();
-    return true;
-  } else if (url.path_piece() == kAsanCorruptHeap) {
+    return;
+  }
+  if (url.path_piece() == kAsanCorruptHeap) {
     base::debug::AsanCorruptHeap();
-    return true;
+    return;
   }
 #endif  // BUILDFLAG(IS_WIN)
 
   if (url.path_piece() == kAsanHeapOverflow) {
     base::debug::AsanHeapOverflow();
-  } else if (url.path_piece() == kAsanHeapUnderflow) {
+    return;
+  }
+  if (url.path_piece() == kAsanHeapUnderflow) {
     base::debug::AsanHeapUnderflow();
-  } else if (url.path_piece() == kAsanUseAfterFree) {
+    return;
+  }
+  if (url.path_piece() == kAsanUseAfterFree) {
     base::debug::AsanHeapUseAfterFree();
-  } else {
-    return false;
+    return;
   }
 #endif
-
-  return true;
 }
 
 NOINLINE void HangCurrentThread() {
@@ -117,101 +123,124 @@
 
 }  // namespace
 
-bool HandleDebugURL(const GURL& url,
+// The conditions here must stay in sync with |HandleDebugURL|.
+bool IsDebugURL(const GURL& url) {
+  if (!(url.is_valid() && url.SchemeIs(kChromeUIScheme))) {
+    return false;
+  }
+
+  if (IsAsanDebugURL(url)) {
+    return true;
+  }
+
+  if (url == blink::kChromeUIBrowserCrashURL ||
+      url == blink::kChromeUIBrowserDcheckURL ||
+#if BUILDFLAG(IS_WIN)
+      url == blink::kChromeUIBrowserHeapCorruptionURL ||
+#endif
+      url == blink::kChromeUIBrowserUIHang ||
+      url == blink::kChromeUIDelayedBrowserUIHang ||
+      url == blink::kChromeUIGpuCleanURL ||
+      url == blink::kChromeUIGpuCrashURL ||
+#if BUILDFLAG(IS_ANDROID)
+      url == blink::kChromeUIGpuJavaCrashURL ||
+#endif
+      url == blink::kChromeUIGpuHangURL ||
+      url == blink::kChromeUIMemoryPressureCriticalURL ||
+      url == blink::kChromeUIMemoryPressureModerateURL) {
+    return true;
+  }
+
+  return false;
+}
+
+// The URLs handled here must exactly match those that return true from
+// |IsDebugURL|.
+void HandleDebugURL(const GURL& url,
                     ui::PageTransition transition,
                     bool is_explicit_navigation) {
+  CHECK(IsDebugURL(url));
   // We want to handle the debug URL if the user explicitly navigated to this
   // URL, unless kEnableGpuBenchmarking is enabled by Telemetry.
   bool is_telemetry_navigation =
       base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableGpuBenchmarking) &&
       (PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED));
-
-  if (!is_explicit_navigation && !is_telemetry_navigation)
-    return false;
-
-  if (IsAsanDebugURL(url))
-    return HandleAsanDebugURL(url);
-
-  if (url == blink::kChromeUIBrowserCrashURL) {
-    CrashBrowserProcessIntentionally();
-    return true;
+  if (!is_explicit_navigation && !is_telemetry_navigation) {
+    return;
   }
 
+  if (IsAsanDebugURL(url)) {
+    HandleAsanDebugURL(url);
+    return;
+  }
+  if (url == blink::kChromeUIBrowserCrashURL) {
+    CrashBrowserProcessIntentionally();
+    return;
+  }
   if (url == blink::kChromeUIBrowserDcheckURL) {
     // Induce an intentional DCHECK in the browser process. This is used to
     // see if a DCHECK will bring down the current process (is FATAL).
     DCHECK(false);
-    return true;
+    return;
   }
-
 #if BUILDFLAG(IS_WIN)
   if (url == blink::kChromeUIBrowserHeapCorruptionURL) {
     // Induce an intentional heap corruption in the browser process.
     base::debug::win::TerminateWithHeapCorruption();
   }
 #endif
-
   if (url == blink::kChromeUIBrowserUIHang) {
     HangCurrentThread();
-    return true;
+    return;
   }
-
   if (url == blink::kChromeUIDelayedBrowserUIHang) {
     // Webdriver-safe url to hang the ui thread. Webdriver waits for the onload
     // event in javascript which needs a little more time to fire.
     GetUIThreadTaskRunner({})->PostDelayedTask(
         FROM_HERE, base::BindOnce(&HangCurrentThread), base::Seconds(2));
-    return true;
+    return;
   }
-
   if (url == blink::kChromeUIGpuCleanURL) {
     auto* host = GpuProcessHost::Get();
     if (host) {
       host->gpu_service()->DestroyAllChannels();
     }
-    return true;
+    return;
   }
-
   if (url == blink::kChromeUIGpuCrashURL) {
     auto* host = GpuProcessHost::Get();
     if (host) {
       host->gpu_service()->Crash();
     }
-    return true;
+    return;
   }
-
 #if BUILDFLAG(IS_ANDROID)
   if (url == blink::kChromeUIGpuJavaCrashURL) {
     auto* host = GpuProcessHost::Get();
     if (host) {
       host->gpu_service()->ThrowJavaException();
     }
-    return true;
+    return;
   }
 #endif
-
   if (url == blink::kChromeUIGpuHangURL) {
     auto* host = GpuProcessHost::Get();
     if (host) {
       host->gpu_service()->Hang();
     }
-    return true;
+    return;
   }
-
   if (url == blink::kChromeUIMemoryPressureCriticalURL) {
     base::MemoryPressureListener::NotifyMemoryPressure(
         base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
-    return true;
+    return;
   }
-
   if (url == blink::kChromeUIMemoryPressureModerateURL) {
     base::MemoryPressureListener::NotifyMemoryPressure(
         base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
-    return true;
+    return;
   }
-
-  return false;
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/debug_urls.h b/content/browser/renderer_host/debug_urls.h
index 4b65148..9cdba99 100644
--- a/content/browser/renderer_host/debug_urls.h
+++ b/content/browser/renderer_host/debug_urls.h
@@ -5,17 +5,22 @@
 #ifndef CONTENT_BROWSER_RENDERER_HOST_DEBUG_URLS_H_
 #define CONTENT_BROWSER_RENDERER_HOST_DEBUG_URLS_H_
 
+#include "content/common/content_export.h"
 #include "ui/base/page_transition_types.h"
 
 class GURL;
 
 namespace content {
 
-// Checks if the given url is a url used for debugging purposes, and if so
-// handles it and returns true.
-bool HandleDebugURL(const GURL& url,
-                    ui::PageTransition transition,
-                    bool is_explicit_navigation);
+// Returns true if |url| is a special debugging URL for triggering
+// intentional browser behaviors like crashes or hangs.
+CONTENT_EXPORT bool IsDebugURL(const GURL& url);
+
+// Triggers debug action for |url| if |IsDebugURL| returns true for it;
+// otherwise, this function will crash.
+CONTENT_EXPORT void HandleDebugURL(const GURL& url,
+                                   ui::PageTransition transition,
+                                   bool is_explicit_navigation);
 
 }  // namespace content
 
diff --git a/content/browser/renderer_host/debug_urls_unittest.cc b/content/browser/renderer_host/debug_urls_unittest.cc
new file mode 100644
index 0000000..d66fb2f
--- /dev/null
+++ b/content/browser/renderer_host/debug_urls_unittest.cc
@@ -0,0 +1,51 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/debug_urls.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/chrome_debug_urls.h"
+#include "url/gurl.h"
+
+namespace content {
+
+using DebugUrlsUnitTest = testing::Test;
+
+TEST_F(DebugUrlsUnitTest, IsDebugURL_NonDebugUrlsReturnFalse) {
+  EXPECT_FALSE(IsDebugURL(GURL("invalid")));
+  EXPECT_FALSE(IsDebugURL(GURL("https://example.com")));
+  EXPECT_FALSE(IsDebugURL(GURL("http://example.com")));
+  EXPECT_FALSE(IsDebugURL(GURL("chrome://version")));
+}
+
+TEST_F(DebugUrlsUnitTest, IsDebugURL_AsanUrlsReturnTrue) {
+  EXPECT_TRUE(IsDebugURL(GURL("chrome://crash/browser-heap-overflow")));
+  EXPECT_TRUE(IsDebugURL(GURL("chrome://crash/browser-heap-overflow")));
+  EXPECT_TRUE(IsDebugURL(GURL("chrome://crash/browser-use-after-free")));
+
+#if BUILDFLAG(IS_WIN)
+  EXPECT_TRUE(IsDebugURL(GURL("chrome://crash/browser-corrupt-heap-block")));
+  EXPECT_TRUE(IsDebugURL(GURL("chrome://crash/browser-corrupt-heap")));
+#endif
+}
+
+TEST_F(DebugUrlsUnitTest, IsDebugURL_DebugUrlsReturnTrue) {
+  EXPECT_TRUE(IsDebugURL(GURL(blink::kChromeUIBrowserCrashURL)));
+  EXPECT_TRUE(IsDebugURL(GURL(blink::kChromeUIBrowserDcheckURL)));
+#if BUILDFLAG(IS_WIN)
+  EXPECT_TRUE(IsDebugURL(GURL(blink::kChromeUIBrowserHeapCorruptionURL)));
+#endif
+  EXPECT_TRUE(IsDebugURL(GURL(blink::kChromeUIBrowserUIHang)));
+  EXPECT_TRUE(IsDebugURL(GURL(blink::kChromeUIDelayedBrowserUIHang)));
+  EXPECT_TRUE(IsDebugURL(GURL(blink::kChromeUIGpuCleanURL)));
+  EXPECT_TRUE(IsDebugURL(GURL(blink::kChromeUIGpuCrashURL)));
+#if BUILDFLAG(IS_ANDROID)
+  EXPECT_TRUE(IsDebugURL(GURL(blink::kChromeUIGpuJavaCrashURL)));
+#endif
+  EXPECT_TRUE(IsDebugURL(GURL(blink::kChromeUIGpuHangURL)));
+  EXPECT_TRUE(IsDebugURL(GURL(blink::kChromeUIMemoryPressureCriticalURL)));
+  EXPECT_TRUE(IsDebugURL(GURL(blink::kChromeUIMemoryPressureModerateURL)));
+}
+
+}  // namespace content
diff --git a/content/browser/renderer_host/navigation_controller_impl.cc b/content/browser/renderer_host/navigation_controller_impl.cc
index 06dfcf6..8418b1d8 100644
--- a/content/browser/renderer_host/navigation_controller_impl.cc
+++ b/content/browser/renderer_host/navigation_controller_impl.cc
@@ -1365,11 +1365,20 @@
   TRACE_EVENT1("browser,navigation",
                "NavigationControllerImpl::LoadURLWithParams", "url",
                params.url.possibly_invalid_spec());
-  bool is_explicit_navigation =
-      GetContentClient()->browser()->IsExplicitNavigation(
-          params.transition_type);
-  if (HandleDebugURL(params.url, params.transition_type,
-                     is_explicit_navigation)) {
+
+  if (IsDebugURL(params.url)) {
+    // Browser-debug URLs won't go through NavigationThrottles so we have to
+    // check them explicitly. See crbug.com/40605746.
+    ContentBrowserClient* client = GetContentClient()->browser();
+    if (client->ShouldBlockRendererDebugURL(
+            params.url, browser_context_,
+            GetTargetFrameTreeNodeForNavigation(params)
+                ->current_frame_host())) {
+      DiscardPendingEntry(false);
+      return nullptr;
+    }
+    HandleDebugURL(params.url, params.transition_type,
+                   client->IsExplicitNavigation(params.transition_type));
     // If Telemetry is running, allow the URL load to proceed as if it's
     // unhandled, otherwise Telemetry can't tell if Navigation completed.
     if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -3701,24 +3710,7 @@
 
 base::WeakPtr<NavigationHandle> NavigationControllerImpl::NavigateWithoutEntry(
     const LoadURLParams& params) {
-  // Find the appropriate FrameTreeNode.
-  FrameTreeNode* node = nullptr;
-  if (params.frame_tree_node_id || !params.frame_name.empty()) {
-    node = params.frame_tree_node_id
-               ? frame_tree_->FindByID(params.frame_tree_node_id)
-               : frame_tree_->FindByName(params.frame_name);
-    DCHECK(!node || &node->frame_tree() == &frame_tree());
-    if (!node && params.frame_tree_node_id) {
-      // If the specified FrameTreeNode exists in another FrameTree, the caller
-      // is using the wrong NavigationController.
-      CHECK(!FrameTreeNode::GloballyFindByID(params.frame_tree_node_id),
-            base::NotFatalUntil::M140);
-    }
-  }
-
-  // If no FrameTreeNode was specified, navigate the main frame.
-  if (!node)
-    node = frame_tree_->root();
+  FrameTreeNode* node = GetTargetFrameTreeNodeForNavigation(params);
 
   // Compute overrides to the LoadURLParams for |override_user_agent|,
   // |should_replace_current_entry| and |has_user_gesture| that will be used
@@ -3755,8 +3747,8 @@
   // URLs, unlike the cases below where we clear it if the navigation doesn't
   // proceed.
   if (blink::IsRendererDebugURL(params.url)) {
-    // Renderer-debug URLs won't go through NavigationThrottlers so we have to
-    // check them explicitly. See bug 913334.
+    // Renderer-debug URLs won't go through NavigationThrottles so we have to
+    // check them explicitly. See crbug.com/40605746.
     if (GetContentClient()->browser()->ShouldBlockRendererDebugURL(
             params.url, browser_context_, node->current_frame_host())) {
       DiscardPendingEntry(false);
@@ -3874,6 +3866,29 @@
   return created_navigation_handle;
 }
 
+FrameTreeNode* NavigationControllerImpl::GetTargetFrameTreeNodeForNavigation(
+    const LoadURLParams& params) {
+  FrameTreeNode* node = nullptr;
+  if (params.frame_tree_node_id || !params.frame_name.empty()) {
+    node = params.frame_tree_node_id
+               ? frame_tree_->FindByID(params.frame_tree_node_id)
+               : frame_tree_->FindByName(params.frame_name);
+    DCHECK(!node || &node->frame_tree() == &frame_tree());
+    if (!node && params.frame_tree_node_id) {
+      // If the specified FrameTreeNode exists in another FrameTree, the caller
+      // is using the wrong NavigationController.
+      CHECK(!FrameTreeNode::GloballyFindByID(params.frame_tree_node_id),
+            base::NotFatalUntil::M140);
+    }
+  }
+
+  // If no FrameTreeNode was specified, navigate the main frame.
+  if (!node) {
+    node = frame_tree_->root();
+  }
+  return node;
+}
+
 void NavigationControllerImpl::HandleRendererDebugURL(
     FrameTreeNode* frame_tree_node,
     const GURL& url) {
diff --git a/content/browser/renderer_host/navigation_controller_impl.h b/content/browser/renderer_host/navigation_controller_impl.h
index 2384e4ee..38de957 100644
--- a/content/browser/renderer_host/navigation_controller_impl.h
+++ b/content/browser/renderer_host/navigation_controller_impl.h
@@ -953,6 +953,11 @@
       const std::string& error_page_html,
       bool is_post_commit_error_page);
 
+  // Finds the target FrameTreeNode for navigation. Returns the node specified
+  // by |params| via ID or name, or the root node if none specified.
+  FrameTreeNode* GetTargetFrameTreeNodeForNavigation(
+      const LoadURLParams& params);
+
   // ---------------------------------------------------------------------------
 
   // The FrameTree this instance belongs to. Each FrameTree gets its own
diff --git a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
index 4b621b15..70185f2 100644
--- a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
@@ -24,6 +24,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/test/bind.h"
+#include "base/test/gtest_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
@@ -68,6 +69,7 @@
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/download_test_observer.h"
 #include "content/public/test/navigation_handle_observer.h"
+#include "content/public/test/no_renderer_crashes_assertion.h"
 #include "content/public/test/test_frame_navigation_observer.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
@@ -22991,6 +22993,152 @@
   EXPECT_TRUE(controller.CanGoForward());
 }
 
+// ContentBrowserClient that blocks all debug URLs.
+class BlockInternalUrlsContentBrowserClient
+    : public ContentBrowserTestContentBrowserClient {
+ public:
+  BlockInternalUrlsContentBrowserClient() = default;
+
+  BlockInternalUrlsContentBrowserClient(
+      const BlockInternalUrlsContentBrowserClient&) = delete;
+  BlockInternalUrlsContentBrowserClient& operator=(
+      const BlockInternalUrlsContentBrowserClient&) = delete;
+
+  // ContentBrowserClient
+  bool ShouldBlockRendererDebugURL(
+      const GURL& url,
+      BrowserContext* context,
+      RenderFrameHost* render_frame_host) override {
+    ++should_block_url_call_count_;
+    return block_debug_urls_;
+  }
+  bool IsExplicitNavigation(ui::PageTransition transition) override {
+    return true;
+  }
+
+  unsigned int GetShouldBlockRendererDebugURLCallCount() {
+    return should_block_url_call_count_;
+  }
+
+  void SetBlockDebugUrls(bool block) { block_debug_urls_ = block; }
+
+ private:
+  unsigned int should_block_url_call_count_ = 0U;
+  bool block_debug_urls_ = true;
+};
+
+IN_PROC_BROWSER_TEST_P(NavigationControllerBrowserTest,
+                       LoadURL_HandleBlockedBrowserDebugUrls) {
+  BlockInternalUrlsContentBrowserClient content_browser_client;
+  NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
+      shell()->web_contents()->GetController());
+  NavigationEntryImpl* last_entry = controller.GetLastCommittedEntry();
+
+  // Navigate to a page to force the renderer process to start.
+  {
+    TestNavigationObserver same_tab_observer(shell()->web_contents(), 1);
+    EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
+    same_tab_observer.Wait();
+    EXPECT_EQ(1, controller.GetEntryCount());
+    last_entry = controller.GetLastCommittedEntry();
+    EXPECT_EQ(GURL(url::kAboutBlankURL), last_entry->GetURL());
+    //  Verify that the entry is not classified as an error page.
+    EXPECT_EQ(PAGE_TYPE_NORMAL, last_entry->GetPageType());
+    EXPECT_FALSE(contents()->GetPrimaryMainFrame()->IsErrorDocument());
+  }
+
+  // Navigate to a blocked debug URL. Action should not trigger, i.e. a crash
+  // should not happen.
+  GURL crash_browser_url(blink::kChromeUIBrowserDcheckURL);
+  EXPECT_FALSE(controller.LoadURL(crash_browser_url, Referrer(),
+                                  ui::PAGE_TRANSITION_LINK,
+                                  /*extra_headers=*/std::string()));
+
+  EXPECT_EQ(1U,
+            content_browser_client.GetShouldBlockRendererDebugURLCallCount());
+  EXPECT_EQ(last_entry, controller.GetLastCommittedEntry());
+  //  Verify that the entry is not classified as an error page.
+  EXPECT_EQ(PAGE_TYPE_NORMAL, last_entry->GetPageType());
+  EXPECT_FALSE(contents()->GetPrimaryMainFrame()->IsErrorDocument());
+  // Make sure the renderer process has not been killed.
+  FrameTreeNode* root = contents()->GetPrimaryFrameTree().root();
+  EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
+
+  // - Death tests misbehave on Android, http://crbug.com/643760.
+#if !BUILDFLAG(IS_ANDROID) && defined(GTEST_HAS_DEATH_TEST)
+  // Disable the policy. Debug URL now crashes the browser process.
+  content_browser_client.SetBlockDebugUrls(false);
+  EXPECT_DCHECK_DEATH({
+    controller.LoadURL(crash_browser_url, Referrer(), ui::PAGE_TRANSITION_LINK,
+                       /*extra_headers=*/std::string());
+    EXPECT_EQ(2U,
+              content_browser_client.GetShouldBlockRendererDebugURLCallCount());
+  });
+#endif  // !BUILDFLAG(IS_ANDROID) && defined(GTEST_HAS_DEATH_TEST)
+}
+
+IN_PROC_BROWSER_TEST_P(NavigationControllerBrowserTest,
+                       LoadURL_HandleBlockedRendererDebugUrls) {
+  BlockInternalUrlsContentBrowserClient content_browser_client;
+  NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
+      shell()->web_contents()->GetController());
+  EXPECT_EQ(1, controller.GetEntryCount());
+  NavigationEntryImpl* last_entry = controller.GetLastCommittedEntry();
+
+  // Navigate to a page to force the renderer process to start.
+  {
+    TestNavigationObserver same_tab_observer(shell()->web_contents(), 1);
+    EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
+    same_tab_observer.Wait();
+    EXPECT_EQ(1, controller.GetEntryCount());
+    last_entry = controller.GetLastCommittedEntry();
+    EXPECT_EQ(GURL(url::kAboutBlankURL), last_entry->GetURL());
+    //  Verify that the entry is not classified as an error page.
+    EXPECT_EQ(PAGE_TYPE_NORMAL, last_entry->GetPageType());
+    EXPECT_FALSE(contents()->GetPrimaryMainFrame()->IsErrorDocument());
+  }
+
+  // Navigate to a blocked debug URL. Action should not trigger, i.e. a crash
+  // should not happen.
+  GURL debug_url(blink::kChromeUIKillURL);
+  EXPECT_FALSE(controller.LoadURL(debug_url, Referrer(),
+                                  ui::PAGE_TRANSITION_LINK, std::string()));
+  EXPECT_EQ(1U,
+            content_browser_client.GetShouldBlockRendererDebugURLCallCount());
+  EXPECT_FALSE(controller.GetPendingEntry());
+  EXPECT_EQ(1, controller.GetEntryCount());
+  EXPECT_EQ(last_entry, controller.GetLastCommittedEntry());
+  //  Verify that the entry is not classified as an error page.
+  EXPECT_EQ(PAGE_TYPE_NORMAL, last_entry->GetPageType());
+  EXPECT_FALSE(contents()->GetPrimaryMainFrame()->IsErrorDocument());
+  // Make sure the renderer process has not been killed.
+  FrameTreeNode* root = contents()->GetPrimaryFrameTree().root();
+  EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
+
+  // Disable the policy. Debug URL now crashes the renderer process.
+  content_browser_client.SetBlockDebugUrls(false);
+  {
+    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
+    WebContents* web_contents = shell()->web_contents();
+    RenderProcessHost* process =
+        web_contents->GetPrimaryMainFrame()->GetProcess();
+    content::RenderProcessHostWatcher exit_observer(
+        process, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+
+    controller.LoadURL(debug_url, Referrer(), ui::PAGE_TRANSITION_LINK,
+                       std::string());
+    exit_observer.Wait();
+
+    EXPECT_EQ(2U,
+              content_browser_client.GetShouldBlockRendererDebugURLCallCount());
+    EXPECT_TRUE(controller.GetPendingEntry());
+    EXPECT_EQ(1, controller.GetEntryCount());
+    EXPECT_TRUE(web_contents->IsCrashed());
+    EXPECT_FALSE(exit_observer.did_exit_normally());
+    EXPECT_FALSE(root->current_frame_host()->IsRenderFrameLive());
+  }
+}
+
 class IgnoreDuplicateNavsBrowserTest
     : public NavigationControllerBrowserTestBase,
       public testing::WithParamInterface<
diff --git a/content/browser/service_worker/OWNERS b/content/browser/service_worker/OWNERS
index 62baa80..39775ed 100644
--- a/content/browser/service_worker/OWNERS
+++ b/content/browser/service_worker/OWNERS
@@ -19,7 +19,7 @@
 mek@chromium.org
 nhiroki@chromium.org
 sisidovski@chromium.org
-wanderview@chromium.org
+wanderview@meta.com
 yyanagisawa@chromium.org
 
 # per-file rules:
diff --git a/content/browser/worker_host/OWNERS b/content/browser/worker_host/OWNERS
index a1d7d144..878105fc 100644
--- a/content/browser/worker_host/OWNERS
+++ b/content/browser/worker_host/OWNERS
@@ -1,4 +1,4 @@
 asamidoi@chromium.org
 nhiroki@chromium.org
-wanderview@chromium.org
+wanderview@meta.com
 yyanagisawa@chromium.org
diff --git a/content/public/test/android/OWNERS b/content/public/test/android/OWNERS
index 9e5bdd3f..f900ffe 100644
--- a/content/public/test/android/OWNERS
+++ b/content/public/test/android/OWNERS
@@ -1,6 +1,5 @@
 boliu@chromium.org
 dtrainor@chromium.org
 nyquist@chromium.org
-skyostil@chromium.org
 tedchoc@chromium.org
 yfriedman@chromium.org
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 5a09ce2..1403a31 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2742,6 +2742,7 @@
     "../browser/renderer_host/clipboard_host_impl_unittest.cc",
     "../browser/renderer_host/commit_deferring_condition_runner_unittest.cc",
     "../browser/renderer_host/cursor_manager_unittest.cc",
+    "../browser/renderer_host/debug_urls_unittest.cc",
     "../browser/renderer_host/display_feature_unittest.cc",
     "../browser/renderer_host/document_service_unittest.cc",
     "../browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc",
diff --git a/docs/security/rule-of-2.md b/docs/security/rule-of-2.md
index c67e67bb..453cd78 100644
--- a/docs/security/rule-of-2.md
+++ b/docs/security/rule-of-2.md
@@ -164,8 +164,8 @@
 memory-safe languages are approved for use in Chromium:
 * Java (on Android only)
 * Swift (on iOS only)
-* [Rust](../docs/rust.md) (for [third-party use](
-  ../docs/adding_to_third_party.md#Rust))
+* [Rust](../rust.md) (for [third-party use](
+  ../adding_to_third_party.md#Rust))
 * JavaScript or WebAssembly (although we don't currently use them in
   high-privilege processes like the browser/gpu process)
 
diff --git a/extensions/common/api/schema.gni b/extensions/common/api/schema.gni
index aa5dda0..dc07342 100644
--- a/extensions/common/api/schema.gni
+++ b/extensions/common/api/schema.gni
@@ -11,6 +11,7 @@
 extensions_api_schema_files_ = [
   "alarms.idl",
   "declarative_net_request.idl",
+  "events.json",
   "extension_types.json",
   "file_handlers.idl",
   "i18n.json",
@@ -60,7 +61,6 @@
     "cec_private.idl",
     "clipboard.idl",
     "dns.idl",
-    "events.json",
     "extension_options_internal.idl",
     "feedback_private.idl",
     "file_system.idl",
@@ -105,7 +105,6 @@
 if (enable_desktop_android_extensions) {
   extensions_types_only_schema_files_ += [
     "clipboard.idl",
-    "events.json",
     "icon_variants.idl",
     "scripts_internal.idl",
     "system_display.idl",
diff --git a/extensions/renderer/api/core_extensions_renderer_api_provider.cc b/extensions/renderer/api/core_extensions_renderer_api_provider.cc
index 75b5db9f4..6832b818 100644
--- a/extensions/renderer/api/core_extensions_renderer_api_provider.cc
+++ b/extensions/renderer/api/core_extensions_renderer_api_provider.cc
@@ -162,8 +162,12 @@
     const char* name = nullptr;
     int id = 0;
   } js_resources[] = {
+#if BUILDFLAG(ENABLE_PLATFORM_APPS)
       {"appView", IDR_APP_VIEW_JS},
+      {"appViewDeny", IDR_APP_VIEW_DENY_JS},
       {"appViewElement", IDR_APP_VIEW_ELEMENT_JS},
+#endif
+
       {"entryIdManager", IDR_ENTRY_ID_MANAGER},
       {"extensionOptions", IDR_EXTENSION_OPTIONS_JS},
       {"extensionOptionsElement", IDR_EXTENSION_OPTIONS_ELEMENT_JS},
@@ -174,10 +178,6 @@
       {"fileEntryBindingUtil", IDR_FILE_ENTRY_BINDING_UTIL_JS},
       {"fileSystem", IDR_FILE_SYSTEM_CUSTOM_BINDINGS_JS},
 
-#if BUILDFLAG(ENABLE_PLATFORM_APPS)
-      {"appViewDeny", IDR_APP_VIEW_DENY_JS},
-#endif
-
 #if BUILDFLAG(ENABLE_GUEST_VIEW)
       {"guestView", IDR_GUEST_VIEW_JS},
       {"guestViewAttributes", IDR_GUEST_VIEW_ATTRIBUTES_JS},
@@ -230,8 +230,10 @@
       {"automationEvent", IDR_AUTOMATION_EVENT_JS},
       {"automationNode", IDR_AUTOMATION_NODE_JS},
       {"automationTreeCache", IDR_AUTOMATION_TREE_CACHE_JS},
+#if BUILDFLAG(ENABLE_PLATFORM_APPS)
       {"app.runtime", IDR_APP_RUNTIME_CUSTOM_BINDINGS_JS},
       {"app.window", IDR_APP_WINDOW_CUSTOM_BINDINGS_JS},
+#endif
       {"declarativeWebRequest", IDR_DECLARATIVE_WEBREQUEST_CUSTOM_BINDINGS_JS},
       {"contextMenus", IDR_CONTEXT_MENUS_CUSTOM_BINDINGS_JS},
       {"contextMenusHandlers", IDR_CONTEXT_MENUS_HANDLERS_JS},
@@ -242,8 +244,10 @@
       {"printerProvider", IDR_PRINTER_PROVIDER_CUSTOM_BINDINGS_JS},
       {"webViewRequest", IDR_WEB_VIEW_REQUEST_CUSTOM_BINDINGS_JS},
 
+#if BUILDFLAG(ENABLE_PLATFORM_APPS)
       // Platform app sources that are not API-specific..
       {"platformApp", IDR_PLATFORM_APP_JS},
+#endif
   };
 
   for (const auto& resource : js_resources) {
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 2acb761..72f68df 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -512,10 +512,12 @@
 
   bindings_system_->DidCreateScriptContext(context);
 
+#if BUILDFLAG(ENABLE_PLATFORM_APPS)
   // Inject custom JS into the platform app context.
   if (IsWithinPlatformApp()) {
     module_system->Require("platformApp");
   }
+#endif
 
   RequireGuestViewModules(context);
 
@@ -1538,7 +1540,6 @@
       context->context_type() == mojom::ContextType::kPrivilegedExtension &&
       !context->IsForServiceWorker() && context->extension() &&
       context->extension()->is_platform_app();
-  const bool app_view_permission_exists = is_platform_app;
   // The webview permission is also available to internal allowlisted
   // extensions, but not to extensions in general.
   const bool web_view_permission_exists = is_platform_app;
@@ -1547,6 +1548,8 @@
   // It would be better if there were a light way of detecting when a webview
   // or appview is created and only then set up the infrastructure.
 
+#if BUILDFLAG(ENABLE_PLATFORM_APPS)
+  const bool app_view_permission_exists = is_platform_app;
   // Require AppView.
   if (context->GetAvailability("appViewEmbedderInternal").is_available()) {
     requires_guest_view_module = true;
@@ -1554,6 +1557,7 @@
   } else if (app_view_permission_exists) {
     module_system->Require("appViewDeny");
   }
+#endif
 
 #if BUILDFLAG(ENABLE_GUEST_VIEW)
   // Require ExtensionOptions.
diff --git a/gpu/command_buffer/service/service_utils.cc b/gpu/command_buffer/service/service_utils.cc
index 09aa932..fa29a141 100644
--- a/gpu/command_buffer/service/service_utils.cc
+++ b/gpu/command_buffer/service/service_utils.cc
@@ -324,25 +324,12 @@
     [[maybe_unused]] auto value =
         command_line->GetSwitchValueASCII(switches::kSkiaGraphiteBackend);
 #if BUILDFLAG(SKIA_USE_DAWN)
-#if !BUILDFLAG(IS_IOS)
-    // TODO(sunnyps): Temporarily use Graphite Metal as the default backend on
-    // iOS Blink until the Dawn backend can be brought up.
-    if (value.empty()) {
-      return GrContextType::kGraphiteDawn;
-    }
-#endif  // !BUILDFLAG(IS_IOS)
-    if (base::StartsWith(value, switches::kSkiaGraphiteBackendDawn)) {
+    if (value.empty() ||
+        base::StartsWith(value, switches::kSkiaGraphiteBackendDawn)) {
       return GrContextType::kGraphiteDawn;
     }
 #endif  // BUILDFLAG(SKIA_USE_DAWN)
 #if BUILDFLAG(SKIA_USE_METAL)
-#if BUILDFLAG(IS_IOS)
-    // TODO(sunnyps): Temporarily use Graphite Metal as the default backend on
-    // iOS Blink until the Dawn backend can be brought up.
-    if (value.empty()) {
-      return GrContextType::kGraphiteMetal;
-    }
-#endif  // BUILDFLAG(IS_IOS)
     if (value == switches::kSkiaGraphiteBackendMetal) {
       return GrContextType::kGraphiteMetal;
     }
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index 0bb9cc6..607bc7c 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -3376,7 +3376,12 @@
       "vendor_id": "0x8086",
       "multi_gpu_category": "any",
       "intel_gpu_series": [
+            "tigerlake",
+            "rocketlake",
+            "dg1",
             "alderlake",
+            "alchemist",
+            "raptorlake",
             "meteorlake",
             "arrowlake",
             "lunarlake",
diff --git a/headless/test/headless_browser_browsertest.cc b/headless/test/headless_browser_browsertest.cc
index 82da43e..2c8fbdf 100644
--- a/headless/test/headless_browser_browsertest.cc
+++ b/headless/test/headless_browser_browsertest.cc
@@ -368,44 +368,6 @@
               DictHasValue("result.result.value", expected_height));
 }
 
-class HeadlessBrowserWindowSizeTest : public HeadlessBrowserTest {
- public:
-  static constexpr gfx::Size kWindowSize = {1920, 1080};
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    HeadlessBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitchASCII(
-        switches::kWindowSize,
-        base::StringPrintf("%u,%u", kWindowSize.width(), kWindowSize.height()));
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(HeadlessBrowserWindowSizeTest, WindowSize) {
-  HeadlessBrowserContext* browser_context =
-      browser()->CreateBrowserContextBuilder().Build();
-
-  HeadlessWebContents* web_contents =
-      browser_context->CreateWebContentsBuilder().Build();
-
-  const int expected_width = kWindowSize.width();
-  const int expected_height = kWindowSize.height();
-
-  EXPECT_THAT(EvaluateScript(web_contents, "screen.width"),
-              DictHasValue("result.result.value", expected_width));
-  EXPECT_THAT(EvaluateScript(web_contents, "screen.height"),
-              DictHasValue("result.result.value", expected_height));
-
-  EXPECT_THAT(EvaluateScript(web_contents, "window.outerWidth"),
-              DictHasValue("result.result.value", expected_width));
-  EXPECT_THAT(EvaluateScript(web_contents, "window.outerHeight"),
-              DictHasValue("result.result.value", expected_height));
-
-  EXPECT_THAT(EvaluateScript(web_contents, "window.innerWidth"),
-              DictHasValue("result.result.value", expected_width));
-  EXPECT_THAT(EvaluateScript(web_contents, "window.innerHeight"),
-              DictHasValue("result.result.value", expected_height));
-}
-
 // TODO(skyostil): This test currently relies on being able to run a shell
 // script.
 #if BUILDFLAG(IS_POSIX)
diff --git a/infra/config/generated/builders/ci/android-desktop-15-x64-fyi-rel/targets/chromium.android.desktop.fyi.json b/infra/config/generated/builders/ci/android-desktop-15-x64-fyi-rel/targets/chromium.android.desktop.fyi.json
index dbbf45d2..a3d6908 100644
--- a/infra/config/generated/builders/ci/android-desktop-15-x64-fyi-rel/targets/chromium.android.desktop.fyi.json
+++ b/infra/config/generated/builders/ci/android-desktop-15-x64-fyi-rel/targets/chromium.android.desktop.fyi.json
@@ -34,13 +34,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -81,13 +81,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -135,13 +135,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -182,13 +182,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/targets/chromium.android.desktop.json b/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/targets/chromium.android.desktop.json
index c297eb0..71e975be 100644
--- a/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/targets/chromium.android.desktop.json
+++ b/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/targets/chromium.android.desktop.json
@@ -40,13 +40,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -95,13 +95,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -142,13 +142,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -189,13 +189,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
diff --git a/infra/config/generated/builders/ci/android-desktop-x64-rel-15-tests/targets/chromium.android.desktop.json b/infra/config/generated/builders/ci/android-desktop-x64-rel-15-tests/targets/chromium.android.desktop.json
index 836dd9a..e267869 100644
--- a/infra/config/generated/builders/ci/android-desktop-x64-rel-15-tests/targets/chromium.android.desktop.json
+++ b/infra/config/generated/builders/ci/android-desktop-x64-rel-15-tests/targets/chromium.android.desktop.json
@@ -35,13 +35,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -90,13 +90,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -137,13 +137,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -184,13 +184,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
diff --git a/infra/config/generated/builders/ci/chromeos-amd64-generic-asan-rel/properties.json b/infra/config/generated/builders/ci/chromeos-amd64-generic-asan-rel/properties.json
index a32a6aa..59b95f0e 100644
--- a/infra/config/generated/builders/ci/chromeos-amd64-generic-asan-rel/properties.json
+++ b/infra/config/generated/builders/ci/chromeos-amd64-generic-asan-rel/properties.json
@@ -63,9 +63,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-trusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/chromeos-amd64-generic-asan-rel/shadow-properties.json b/infra/config/generated/builders/ci/chromeos-amd64-generic-asan-rel/shadow-properties.json
index d962ff0b..dc0a10e9 100644
--- a/infra/config/generated/builders/ci/chromeos-amd64-generic-asan-rel/shadow-properties.json
+++ b/infra/config/generated/builders/ci/chromeos-amd64-generic-asan-rel/shadow-properties.json
@@ -6,9 +6,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-untrusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/chromeos-amd64-generic-cfi-thin-lto-rel/properties.json b/infra/config/generated/builders/ci/chromeos-amd64-generic-cfi-thin-lto-rel/properties.json
index 1ab92fb6..a2d359a 100644
--- a/infra/config/generated/builders/ci/chromeos-amd64-generic-cfi-thin-lto-rel/properties.json
+++ b/infra/config/generated/builders/ci/chromeos-amd64-generic-cfi-thin-lto-rel/properties.json
@@ -63,9 +63,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-trusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/chromeos-amd64-generic-cfi-thin-lto-rel/shadow-properties.json b/infra/config/generated/builders/ci/chromeos-amd64-generic-cfi-thin-lto-rel/shadow-properties.json
index d962ff0b..dc0a10e9 100644
--- a/infra/config/generated/builders/ci/chromeos-amd64-generic-cfi-thin-lto-rel/shadow-properties.json
+++ b/infra/config/generated/builders/ci/chromeos-amd64-generic-cfi-thin-lto-rel/shadow-properties.json
@@ -6,9 +6,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-untrusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/chromeos-amd64-generic-dbg/properties.json b/infra/config/generated/builders/ci/chromeos-amd64-generic-dbg/properties.json
index aa6a70c..304fa6a2 100644
--- a/infra/config/generated/builders/ci/chromeos-amd64-generic-dbg/properties.json
+++ b/infra/config/generated/builders/ci/chromeos-amd64-generic-dbg/properties.json
@@ -63,9 +63,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-trusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/chromeos-amd64-generic-dbg/shadow-properties.json b/infra/config/generated/builders/ci/chromeos-amd64-generic-dbg/shadow-properties.json
index d962ff0b..dc0a10e9 100644
--- a/infra/config/generated/builders/ci/chromeos-amd64-generic-dbg/shadow-properties.json
+++ b/infra/config/generated/builders/ci/chromeos-amd64-generic-dbg/shadow-properties.json
@@ -6,9 +6,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-untrusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/chromeos-amd64-generic-rel/properties.json b/infra/config/generated/builders/ci/chromeos-amd64-generic-rel/properties.json
index ee5b800..5900f39 100644
--- a/infra/config/generated/builders/ci/chromeos-amd64-generic-rel/properties.json
+++ b/infra/config/generated/builders/ci/chromeos-amd64-generic-rel/properties.json
@@ -166,9 +166,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-trusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/chromeos-amd64-generic-rel/shadow-properties.json b/infra/config/generated/builders/ci/chromeos-amd64-generic-rel/shadow-properties.json
index d962ff0b..dc0a10e9 100644
--- a/infra/config/generated/builders/ci/chromeos-amd64-generic-rel/shadow-properties.json
+++ b/infra/config/generated/builders/ci/chromeos-amd64-generic-rel/shadow-properties.json
@@ -6,9 +6,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-untrusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/chromeos-arm-generic-dbg/properties.json b/infra/config/generated/builders/ci/chromeos-arm-generic-dbg/properties.json
index 95e813b..915141b 100644
--- a/infra/config/generated/builders/ci/chromeos-arm-generic-dbg/properties.json
+++ b/infra/config/generated/builders/ci/chromeos-arm-generic-dbg/properties.json
@@ -63,9 +63,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-trusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/chromeos-arm-generic-dbg/shadow-properties.json b/infra/config/generated/builders/ci/chromeos-arm-generic-dbg/shadow-properties.json
index d962ff0b..dc0a10e9 100644
--- a/infra/config/generated/builders/ci/chromeos-arm-generic-dbg/shadow-properties.json
+++ b/infra/config/generated/builders/ci/chromeos-arm-generic-dbg/shadow-properties.json
@@ -6,9 +6,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-untrusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/chromeos-arm-generic-rel/properties.json b/infra/config/generated/builders/ci/chromeos-arm-generic-rel/properties.json
index 4a3c5b6..a3be85e 100644
--- a/infra/config/generated/builders/ci/chromeos-arm-generic-rel/properties.json
+++ b/infra/config/generated/builders/ci/chromeos-arm-generic-rel/properties.json
@@ -62,9 +62,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-trusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/chromeos-arm-generic-rel/shadow-properties.json b/infra/config/generated/builders/ci/chromeos-arm-generic-rel/shadow-properties.json
index d962ff0b..dc0a10e9 100644
--- a/infra/config/generated/builders/ci/chromeos-arm-generic-rel/shadow-properties.json
+++ b/infra/config/generated/builders/ci/chromeos-arm-generic-rel/shadow-properties.json
@@ -6,9 +6,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-untrusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/chromeos-arm64-generic-rel/properties.json b/infra/config/generated/builders/ci/chromeos-arm64-generic-rel/properties.json
index b6d46572..d2386358 100644
--- a/infra/config/generated/builders/ci/chromeos-arm64-generic-rel/properties.json
+++ b/infra/config/generated/builders/ci/chromeos-arm64-generic-rel/properties.json
@@ -62,9 +62,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-trusted",
     "remote_jobs": 250
diff --git a/infra/config/generated/builders/ci/chromeos-arm64-generic-rel/shadow-properties.json b/infra/config/generated/builders/ci/chromeos-arm64-generic-rel/shadow-properties.json
index 9eb5370..78dedff8 100644
--- a/infra/config/generated/builders/ci/chromeos-arm64-generic-rel/shadow-properties.json
+++ b/infra/config/generated/builders/ci/chromeos-arm64-generic-rel/shadow-properties.json
@@ -6,9 +6,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-untrusted",
     "remote_jobs": 250
diff --git a/infra/config/generated/builders/ci/chromeos-jacuzzi-rel/properties.json b/infra/config/generated/builders/ci/chromeos-jacuzzi-rel/properties.json
index fb32a3c..2d8091fa 100644
--- a/infra/config/generated/builders/ci/chromeos-jacuzzi-rel/properties.json
+++ b/infra/config/generated/builders/ci/chromeos-jacuzzi-rel/properties.json
@@ -66,9 +66,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-trusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/chromeos-jacuzzi-rel/shadow-properties.json b/infra/config/generated/builders/ci/chromeos-jacuzzi-rel/shadow-properties.json
index d962ff0b..dc0a10e9 100644
--- a/infra/config/generated/builders/ci/chromeos-jacuzzi-rel/shadow-properties.json
+++ b/infra/config/generated/builders/ci/chromeos-jacuzzi-rel/shadow-properties.json
@@ -6,9 +6,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-untrusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/chromeos-octopus-rel/properties.json b/infra/config/generated/builders/ci/chromeos-octopus-rel/properties.json
index 44b631b..0124f3b 100644
--- a/infra/config/generated/builders/ci/chromeos-octopus-rel/properties.json
+++ b/infra/config/generated/builders/ci/chromeos-octopus-rel/properties.json
@@ -66,9 +66,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-trusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/chromeos-octopus-rel/shadow-properties.json b/infra/config/generated/builders/ci/chromeos-octopus-rel/shadow-properties.json
index d962ff0b..dc0a10e9 100644
--- a/infra/config/generated/builders/ci/chromeos-octopus-rel/shadow-properties.json
+++ b/infra/config/generated/builders/ci/chromeos-octopus-rel/shadow-properties.json
@@ -6,9 +6,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-untrusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/linux-cfm-rel/properties.json b/infra/config/generated/builders/ci/linux-cfm-rel/properties.json
index 90392982..e49d7d8c5 100644
--- a/infra/config/generated/builders/ci/linux-cfm-rel/properties.json
+++ b/infra/config/generated/builders/ci/linux-cfm-rel/properties.json
@@ -60,9 +60,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-trusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/linux-cfm-rel/shadow-properties.json b/infra/config/generated/builders/ci/linux-cfm-rel/shadow-properties.json
index d962ff0b..dc0a10e9 100644
--- a/infra/config/generated/builders/ci/linux-cfm-rel/shadow-properties.json
+++ b/infra/config/generated/builders/ci/linux-cfm-rel/shadow-properties.json
@@ -6,9 +6,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-untrusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/linux-chromeos-dbg/properties.json b/infra/config/generated/builders/ci/linux-chromeos-dbg/properties.json
index eb3663c..a1fedac 100644
--- a/infra/config/generated/builders/ci/linux-chromeos-dbg/properties.json
+++ b/infra/config/generated/builders/ci/linux-chromeos-dbg/properties.json
@@ -65,9 +65,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-trusted",
diff --git a/infra/config/generated/builders/ci/linux-chromeos-dbg/shadow-properties.json b/infra/config/generated/builders/ci/linux-chromeos-dbg/shadow-properties.json
index 092101a..f1b7529 100644
--- a/infra/config/generated/builders/ci/linux-chromeos-dbg/shadow-properties.json
+++ b/infra/config/generated/builders/ci/linux-chromeos-dbg/shadow-properties.json
@@ -7,9 +7,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/ci/linux-chromeos-rel/properties.json b/infra/config/generated/builders/ci/linux-chromeos-rel/properties.json
index 9fcea66..30c99f2 100644
--- a/infra/config/generated/builders/ci/linux-chromeos-rel/properties.json
+++ b/infra/config/generated/builders/ci/linux-chromeos-rel/properties.json
@@ -61,9 +61,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-trusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/ci/linux-chromeos-rel/shadow-properties.json b/infra/config/generated/builders/ci/linux-chromeos-rel/shadow-properties.json
index d962ff0b..dc0a10e9 100644
--- a/infra/config/generated/builders/ci/linux-chromeos-rel/shadow-properties.json
+++ b/infra/config/generated/builders/ci/linux-chromeos-rel/shadow-properties.json
@@ -6,9 +6,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "project": "rbe-chromium-untrusted",
     "remote_jobs": 500
diff --git a/infra/config/generated/builders/try/android-desktop-15-x64-fyi-rel/targets/chromium.android.desktop.fyi.json b/infra/config/generated/builders/try/android-desktop-15-x64-fyi-rel/targets/chromium.android.desktop.fyi.json
index dbbf45d2..a3d6908 100644
--- a/infra/config/generated/builders/try/android-desktop-15-x64-fyi-rel/targets/chromium.android.desktop.fyi.json
+++ b/infra/config/generated/builders/try/android-desktop-15-x64-fyi-rel/targets/chromium.android.desktop.fyi.json
@@ -34,13 +34,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -81,13 +81,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -135,13 +135,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -182,13 +182,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/infra/config/generated/builders/try/android-desktop-15-x64-rel/targets/chromium.android.desktop.json b/infra/config/generated/builders/try/android-desktop-15-x64-rel/targets/chromium.android.desktop.json
index c297eb0..71e975be 100644
--- a/infra/config/generated/builders/try/android-desktop-15-x64-rel/targets/chromium.android.desktop.json
+++ b/infra/config/generated/builders/try/android-desktop-15-x64-rel/targets/chromium.android.desktop.json
@@ -40,13 +40,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -95,13 +95,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -142,13 +142,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -189,13 +189,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
diff --git a/infra/config/generated/builders/try/android-desktop-x64-rel/targets/chromium.android.desktop.json b/infra/config/generated/builders/try/android-desktop-x64-rel/targets/chromium.android.desktop.json
index c297eb0..71e975be 100644
--- a/infra/config/generated/builders/try/android-desktop-x64-rel/targets/chromium.android.desktop.json
+++ b/infra/config/generated/builders/try/android-desktop-x64-rel/targets/chromium.android.desktop.json
@@ -40,13 +40,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -95,13 +95,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -142,13 +142,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -189,13 +189,13 @@
           },
           "named_caches": [
             {
-              "name": "android_35_google_apis_tablet_x64",
-              "path": ".android_emulator/android_35_google_apis_tablet_x64"
+              "name": "android_35_google_apis_tablet_x64_tablet_landscape",
+              "path": ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_35_google_apis_tablet_x64"
+              "caches": "android_35_google_apis_tablet_x64_tablet_landscape"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
diff --git a/infra/config/generated/builders/try/chromeos-amd64-generic-asan-rel/properties.json b/infra/config/generated/builders/try/chromeos-amd64-generic-asan-rel/properties.json
index fdbe220..b8a2df62 100644
--- a/infra/config/generated/builders/try/chromeos-amd64-generic-asan-rel/properties.json
+++ b/infra/config/generated/builders/try/chromeos-amd64-generic-asan-rel/properties.json
@@ -57,9 +57,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/chromeos-amd64-generic-cfi-thin-lto-rel/properties.json b/infra/config/generated/builders/try/chromeos-amd64-generic-cfi-thin-lto-rel/properties.json
index 9c557a9..035effa 100644
--- a/infra/config/generated/builders/try/chromeos-amd64-generic-cfi-thin-lto-rel/properties.json
+++ b/infra/config/generated/builders/try/chromeos-amd64-generic-cfi-thin-lto-rel/properties.json
@@ -57,9 +57,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/chromeos-amd64-generic-dbg/properties.json b/infra/config/generated/builders/try/chromeos-amd64-generic-dbg/properties.json
index 749b368..f6cc206b 100644
--- a/infra/config/generated/builders/try/chromeos-amd64-generic-dbg/properties.json
+++ b/infra/config/generated/builders/try/chromeos-amd64-generic-dbg/properties.json
@@ -61,9 +61,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest-and-tast/properties.json b/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest-and-tast/properties.json
index 0d2ae4c..191f06a 100644
--- a/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest-and-tast/properties.json
+++ b/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest-and-tast/properties.json
@@ -160,9 +160,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest/properties.json b/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest/properties.json
index 9cb382d..8e1b2c4 100644
--- a/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest/properties.json
+++ b/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest/properties.json
@@ -116,9 +116,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/chromeos-amd64-generic-rel/properties.json b/infra/config/generated/builders/try/chromeos-amd64-generic-rel/properties.json
index 7f4280ce..eb6d12c2 100644
--- a/infra/config/generated/builders/try/chromeos-amd64-generic-rel/properties.json
+++ b/infra/config/generated/builders/try/chromeos-amd64-generic-rel/properties.json
@@ -62,9 +62,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/chromeos-arm-generic-dbg/properties.json b/infra/config/generated/builders/try/chromeos-arm-generic-dbg/properties.json
index 82d80c8..ab0b713 100644
--- a/infra/config/generated/builders/try/chromeos-arm-generic-dbg/properties.json
+++ b/infra/config/generated/builders/try/chromeos-arm-generic-dbg/properties.json
@@ -57,9 +57,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/chromeos-arm-generic-rel/properties.json b/infra/config/generated/builders/try/chromeos-arm-generic-rel/properties.json
index 94cd9870..24179a4 100644
--- a/infra/config/generated/builders/try/chromeos-arm-generic-rel/properties.json
+++ b/infra/config/generated/builders/try/chromeos-arm-generic-rel/properties.json
@@ -56,9 +56,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/chromeos-arm64-generic-rel/properties.json b/infra/config/generated/builders/try/chromeos-arm64-generic-rel/properties.json
index 60386d0..c3325572 100644
--- a/infra/config/generated/builders/try/chromeos-arm64-generic-rel/properties.json
+++ b/infra/config/generated/builders/try/chromeos-arm64-generic-rel/properties.json
@@ -60,9 +60,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/chromeos-jacuzzi-rel/properties.json b/infra/config/generated/builders/try/chromeos-jacuzzi-rel/properties.json
index 9ad90028..6e7c4f2 100644
--- a/infra/config/generated/builders/try/chromeos-jacuzzi-rel/properties.json
+++ b/infra/config/generated/builders/try/chromeos-jacuzzi-rel/properties.json
@@ -61,9 +61,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/chromeos-libfuzzer-asan-rel/properties.json b/infra/config/generated/builders/try/chromeos-libfuzzer-asan-rel/properties.json
index 30b3580..00e971df 100644
--- a/infra/config/generated/builders/try/chromeos-libfuzzer-asan-rel/properties.json
+++ b/infra/config/generated/builders/try/chromeos-libfuzzer-asan-rel/properties.json
@@ -52,9 +52,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/chromeos-octopus-rel/properties.json b/infra/config/generated/builders/try/chromeos-octopus-rel/properties.json
index 11f2cd5..cb0c0f7 100644
--- a/infra/config/generated/builders/try/chromeos-octopus-rel/properties.json
+++ b/infra/config/generated/builders/try/chromeos-octopus-rel/properties.json
@@ -61,9 +61,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/linux-cfm-rel/properties.json b/infra/config/generated/builders/try/linux-cfm-rel/properties.json
index f15e395..07c46a7 100644
--- a/infra/config/generated/builders/try/linux-cfm-rel/properties.json
+++ b/infra/config/generated/builders/try/linux-cfm-rel/properties.json
@@ -58,9 +58,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/linux-chromeos-annotator-rel/properties.json b/infra/config/generated/builders/try/linux-chromeos-annotator-rel/properties.json
index 5c0773d..5d0ad455 100644
--- a/infra/config/generated/builders/try/linux-chromeos-annotator-rel/properties.json
+++ b/infra/config/generated/builders/try/linux-chromeos-annotator-rel/properties.json
@@ -53,9 +53,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/linux-chromeos-compile-dbg/properties.json b/infra/config/generated/builders/try/linux-chromeos-compile-dbg/properties.json
index 1ca6f1d..c47c02e 100644
--- a/infra/config/generated/builders/try/linux-chromeos-compile-dbg/properties.json
+++ b/infra/config/generated/builders/try/linux-chromeos-compile-dbg/properties.json
@@ -59,9 +59,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/linux-chromeos-dbg-oslogin/properties.json b/infra/config/generated/builders/try/linux-chromeos-dbg-oslogin/properties.json
index cd3363b6..4d9cf68 100644
--- a/infra/config/generated/builders/try/linux-chromeos-dbg-oslogin/properties.json
+++ b/infra/config/generated/builders/try/linux-chromeos-dbg-oslogin/properties.json
@@ -53,9 +53,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/linux-chromeos-dbg/properties.json b/infra/config/generated/builders/try/linux-chromeos-dbg/properties.json
index 76cee8a..18d4f07 100644
--- a/infra/config/generated/builders/try/linux-chromeos-dbg/properties.json
+++ b/infra/config/generated/builders/try/linux-chromeos-dbg/properties.json
@@ -54,9 +54,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/generated/builders/try/linux-chromeos-rel/properties.json b/infra/config/generated/builders/try/linux-chromeos-rel/properties.json
index ce27e24..0f948afa 100644
--- a/infra/config/generated/builders/try/linux-chromeos-rel/properties.json
+++ b/infra/config/generated/builders/try/linux-chromeos-rel/properties.json
@@ -70,9 +70,7 @@
     "enable_cloud_monitoring": true,
     "enable_cloud_profiler": true,
     "enable_cloud_trace": true,
-    "experiments": [
-      "fallback-on-exec-error"
-    ],
+    "experiments": [],
     "metrics_project": "chromium-reclient-metrics",
     "output_local_strategy": "minimum",
     "project": "rbe-chromium-untrusted",
diff --git a/infra/config/subprojects/chromium/ci/chromium.chromiumos.star b/infra/config/subprojects/chromium/ci/chromium.chromiumos.star
index c7c46a0b..492d73501 100644
--- a/infra/config/subprojects/chromium/ci/chromium.chromiumos.star
+++ b/infra/config/subprojects/chromium/ci/chromium.chromiumos.star
@@ -37,8 +37,6 @@
     service_account = ci.DEFAULT_SERVICE_ACCOUNT,
     shadow_service_account = ci.DEFAULT_SHADOW_SERVICE_ACCOUNT,
     siso_enabled = True,
-    # TODO(crbug.com/406369220): Remove this when cros compiler_wrapper doesn't crash.
-    siso_experiments = ["fallback-on-exec-error"],
     siso_project = siso.project.DEFAULT_TRUSTED,
     siso_remote_jobs = siso.remote_jobs.DEFAULT,
 )
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
index fb67dee..995612cf 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
@@ -24,8 +24,6 @@
     orchestrator_siso_remote_jobs = siso.remote_jobs.HIGH_JOBS_FOR_CQ,
     service_account = try_.DEFAULT_SERVICE_ACCOUNT,
     siso_enabled = True,
-    # TODO(crbug.com/406369220): Remove this when cros compiler_wrapper doesn't crash.
-    siso_experiments = ["fallback-on-exec-error"],
     siso_project = siso.project.DEFAULT_UNTRUSTED,
     siso_remote_jobs = siso.remote_jobs.LOW_JOBS_FOR_CQ,
     siso_remote_linking = True,
diff --git a/infra/config/targets/mixins.star b/infra/config/targets/mixins.star
index 65c3539..a85e4ae 100644
--- a/infra/config/targets/mixins.star
+++ b/infra/config/targets/mixins.star
@@ -282,13 +282,13 @@
         # soft affinity so that bots with caches will be picked first
         optional_dimensions = {
             60: {
-                "caches": "android_35_google_apis_tablet_x64",
+                "caches": "android_35_google_apis_tablet_x64_tablet_landscape",
             },
         },
         named_caches = [
             swarming.cache(
-                name = "android_35_google_apis_tablet_x64",
-                path = ".android_emulator/android_35_google_apis_tablet_x64",
+                name = "android_35_google_apis_tablet_x64_tablet_landscape",
+                path = ".android_emulator/android_35_google_apis_tablet_x64_tablet_landscape",
             ),
         ],
     ),
diff --git a/internal b/internal
index 9dc1270..5c45e13 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 9dc12704a5bffd1ff42310211eaa421bf1b6f6de
+Subproject commit 5c45e13e948d0c028bd62c61ea316ec5d4e16ce1
diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn
index fb7cfb1..6192d8f 100644
--- a/media/audio/BUILD.gn
+++ b/media/audio/BUILD.gn
@@ -246,6 +246,7 @@
       "dxguid.lib",
       "setupapi.lib",
       "winmm.lib",
+      "mmdevapi.lib",
     ]
   }
 
@@ -520,6 +521,15 @@
       "win/audio_session_event_listener_win_unittest.cc",
       "win/core_audio_util_win_unittest.cc",
       "win/device_enumeration_win_unittest.cc",
+      "win/test_support/fake_iactivate_audio_interface_async_operation.cc",
+      "win/test_support/fake_iactivate_audio_interface_async_operation.h",
+      "win/test_support/fake_iaudio_capture_client.cc",
+      "win/test_support/fake_iaudio_capture_client.h",
+      "win/test_support/fake_iaudio_client.cc",
+      "win/test_support/fake_iaudio_client.h",
+      "win/test_support/fake_win_wasapi_environment.cc",
+      "win/test_support/fake_win_wasapi_environment.h",
+      "win/test_support/wasapi_test_error_code.h",
     ]
 
     deps += [ "//media/gpu:gpu" ]
diff --git a/media/audio/audio_input_stream_data_interceptor.h b/media/audio/audio_input_stream_data_interceptor.h
index a886ce4..5696ed1e 100644
--- a/media/audio/audio_input_stream_data_interceptor.h
+++ b/media/audio/audio_input_stream_data_interceptor.h
@@ -61,6 +61,9 @@
 
   void OnError() override;
 
+  // Returns the underlying stream.
+  AudioInputStream* GetUnderlyingStreamForTesting() const { return stream_; }
+
  private:
   const CreateDebugRecorderCB create_debug_recorder_cb_;
   std::unique_ptr<AudioDebugRecorder> debug_recorder_;
diff --git a/media/audio/win/audio_low_latency_input_win.cc b/media/audio/win/audio_low_latency_input_win.cc
index 0d736b5..6ff0a11 100644
--- a/media/audio/win/audio_low_latency_input_win.cc
+++ b/media/audio/win/audio_low_latency_input_win.cc
@@ -6,6 +6,10 @@
 
 #include <objbase.h>
 
+#include <mmdeviceapi.h>
+
+#include <audioclient.h>
+#include <audioclientactivationparams.h>
 #include <combaseapi.h>
 #include <ksmedia.h>
 #include <propkey.h>
@@ -15,6 +19,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/check_deref.h"
 #include "base/compiler_specific.h"
 #include "base/containers/span.h"
 #include "base/functional/callback.h"
@@ -25,6 +30,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/trace_event/common/trace_event_common.h"
 #include "base/trace_event/trace_event.h"
 #include "base/win/core_winrt_util.h"
@@ -32,6 +38,7 @@
 #include "base/win/scoped_variant.h"
 #include "base/win/vector.h"
 #include "base/win/windows_version.h"
+#include "media/audio/application_loopback_device_helper.h"
 #include "media/audio/audio_device_description.h"
 #include "media/audio/audio_device_name.h"
 #include "media/audio/audio_features.h"
@@ -191,6 +198,15 @@
                               abs_delta_time);
 }
 
+WASAPIAudioInputStream::ActivateAudioInterfaceAsyncCallback&
+GetActivateAudioInterfaceAsyncCallback() {
+  static base::NoDestructor<
+      WASAPIAudioInputStream::ActivateAudioInterfaceAsyncCallback>
+      activate_audio_interface_async_callback{
+          base::BindRepeating(&ActivateAudioInterfaceAsync)};
+  return *activate_audio_interface_async_callback;
+}
+
 }  // namespace
 
 // Counts how often an OS capture callback reports a data discontinuity and logs
@@ -447,6 +463,66 @@
       AudioDeviceDescription::kDefaultDeviceId;
 };
 
+// Helper class to synchronously wait for the activation of an audio client
+// during a call to ActivateAudioInterfaceAsync.
+class WASAPIAudioInputStream::AudioClientActivationHandler
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+          Microsoft::WRL::FtmBase,
+          IActivateAudioInterfaceCompletionHandler> {
+ public:
+  friend class FakeWinWASAPIEnvironment;
+
+  AudioClientActivationHandler() = default;
+  ~AudioClientActivationHandler() override = default;
+
+  // When called, returns only after the activation is completed.
+  HRESULT WaitAndGetAudioClient(ComPtr<IAudioClient>* audio_client,
+                                base::TimeDelta async_activation_timeout_ms) {
+    // Wait for a maximum of 10 seconds for the activation to complete.
+    if (!wait_event_.TimedWait(async_activation_timeout_ms)) {
+      return E_FAIL;
+    }
+
+    // If the activation was successful, move the audio client to the output
+    // parameter.
+    if (SUCCEEDED(activation_result_)) {
+      *audio_client = std::move(audio_client_);
+    }
+    return activation_result_;
+  }
+
+ private:
+  // IActivateAudioInterfaceAudioClientActivationHandler::ActivateCompleted
+  // implementation.
+  // Called by the OS when the activation is completed.
+  IFACEMETHODIMP ActivateCompleted(
+      IActivateAudioInterfaceAsyncOperation* activate_operation) override {
+    HRESULT hr_activate = S_OK;
+    ComPtr<IAudioClient> audio_client = nullptr;
+    activation_result_ =
+        activate_operation->GetActivateResult(&hr_activate, &audio_client);
+    if (FAILED(activation_result_)) {
+      return activation_result_;
+    }
+
+    activation_result_ = hr_activate;
+    if (SUCCEEDED(activation_result_)) {
+      audio_client_ = std::move(audio_client);
+    }
+    wait_event_.Signal();
+
+    // If the activation was successful, the audio client is now available.
+    return activation_result_;
+  }
+
+  ComPtr<IAudioClient> audio_client_ = nullptr;
+  HRESULT activation_result_ = E_FAIL;
+  base::WaitableEvent wait_event_{
+      base::WaitableEvent::ResetPolicy::AUTOMATIC,
+      base::WaitableEvent::InitialState::NOT_SIGNALED};
+};
+
 // Creates an audio input stream given preferred audio parameters in `params`
 // and an input device given by `device_id`.
 // Support for system effects exists behind a command-line flag called
@@ -512,7 +588,9 @@
           base::BindRepeating(
               static_cast<void (WASAPIAudioInputStream::*)(std::string)>(
                   &WASAPIAudioInputStream::SendLogMessage),
-              base::Unretained(this)))) {
+              base::Unretained(this)))),
+      is_application_loopback_capture_(
+          AudioDeviceDescription::IsApplicationLoopbackDevice(device_id)) {
   DCHECK(manager_);
   DCHECK(!device_id_.empty());
   DCHECK(!log_callback_.is_null());
@@ -605,41 +683,52 @@
     return OpenOutcome::kAlreadyOpen;
   }
 
-  // Obtain a reference to the IMMDevice interface of the capturing device with
-  // the specified unique identifier or role which was set at construction.
-  HRESULT hr = SetCaptureDevice();
-  if (FAILED(hr)) {
-    ReportOpenResult(hr);
-    return OpenOutcome::kFailed;
+  HRESULT hr = S_OK;
+  // Application loopback captures do not get audio from an endpoint device, but
+  // rather from an audio interface.
+  if (!is_application_loopback_capture_) {
+    // Obtain a reference to the IMMDevice interface of the capturing device
+    // with the specified unique identifier or role which was set at
+    // construction.
+    hr = SetCaptureDevice();
+    if (FAILED(hr)) {
+      ReportOpenResult(hr);
+      return OpenOutcome::kFailed;
+    }
   }
 
-  // Check if raw audio processing is supported for the selected capture device.
-  raw_processing_supported_ = RawProcessingSupported();
-
-  // Obtain an IAudioClient interface which enables us to create and initialize
-  // an audio stream between an audio application and the audio engine.
-  hr = endpoint_device_->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr,
-                                  &audio_client_);
+  // Activate the AudioClient interface. This is done differently depending on
+  // whether the device is an application device or not. For application
+  // devices, a special activation method must be used to activate the audio
+  // client asynchronously.
+  hr = ActivateAudioClientInterface();
   if (FAILED(hr)) {
     open_result_ = OPEN_RESULT_ACTIVATION_FAILED;
     ReportOpenResult(hr);
     return OpenOutcome::kFailed;
   }
 
+  // Application loopback captures do not get audio from an endpoint device.
+  if (!is_application_loopback_capture_) {
+    // Check if raw audio processing is supported for the selected capture
+    // device.
+    raw_processing_supported_ = RawProcessingSupported();
+  }
+
   // Raw audio capture suppresses processing that down mixes e.g. a microphone
   // array into a supported format and instead exposes the device's native
   // format. Chrome only supports a maximum number of input channels given by
   // media::kMaxConcurrentChannels. Therefore, one additional test is needed
   // before stating that raw audio processing can be supported.
-  // Failure will not prevent opening but the method must succeed to be able to
-  // select raw input capture mode.
+  // Failure will not prevent opening but the method must succeed to be able
+  // to select raw input capture mode.
   WORD audio_engine_channels = 0;
   hr = GetAudioEngineNumChannels(&audio_engine_channels);
 
-  // Attempt to enable communications category and raw capture mode on the audio
-  // stream. Avoid using raw capture if echo cancellation has been requested.
-  // Ignoring return value since the method logs its own error messages
-  // and it should be OK to continue opening the stream even after a failure.
+  // Attempt to enable communications category and raw capture mode on the
+  // audio stream. Ignoring return value since the method logs its own error
+  // messages and it should be OK to continue opening the stream even after a
+  // failure.
   if (raw_processing_supported_ &&
       !AudioDeviceDescription::IsLoopbackDevice(device_id_) && SUCCEEDED(hr)) {
     SetCommunicationsCategoryAndMaybeRawCaptureMode(audio_engine_channels);
@@ -717,6 +806,15 @@
   // using SetAutomaticGainControl().
   StartAgc();
 
+  // Waiting for the first audio sample ready event to be signaled is only
+  // needed for application devices. We need to do it because, due to a Windows
+  // bug, the value returned by GetBufferSize() can not be trusted until we get
+  // the first sample.
+  // https://crbug.com/411452039
+  if (!is_application_loopback_capture_) {
+    CreateFifoIfNeeded();
+  }
+
   // Create and start the thread that will drive the capturing by waiting for
   // capture events.
   DCHECK(!capture_thread_.get());
@@ -826,8 +924,9 @@
   DCHECK_LE(volume, 1.0);
   SendLogMessage("%s({volume=%.2f} [opened=%s])", __func__, volume,
                  opened_ ? "true" : "false");
-  if (!opened_)
+  if (!opened_ || !simple_audio_volume_) {
     return;
+  }
 
   // Set a new master volume level. Valid volume levels are in the range
   // 0.0 to 1.0. Ignore volume-change events.
@@ -848,8 +947,9 @@
 
 double WASAPIAudioInputStream::GetVolume() {
   DCHECK(opened_) << "Open() has not been called successfully";
-  if (!opened_)
+  if (!simple_audio_volume_) {
     return 0.0;
+  }
 
   // Retrieve the current volume level. The value is in the range 0.0 to 1.0.
   float level = 0.0f;
@@ -865,8 +965,9 @@
 bool WASAPIAudioInputStream::IsMuted() {
   DCHECK(opened_) << "Open() has not been called successfully";
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!opened_)
+  if (!simple_audio_volume_) {
     return false;
+  }
 
   // Retrieves the current muting state for the audio session.
   BOOL is_muted = FALSE;
@@ -906,6 +1007,52 @@
   log_callback_.Run(std::move(msg));
 }
 
+// static
+void WASAPIAudioInputStream::
+    OverrideActivateAudioInterfaceAsyncCallbackForTesting(
+        ActivateAudioInterfaceAsyncCallback callback) {
+  GetActivateAudioInterfaceAsyncCallback() = callback;
+}
+
+void WASAPIAudioInputStream::CreateFifoIfNeeded() {
+  if (fifo_) {
+    return;
+  }
+
+  // Retrieve the length of the endpoint buffer shared between the client
+  // and the audio engine. The buffer length determines the maximum amount
+  // of capture data that the audio engine can read from the endpoint buffer
+  // during a single processing pass.
+  uint32_t endpoint_buffer_size_frames = 0;
+  HRESULT hr = audio_client_->GetBufferSize(&endpoint_buffer_size_frames);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  // Allocate a buffer with a size that enables us to take care of cases like:
+  // 1) The recorded buffer size is smaller, or does not match exactly with,
+  //    the selected packet size used in each callback.
+  // 2) The selected buffer size is larger than the recorded buffer size in
+  //    each event.
+  // In the case where no resampling is required, a single buffer should be
+  // enough but in case we get buffers that don't match exactly, we'll go with
+  // two. Same applies if we need to resample and the buffer ratio is perfect.
+  // However if the buffer ratio is imperfect, we will need 3 buffers to safely
+  // be able to buffer up data in cases where a conversion requires two audio
+  // buffers (and we need to be able to write to the third one).
+  size_t capture_buffer_size =
+      std::max(2 * endpoint_buffer_size_frames * frame_size_bytes_,
+               2 * packet_size_frames_ * frame_size_bytes_);
+  int buffers_required = capture_buffer_size / packet_size_bytes_;
+  if (converter_ && imperfect_buffer_size_conversion_)
+    ++buffers_required;
+
+  DCHECK(!fifo_);
+  fifo_ = std::make_unique<AudioBlockFifo>(
+      input_format_.Format.nChannels, packet_size_frames_, buffers_required);
+  DVLOG(1) << "AudioBlockFifo buffer count: " << buffers_required;
+}
+
 void WASAPIAudioInputStream::Run() {
   ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA);
 
@@ -925,29 +1072,6 @@
                << "))";
   }
 
-  // Allocate a buffer with a size that enables us to take care of cases like:
-  // 1) The recorded buffer size is smaller, or does not match exactly with,
-  //    the selected packet size used in each callback.
-  // 2) The selected buffer size is larger than the recorded buffer size in
-  //    each event.
-  // In the case where no resampling is required, a single buffer should be
-  // enough but in case we get buffers that don't match exactly, we'll go with
-  // two. Same applies if we need to resample and the buffer ratio is perfect.
-  // However if the buffer ratio is imperfect, we will need 3 buffers to safely
-  // be able to buffer up data in cases where a conversion requires two audio
-  // buffers (and we need to be able to write to the third one).
-  size_t capture_buffer_size =
-      std::max(2 * endpoint_buffer_size_frames_ * frame_size_bytes_,
-               2 * packet_size_frames_ * frame_size_bytes_);
-  int buffers_required = capture_buffer_size / packet_size_bytes_;
-  if (converter_ && imperfect_buffer_size_conversion_)
-    ++buffers_required;
-
-  DCHECK(!fifo_);
-  fifo_ = std::make_unique<AudioBlockFifo>(
-      input_format_.Format.nChannels, packet_size_frames_, buffers_required);
-  DVLOG(1) << "AudioBlockFifo buffer count: " << buffers_required;
-
   bool recording = true;
   bool error = false;
   HANDLE wait_array[2] = {stop_capture_event_.Get(),
@@ -968,6 +1092,14 @@
         break;
       case WAIT_OBJECT_0 + 1:
         // |audio_samples_ready_event_| has been set.
+        CreateFifoIfNeeded();
+        if (!fifo_) {
+          // An error happened while creating the FIFO.
+          error = true;
+          LOG(ERROR) << "WAIS::" << __func__
+                     << " => (ERROR: failed to create FIFO)";
+          break;
+        }
         PullCaptureDataAndPushToSink();
         break;
       case WAIT_FAILED:
@@ -980,6 +1112,8 @@
   if (recording && error) {
     // TODO(henrika): perhaps it worth improving the cleanup here by e.g.
     // stopping the audio client, joining the thread etc.?
+    // TODO(crbug.com/417505389): We should handle pipeline errors in a more
+    // graceful way instead of using NOTREACHED() here.
     auto saved_last_error = GetLastError();
     NOTREACHED() << "WASAPI capturing failed with error code "
                  << saved_last_error;
@@ -1146,7 +1280,11 @@
     // was monotonic.
     if (!last_capture_time_.is_null()) {
       const auto delta_ts = capture_time - last_capture_time_;
-      DCHECK_GT(device_position, 0u);
+      if (is_application_loopback_capture_) {
+        DCHECK_EQ(device_position, 0u);
+      } else {
+        DCHECK_GT(device_position, 0u);
+      }
       DCHECK_GT(delta_ts, base::TimeDelta::Min());
       if (delta_ts > max_timestamp_diff_) {
         max_timestamp_diff_ = delta_ts;
@@ -1237,6 +1375,7 @@
 }
 
 HRESULT WASAPIAudioInputStream::SetCaptureDevice() {
+  DCHECK(!is_application_loopback_capture_);
   DCHECK_EQ(OPEN_RESULT_OK, open_result_);
   DCHECK(!endpoint_device_.Get());
   SendLogMessage("%s()", __func__);
@@ -1304,7 +1443,56 @@
   return hr;
 }
 
+HRESULT WASAPIAudioInputStream::ActivateAudioClientInterface() {
+  if (!is_application_loopback_capture_) {
+    // Obtain an IAudioClient interface for the endpoint device which enables us
+    // to create and initialize an audio stream between an audio application and
+    // the audio engine.
+    return endpoint_device_->Activate(__uuidof(IAudioClient), CLSCTX_ALL,
+                                      nullptr, &audio_client_);
+  }
+
+  // Detailed information about AUDIOCLIENT_ACTIVATION_PARAMS can be found at:
+  // https://learn.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_activation_params
+  AUDIOCLIENT_ACTIVATION_PARAMS params = {
+      //  Specify the process capture.
+      .ActivationType = AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK,
+      .ProcessLoopbackParams =
+          {
+              .TargetProcessId =
+                  GetApplicationIdFromApplicationLoopbackDeviceId(device_id_),
+              // Specify that this process capture should capture audio coming
+              // from all the processes in the tree in which `TargetProcessId`
+              // is the tree root.
+              .ProcessLoopbackMode =
+                  PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE,
+          },
+  };
+  PROPVARIANT propvariant = {
+      .vt = VT_BLOB,
+      .blob =
+          {
+              .cbSize = sizeof(params),
+              .pBlobData = reinterpret_cast<BYTE*>(&params),
+          },
+  };
+
+  ComPtr<AudioClientActivationHandler> completion_handler =
+      Microsoft::WRL::Make<AudioClientActivationHandler>();
+  ComPtr<IActivateAudioInterfaceAsyncOperation> async_op;
+  HRESULT hr = GetActivateAudioInterfaceAsyncCallback().Run(
+      VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, __uuidof(IAudioClient),
+      &propvariant, completion_handler.Get(), &async_op);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return completion_handler->WaitAndGetAudioClient(
+      &audio_client_, async_activation_timeout_ms_);
+}
+
 bool WASAPIAudioInputStream::RawProcessingSupported() {
+  DCHECK(!is_application_loopback_capture_);
   DCHECK(endpoint_device_.Get());
   // Check if System.Devices.AudioDevice.RawProcessingSupported can be found
   // and queried in the Windows Property System. It corresponds to raw
@@ -1402,6 +1590,15 @@
 
 bool WASAPIAudioInputStream::DesiredFormatIsSupported(HRESULT* hr) {
   SendLogMessage("%s()", __func__);
+
+  // Process loopback mode is a virtual device. Therefore, neither
+  // IAudioClient::GetMixFormat nor IAudioClient::IsFormatSupported are
+  // supported. We are free to pick whichever format we want and can pass it
+  // into the call to IAudioClient::Initialize.
+  if (is_application_loopback_capture_) {
+    return true;
+  }
+
   // An application that uses WASAPI to manage shared-mode streams can rely
   // on the audio engine to perform only limited format conversions. The audio
   // engine can convert between a standard PCM sample size used by the
@@ -1514,8 +1711,12 @@
   SendLogMessage("%s()", __func__);
 
   // Use event-driven mode for regular input devices and for loopback.
-  DWORD flags =
-      AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST;
+  DWORD flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
+  if (!is_application_loopback_capture_) {
+    // Application loopback capture does not support the
+    // AUDCLNT_STREAMFLAGS_NOPERSIST flag.
+    flags |= AUDCLNT_STREAMFLAGS_NOPERSIST;
+  }
   if (AudioDeviceDescription::IsLoopbackDevice(device_id_)) {
     // Create a loopback stream that captures what the system is playing
     // instead of the microphone input.
@@ -1548,22 +1749,6 @@
     return hr;
   }
 
-  // Retrieve the length of the endpoint buffer shared between the client
-  // and the audio engine. The buffer length determines the maximum amount
-  // of capture data that the audio engine can read from the endpoint buffer
-  // during a single processing pass.
-  hr = audio_client_->GetBufferSize(&endpoint_buffer_size_frames_);
-  if (FAILED(hr)) {
-    open_result_ = OPEN_RESULT_GET_BUFFER_SIZE_FAILED;
-    return hr;
-  }
-  const int endpoint_buffer_size_ms =
-      static_cast<double>(endpoint_buffer_size_frames_ * 1000) /
-          input_format_.Format.nSamplesPerSec +
-      0.5;
-  SendLogMessage("%s => (endpoint_buffer_size_frames=%u (%d ms))", __func__,
-                 endpoint_buffer_size_frames_, endpoint_buffer_size_ms);
-
 #ifndef NDEBUG
   // The period between processing passes by the audio engine is fixed for a
   // particular audio endpoint device and represents the smallest processing
@@ -1605,11 +1790,17 @@
     return hr;
   }
 
-  // Obtain a reference to the ISimpleAudioVolume interface which enables
-  // us to control the master volume level of an audio session.
-  hr = audio_client_->GetService(IID_PPV_ARGS(&simple_audio_volume_));
-  if (FAILED(hr))
-    open_result_ = OPEN_RESULT_NO_AUDIO_VOLUME;
+  // WASAPI does not allow the AudioClient to control the process loopback
+  // device volume. The AudioEndpointVolume interface is not available for
+  // process loopback devices.
+  if (!is_application_loopback_capture_) {
+    // Obtain a reference to the ISimpleAudioVolume interface which enables
+    // us to control the master volume level of an audio session.
+    hr = audio_client_->GetService(IID_PPV_ARGS(&simple_audio_volume_));
+    if (FAILED(hr)) {
+      open_result_ = OPEN_RESULT_NO_AUDIO_VOLUME;
+    }
+  }
 
   return hr;
 }
@@ -1649,7 +1840,7 @@
     AudioBus* audio_bus,
     uint32_t frames_delayed,
     const AudioGlitchInfo& glitch_info) {
-  fifo_->Consume()->CopyTo(audio_bus);
+  CHECK_DEREF(fifo_.get()).Consume()->CopyTo(audio_bus);
   return 1.0;
 }
 
diff --git a/media/audio/win/audio_low_latency_input_win.h b/media/audio/win/audio_low_latency_input_win.h
index 87871adac..1452cb9 100644
--- a/media/audio/win/audio_low_latency_input_win.h
+++ b/media/audio/win/audio_low_latency_input_win.h
@@ -112,7 +112,7 @@
     OPEN_RESULT_ACTIVATION_FAILED = 5,
     OPEN_RESULT_FORMAT_NOT_SUPPORTED = 6,
     OPEN_RESULT_AUDIO_CLIENT_INIT_FAILED = 7,
-    OPEN_RESULT_GET_BUFFER_SIZE_FAILED = 8,
+    OPEN_RESULT_GET_BUFFER_SIZE_FAILED = 8,  // Obsolete.
     OPEN_RESULT_LOOPBACK_ACTIVATE_FAILED = 9,
     OPEN_RESULT_LOOPBACK_INIT_FAILED = 10,
     OPEN_RESULT_SET_EVENT_HANDLE = 11,
@@ -122,6 +122,13 @@
     OPEN_RESULT_MAX = OPEN_RESULT_OK_WITH_RESAMPLING
   };
 
+  using ActivateAudioInterfaceAsyncCallback =
+      base::RepeatingCallback<HRESULT(LPCWSTR,
+                                      REFIID,
+                                      PROPVARIANT*,
+                                      IActivateAudioInterfaceCompletionHandler*,
+                                      IActivateAudioInterfaceAsyncOperation**)>;
+
   // The ctor takes all the usual parameters, plus |manager| which is the
   // the audio manager who is creating this object.
   WASAPIAudioInputStream(AudioManagerWin* manager,
@@ -151,9 +158,21 @@
 
   void SendLogMessage(std::string message);
 
+  // Overrides the function pointer used to activate an IAudioClient during
+  // application loopback captures. This is used for testing purposes only to
+  // add a hook to obtain fake implementations of Windows interfaces.
+  static void OverrideActivateAudioInterfaceAsyncCallbackForTesting(
+      ActivateAudioInterfaceAsyncCallback callback);
+
+  void OverrideAsyncActivationTimeoutForTesting(
+      base::TimeDelta async_activation_timeout_ms) {
+    async_activation_timeout_ms_ = async_activation_timeout_ms;
+  }
+
  private:
   class DataDiscontinuityReporter;
   class EchoCancellationConfig;
+  class AudioClientActivationHandler;
 
   PRINTF_FORMAT(2, 3) void SendLogMessage(const char* format, ...);
 
@@ -168,6 +187,12 @@
 
   // The Open() method is divided into these sub methods.
   HRESULT SetCaptureDevice();
+  // Activates the IAudioClient interface with the adequate parameters. If
+  // `device_id_` represents an application device, the function will call
+  // ActivateAudioInterfaceAsync to activate an audio interface for process
+  // loopback capture. If `device_id_` does not represent an application device,
+  // it will activate the selected audio endpoint `endpoint_device_`.
+  HRESULT ActivateAudioClientInterface();
   // Returns whether raw audio processing is supported or not for the selected
   // capture device.
   bool RawProcessingSupported();
@@ -201,6 +226,10 @@
   // Reports glitch stats and resets associated variables.
   void ReportAndResetGlitchStats();
 
+  // Creates the FIFO used to store audio data between the audio engine and the
+  // converter.
+  void CreateFifoIfNeeded();
+
   // Our creator, the audio manager needs to be notified when we close.
   const raw_ptr<AudioManagerWin> manager_;
 
@@ -239,21 +268,18 @@
   bool started_ = false;
   StreamOpenResult open_result_ = OPEN_RESULT_OK;
 
-  // Size in bytes of each audio frame before the converter (4 bytes for 16-bit
-  // stereo PCM). Note that this is the same before and after the fifo.
+  // Size in bytes of each audio frame before the converter (e.g. 4 bytes for
+  // 16-bit stereo PCM). Note that this is the same before and after the FIFO.
   size_t frame_size_bytes_ = 0;
 
-  // Size in audio frames of each audio packet (buffer) after the fifo but
+  // Size in audio frames of each audio packet (buffer) after the FIFO but
   // before the converter.
   size_t packet_size_frames_ = 0;
 
-  // Size in bytes of each audio packet (buffer) after the fifo but before the
+  // Size in bytes of each audio packet (buffer) after the FIFO but before the
   // converter.
   size_t packet_size_bytes_ = 0;
 
-  // Length of the audio endpoint buffer, i.e. the buffer size before the fifo.
-  uint32_t endpoint_buffer_size_frames_ = 0;
-
   // Contains the unique name of the selected endpoint device.
   // Note that AudioDeviceDescription::kDefaultDeviceId represents the default
   // device role and is not a valid ID as such.
@@ -300,7 +326,7 @@
   // indicates that we need to unmute the system audio when stopping capturing.
   bool mute_done_ = false;
 
-  // Used for the captured audio on the callback thread.
+  // Used to store data between the audio engine and the converter.
   std::unique_ptr<AudioBlockFifo> fifo_;
 
   // If the caller requires resampling (should only be in exceptional cases and
@@ -343,6 +369,19 @@
   // Will be set to nullptr during construction if AEC is not supported.
   std::unique_ptr<EchoCancellationConfig> aec_config_;
 
+  // It's is possible to check this using
+  // AudioDeviceDescription::IsApplicationLoopbackDevice. However, we need to
+  // perform this check every time we need to pull data from the audio engine,
+  // which can be expensive. Checking the variable is cheaper than calling the
+  // function.
+  const bool is_application_loopback_capture_;
+
+  // Timeout period for waiting on the OS to activate the audio interface for
+  // application loopback capture.
+  // TODO(crbug.com/40947205): Add UMA stats to track the actual wait time in
+  // the field
+  base::TimeDelta async_activation_timeout_ms_ = base::Seconds(10);
+
   SEQUENCE_CHECKER(sequence_checker_);
 };
 
diff --git a/media/audio/win/audio_low_latency_input_win_unittest.cc b/media/audio/win/audio_low_latency_input_win_unittest.cc
index de632b0..f20f6852 100644
--- a/media/audio/win/audio_low_latency_input_win_unittest.cc
+++ b/media/audio/win/audio_low_latency_input_win_unittest.cc
@@ -29,11 +29,14 @@
 #include "base/win/scoped_com_initializer.h"
 #include "media/audio/audio_device_description.h"
 #include "media/audio/audio_device_info_accessor_for_tests.h"
+#include "media/audio/audio_input_stream_data_interceptor.h"
 #include "media/audio/audio_io.h"
 #include "media/audio/audio_manager.h"
 #include "media/audio/audio_unittest_util.h"
 #include "media/audio/test_audio_thread.h"
 #include "media/audio/win/core_audio_util_win.h"
+#include "media/audio/win/test_support/fake_win_wasapi_environment.h"
+#include "media/audio/win/test_support/wasapi_test_error_code.h"
 #include "media/base/media_switches.h"
 #include "media/base/seekable_buffer.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -52,6 +55,13 @@
 
 namespace {
 
+constexpr char kMockApplicationLoopbackDeviceId[] = "applicationLoopback:12345";
+// When opening a WASAPIAudioInputStream for application loopback capture, it's
+// necessary to wait for the activation to complete. This short timeout is used
+// to avoid long waits in the timeout test cases.
+constexpr base::TimeDelta kShortAsyncActivationTimeoutMs =
+    base::Milliseconds(10);
+
 void LogCallbackDummy(const std::string& /* message */) {}
 
 }  // namespace
@@ -315,7 +325,7 @@
 
   AudioInputStream* operator->() { return stream_; }
 
-  AudioInputStream* get() const { return stream_; }
+  AudioInputStream* get() const { return stream_.get(); }
 
   void Reset(AudioInputStream* new_stream) {
     Close();
@@ -373,7 +383,7 @@
   ~WinAudioInputTest() override { audio_manager_->Shutdown(); }
 
  protected:
-  base::test::SingleThreadTaskEnvironment task_environment_;
+  base::test::TaskEnvironment task_environment_;
   std::unique_ptr<AudioManager> audio_manager_;
 };
 
@@ -728,7 +738,7 @@
   }
 
   void SetUp() override {
-    // Abort early if requirements are mot met.
+    // Abort early if requirements are not met.
     bool prerequisites_met = device_info_accessor_.HasAudioOutputDevices() &&
                              CoreAudioUtil::IsSupported();
     if (!prerequisites_met) {
@@ -800,6 +810,103 @@
   EXPECT_FALSE(sink.error());
 }
 
+class WinAudioApplicationLoopbackTest : public WinAudioInputTest {
+ public:
+  WinAudioApplicationLoopbackTest()
+      : device_info_accessor_(audio_manager_.get()) {
+    // Defer stream creation and parameter fetching to SetUp.
+  }
+
+  void SetUp() override {
+    // Abort early if requirements are not met.
+    bool prerequisites_met = CoreAudioUtil::IsSupported();
+    if (!prerequisites_met) {
+      GTEST_SKIP() << "Missing audio output devices or CoreAudio support";
+    }
+
+    CreateParameters();
+    CreateStream();
+  }
+
+  void CreateParameters() {
+    params_ = device_info_accessor_.GetInputStreamParameters(
+        AudioDeviceDescription::kApplicationLoopbackDeviceId);
+  }
+
+  void CreateStream() {
+    stream_.Reset(audio_manager_->MakeAudioInputStream(
+        params_, kMockApplicationLoopbackDeviceId,
+        base::BindRepeating(&LogCallbackDummy)));
+    EXPECT_THAT(stream_.get(), NotNull());
+  }
+
+  void OverrideAsyncActivationTimeout(base::TimeDelta timeout_ms) {
+    AudioInputStreamDataInterceptor* audio_input_stream_data_interceptor =
+        static_cast<AudioInputStreamDataInterceptor*>(stream_.get());
+    static_cast<WASAPIAudioInputStream*>(
+        audio_input_stream_data_interceptor->GetUnderlyingStreamForTesting())
+        ->OverrideAsyncActivationTimeoutForTesting(timeout_ms);
+  }
+
+ protected:
+  AudioDeviceInfoAccessorForTests device_info_accessor_;
+  AudioParameters params_;
+  ScopedAudioInputStream stream_;
+  FakeWinWASAPIEnvironment fake_wasapi_environment_;
+};
+
+TEST_F(WinAudioApplicationLoopbackTest, OpenStreamSuccess) {
+  ASSERT_THAT(stream_->Open(), Eq(AudioInputStream::OpenOutcome::kSuccess));
+}
+
+TEST_F(WinAudioApplicationLoopbackTest,
+       OpenStreamActivateAudioInterfaceAsyncFailed) {
+  fake_wasapi_environment_.SimulateError(
+      WASAPITestErrorCode::kActivateAudioInterfaceAsyncFailed);
+  EXPECT_EQ(stream_->Open(), AudioInputStream::OpenOutcome::kFailed);
+}
+
+TEST_F(WinAudioApplicationLoopbackTest,
+       OpenInputStreamActivateAudioInterfaceAsyncOperationTimedOut) {
+  fake_wasapi_environment_.SimulateError(
+      WASAPITestErrorCode::kAudioClientActivationTimeout);
+  // Override the default timeout so that this test can run quickly. The default
+  // timeout is 10 seconds.
+  OverrideAsyncActivationTimeout(kShortAsyncActivationTimeoutMs);
+  EXPECT_EQ(stream_->Open(), AudioInputStream::OpenOutcome::kFailed);
+}
+
+TEST_F(WinAudioApplicationLoopbackTest,
+       OpenStreamAudioClientActivationAsyncOperationFailed) {
+  fake_wasapi_environment_.SimulateError(
+      WASAPITestErrorCode::kAudioClientActivationAsyncOperationFailed);
+  // Override the default timeout so that this test can run quickly. The default
+  // timeout is 10 seconds.
+  OverrideAsyncActivationTimeout(kShortAsyncActivationTimeoutMs);
+  EXPECT_EQ(stream_->Open(), AudioInputStream::OpenOutcome::kFailed);
+}
+
+TEST_F(WinAudioApplicationLoopbackTest, OpenStreamAudioClientActivationFailed) {
+  fake_wasapi_environment_.SimulateError(
+      WASAPITestErrorCode::kAudioClientActivationFailed);
+  EXPECT_EQ(stream_->Open(), AudioInputStream::OpenOutcome::kFailed);
+}
+
+TEST_F(WinAudioApplicationLoopbackTest, SuccessfulCapture) {
+  ASSERT_THAT(stream_->Open(), Eq(AudioInputStream::OpenOutcome::kSuccess));
+
+  FakeAudioInputCallback sink;
+  stream_->Start(&sink);
+  ASSERT_FALSE(sink.error());
+  sink.WaitForData();
+  sink.WaitForData();
+  stream_.Close();
+
+  EXPECT_EQ(sink.num_callbacks(), 2);
+  EXPECT_GT(sink.num_received_audio_frames(), 0);
+  EXPECT_FALSE(sink.error());
+}
+
 // This test is intended for manual tests and should only be enabled
 // when it is required to store the captured data on a local file.
 // By default, GTest will print out YOU HAVE 1 DISABLED TEST.
diff --git a/media/audio/win/core_audio_util_win.cc b/media/audio/win/core_audio_util_win.cc
index dce888a..55cc0fe8 100644
--- a/media/audio/win/core_audio_util_win.cc
+++ b/media/audio/win/core_audio_util_win.cc
@@ -396,7 +396,8 @@
   // In loopback mode, a client of WASAPI can capture the audio stream that
   // is being played by a rendering endpoint device.
   // See https://crbug.com/956526 for why we use both a DCHECK and then deal
-  // with the error here and below.
+  // with the error here and below. Also, see comments in CreateDeviceByID() for
+  // more details.
   DCHECK(!(AudioDeviceDescription::IsLoopbackDevice(device_id) &&
            data_flow != eCapture));
   if (AudioDeviceDescription::IsLoopbackDevice(device_id) &&
@@ -449,6 +450,10 @@
 // corresponding audio device.
 ComPtr<IMMDevice> CreateDeviceByID(const std::string& device_id,
                                    bool is_output_device) {
+  // Loopback devices are only supported for capture streams. If a loopback
+  // device is requested for a render stream, the default render device will be
+  // used instead.
+  // See https://crbug.com/956526 for more details.
   if (AudioDeviceDescription::IsLoopbackDevice(device_id)) {
     DCHECK(!is_output_device);
     return CreateDeviceInternal(AudioDeviceDescription::kDefaultDeviceId,
@@ -1050,9 +1055,13 @@
                                                    bool is_output_device,
                                                    AudioParameters* params,
                                                    bool is_offload_stream) {
-  // Loopback audio streams must be input streams.
-  DCHECK(!(AudioDeviceDescription::IsLoopbackDevice(device_id) &&
-           is_output_device));
+  // Loopback capture audio streams must be input streams. If an output device
+  // is requested for a loopback device, the default output device will be used
+  // instead. See https://crbug.com/956526 for more details.
+  // TODO(crbug.com/40947205): figure out which parameters to use for
+  // application loopback capture.
+  DCHECK(!(is_output_device &&
+           (AudioDeviceDescription::IsLoopbackDevice(device_id))));
   if (AudioDeviceDescription::IsLoopbackDevice(device_id) && is_output_device) {
     LOG(WARNING) << "Loopback device must be an input device";
     return E_FAIL;
diff --git a/media/audio/win/core_audio_util_win_unittest.cc b/media/audio/win/core_audio_util_win_unittest.cc
index 74b3c3b71..19b7adf5 100644
--- a/media/audio/win/core_audio_util_win_unittest.cc
+++ b/media/audio/win/core_audio_util_win_unittest.cc
@@ -398,15 +398,18 @@
   for (size_t i = 0; i < std::size(data); ++i) {
     AudioParameters params;
     const bool is_output_device = (data[i] == eRender);
-    EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
-        AudioDeviceDescription::kDefaultDeviceId, is_output_device, &params)));
+    EXPECT_HRESULT_SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
+        AudioDeviceDescription::kDefaultDeviceId, is_output_device, &params));
     EXPECT_TRUE(params.IsValid());
     if (!is_output_device) {
       // Loopack devices are supported for input streams.
-      EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
+      EXPECT_HRESULT_SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
           AudioDeviceDescription::kLoopbackInputDeviceId, is_output_device,
-          &params)));
+          &params));
       EXPECT_TRUE(params.IsValid());
+      EXPECT_HRESULT_SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
+          AudioDeviceDescription::kApplicationLoopbackDeviceId,
+          is_output_device, &params));
       {
         base::test::ScopedFeatureList feature_list;
         base::HistogramTester histogram_tester;
@@ -415,9 +418,9 @@
         // version and device hardware. This code is behind a flag currently.
         feature_list.InitAndEnableFeature(
             media::kEnforceSystemEchoCancellation);
-        EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
+        EXPECT_HRESULT_SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
             AudioDeviceDescription::kDefaultDeviceId, is_output_device, &params,
-            false /*is_offload_stream*/)));
+            false /*is_offload_stream*/));
         EXPECT_TRUE(params.IsValid());
         // If GetPreferredAudioParameters() runs we know that at least one
         // sample is created since, even if the method fails or if the device
@@ -438,9 +441,9 @@
         feature_list.InitAndDisableFeature(
             media::kEnforceSystemEchoCancellation);
         base::HistogramTester histogram_tester;
-        EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
+        EXPECT_HRESULT_SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
             AudioDeviceDescription::kDefaultDeviceId, is_output_device, &params,
-            false /*is_offload_stream*/)));
+            false /*is_offload_stream*/));
         EXPECT_TRUE(params.IsValid());
         histogram_tester.ExpectTotalCount(
             "Media.Audio.Capture.Win.VoiceProcessingEffects", 0);
diff --git a/media/audio/win/test_support/fake_iactivate_audio_interface_async_operation.cc b/media/audio/win/test_support/fake_iactivate_audio_interface_async_operation.cc
new file mode 100644
index 0000000..9cd4ebd
--- /dev/null
+++ b/media/audio/win/test_support/fake_iactivate_audio_interface_async_operation.cc
@@ -0,0 +1,40 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/win/test_support/fake_iactivate_audio_interface_async_operation.h"
+
+#include <mmdeviceapi.h>
+#include <windows.h>
+
+#include <audioclient.h>
+#include <wrl.h>
+
+#include "media/audio/win/test_support/fake_iaudio_client.h"
+#include "media/audio/win/test_support/fake_win_wasapi_environment.h"
+
+namespace media {
+
+IFACEMETHODIMP FakeIActivateAudioInterfaceAsyncOperation::GetActivateResult(
+    HRESULT* activateResult,
+    IUnknown** activatedInterface) {
+  if (FakeWinWASAPIEnvironment::GetError() ==
+      WASAPITestErrorCode::kAudioClientActivationAsyncOperationFailed) {
+    return E_ILLEGAL_METHOD_CALL;
+  }
+
+  if (FakeWinWASAPIEnvironment::GetError() ==
+      WASAPITestErrorCode::kAudioClientActivationFailed) {
+    *activateResult = E_OUTOFMEMORY;
+    return S_OK;
+  }
+
+  Microsoft::WRL::ComPtr<IAudioClient> audio_client =
+      Microsoft::WRL::Make<FakeIAudioClient>(
+          FakeIAudioClient::ClientType::kApplicationLoopbackDevice);
+  *activateResult = S_OK;
+  *activatedInterface = audio_client.Detach();
+  return S_OK;
+}
+
+}  // namespace media
diff --git a/media/audio/win/test_support/fake_iactivate_audio_interface_async_operation.h b/media/audio/win/test_support/fake_iactivate_audio_interface_async_operation.h
new file mode 100644
index 0000000..5ae4caba
--- /dev/null
+++ b/media/audio/win/test_support/fake_iactivate_audio_interface_async_operation.h
@@ -0,0 +1,36 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_WIN_TEST_SUPPORT_FAKE_IACTIVATE_AUDIO_INTERFACE_ASYNC_OPERATION_H_
+#define MEDIA_AUDIO_WIN_TEST_SUPPORT_FAKE_IACTIVATE_AUDIO_INTERFACE_ASYNC_OPERATION_H_
+
+#include <mmdeviceapi.h>
+#include <windows.h>
+
+#include <wrl.h>
+
+namespace media {
+
+// FakeIActivateAudioInterfaceAsyncOperation is a mock implementation of the
+// IActivateAudioInterfaceAsyncOperation interface. It is used for testing
+// purposes and simulates the behavior of an audio interface activation
+// operation without requiring actual audio hardware or a real WASAPI
+// environment.
+class FakeIActivateAudioInterfaceAsyncOperation
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+          Microsoft::WRL::FtmBase,
+          IActivateAudioInterfaceAsyncOperation> {
+ public:
+  FakeIActivateAudioInterfaceAsyncOperation() = default;
+  ~FakeIActivateAudioInterfaceAsyncOperation() override = default;
+
+  // IActivateAudioInterfaceAsyncOperation methods
+  IFACEMETHODIMP(GetActivateResult)(HRESULT* activateResult,
+                                    IUnknown** activatedInterface) override;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_WIN_TEST_SUPPORT_FAKE_IACTIVATE_AUDIO_INTERFACE_ASYNC_OPERATION_H_
diff --git a/media/audio/win/test_support/fake_iaudio_capture_client.cc b/media/audio/win/test_support/fake_iaudio_capture_client.cc
new file mode 100644
index 0000000..5b95349
--- /dev/null
+++ b/media/audio/win/test_support/fake_iaudio_capture_client.cc
@@ -0,0 +1,61 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/win/test_support/fake_iaudio_capture_client.h"
+
+#include <wrl.h>
+
+namespace media {
+
+FakeIAudioCaptureClient::FakeIAudioCaptureClient(
+    FakeIAudioClient::ClientType client_type,
+    UINT32 buffer_size)
+    : client_type_(client_type),
+      buffer_data_(buffer_size, /*initial_value=*/1),
+      next_packet_size_(buffer_size) {}
+
+FakeIAudioCaptureClient::~FakeIAudioCaptureClient() = default;
+
+IFACEMETHODIMP FakeIAudioCaptureClient::GetBuffer(BYTE** data,
+                                                  UINT32* num_frames_available,
+                                                  DWORD* flags,
+                                                  UINT64* device_position,
+                                                  UINT64* qpc_position) {
+  *data = buffer_data_.data();
+  *num_frames_available = static_cast<UINT32>(buffer_data_.size());
+  *flags = 0;
+  *device_position = device_position_;
+  *qpc_position = qpc_position_;
+
+  // Simulate a device position increment for the next call.
+  if (client_type_ !=
+      FakeIAudioClient::ClientType::kApplicationLoopbackDevice) {
+    // Application loopback uses a virtual device, which do not have a device
+    // position and should always return 0.
+    device_position_ += *num_frames_available;
+  }
+  qpc_position_ += *num_frames_available;
+
+  // After the buffer content has been read, make the next call to
+  // `GetNextPacketSize` return 0. This simulates the behavior of the audio
+  // engine when there is no more data available, which will make the
+  // `WASAPIAudioInputStream` to break the loop in
+  // `WASAPIAudioInputStream::PullCaptureDataAndPushToSink`.
+  next_packet_size_ = 0;
+  return S_OK;
+}
+
+IFACEMETHODIMP FakeIAudioCaptureClient::ReleaseBuffer(UINT32 num_frames_read) {
+  return S_OK;
+}
+
+IFACEMETHODIMP FakeIAudioCaptureClient::GetNextPacketSize(UINT32* packet_size) {
+  *packet_size = static_cast<UINT32>(next_packet_size_);
+  if (next_packet_size_ == 0) {
+    next_packet_size_ = static_cast<int>(buffer_data_.size());
+  }
+  return S_OK;
+}
+
+}  // namespace media
diff --git a/media/audio/win/test_support/fake_iaudio_capture_client.h b/media/audio/win/test_support/fake_iaudio_capture_client.h
new file mode 100644
index 0000000..8c5f84b6
--- /dev/null
+++ b/media/audio/win/test_support/fake_iaudio_capture_client.h
@@ -0,0 +1,54 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_WIN_TEST_SUPPORT_FAKE_IAUDIO_CAPTURE_CLIENT_H_
+#define MEDIA_AUDIO_WIN_TEST_SUPPORT_FAKE_IAUDIO_CAPTURE_CLIENT_H_
+
+#include <audioclient.h>
+#include <wrl.h>
+
+#include <vector>
+
+#include "base/win/windows_types.h"
+#include "media/audio/win/test_support/fake_iaudio_client.h"
+
+namespace media {
+
+// FakeIAudioCaptureClient is a mock implementation of the IAudioCaptureClient
+// interface. It is used for testing purposes and simulates the behavior of an
+// IAudioCaptureClient without requiring actual audio hardware or a real
+// WASAPI environment.
+class FakeIAudioCaptureClient
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+          Microsoft::WRL::FtmBase,
+          IAudioCaptureClient> {
+ public:
+  FakeIAudioCaptureClient(FakeIAudioClient::ClientType client_type,
+                          UINT32 buffer_size);
+  FakeIAudioCaptureClient(const FakeIAudioCaptureClient&) = delete;
+  FakeIAudioCaptureClient& operator=(const FakeIAudioCaptureClient&) = delete;
+  ~FakeIAudioCaptureClient() override;
+
+  // IAudioCaptureClient methods
+  IFACEMETHODIMP(GetBuffer)(BYTE** data,
+                            UINT32* num_frames_available,
+                            DWORD* flags,
+                            UINT64* device_position,
+                            UINT64* qpc_position) override;
+  IFACEMETHODIMP(ReleaseBuffer)(UINT32 num_frames_read) override;
+  IFACEMETHODIMP(GetNextPacketSize)(UINT32* packet_size) override;
+
+ private:
+  const FakeIAudioClient::ClientType client_type_;
+  // Simulated buffer data
+  std::vector<BYTE> buffer_data_;
+  UINT64 device_position_ = 0;
+  UINT64 qpc_position_ = 0;
+  int next_packet_size_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_WIN_TEST_SUPPORT_FAKE_IAUDIO_CAPTURE_CLIENT_H_
diff --git a/media/audio/win/test_support/fake_iaudio_client.cc b/media/audio/win/test_support/fake_iaudio_client.cc
new file mode 100644
index 0000000..cbc2eb7c
--- /dev/null
+++ b/media/audio/win/test_support/fake_iaudio_client.cc
@@ -0,0 +1,136 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/win/test_support/fake_iaudio_client.h"
+
+#include <mmdeviceapi.h>
+#include <windows.h>
+
+#include <wrl.h>
+
+#include "base/functional/bind.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/task/thread_pool.h"
+#include "base/time/time.h"
+#include "media/audio/win/test_support/fake_iaudio_capture_client.h"
+
+namespace {
+
+constexpr UINT32 kBufferSize = 256;
+constexpr REFERENCE_TIME kSamplingPeriodMs = 1;
+constexpr REFERENCE_TIME kStreamLatencyMs = 1;
+
+}  // namespace
+
+namespace media {
+
+FakeIAudioClient::FakeIAudioClient(ClientType client_type)
+    : client_type_(client_type) {}
+
+FakeIAudioClient::~FakeIAudioClient() = default;
+
+IFACEMETHODIMP FakeIAudioClient::GetBufferSize(UINT32* buffer_size) {
+  *buffer_size = kBufferSize;
+  return S_OK;
+}
+
+IFACEMETHODIMP FakeIAudioClient::GetCurrentPadding(UINT32* padding) {
+  return E_NOTIMPL;
+}
+
+IFACEMETHODIMP FakeIAudioClient::GetDevicePeriod(
+    REFERENCE_TIME* default_device_period,
+    REFERENCE_TIME* minimum_device_period) {
+  // Convert to 100ns units.
+  *default_device_period = kSamplingPeriodMs * 10000;
+  return S_OK;
+}
+
+IFACEMETHODIMP FakeIAudioClient::GetMixFormat(WAVEFORMATEX** device_format) {
+  return E_NOTIMPL;
+}
+
+IFACEMETHODIMP FakeIAudioClient::GetService(REFIID riid, void** service) {
+  if (riid == __uuidof(IAudioCaptureClient)) {
+    audio_capture_client_ = Microsoft::WRL::Make<FakeIAudioCaptureClient>(
+        client_type_, kBufferSize);
+    audio_capture_client_.CopyTo(
+        reinterpret_cast<IAudioCaptureClient**>(service));
+    return S_OK;
+  }
+
+  return E_NOTIMPL;
+}
+
+IFACEMETHODIMP FakeIAudioClient::GetStreamLatency(REFERENCE_TIME* latency) {
+  // Convert to 100ns units.
+  *latency = kStreamLatencyMs * 10000;
+  return S_OK;
+}
+
+IFACEMETHODIMP FakeIAudioClient::Initialize(AUDCLNT_SHAREMODE share_mode,
+                                            DWORD stream_flags,
+                                            REFERENCE_TIME buffer_duration,
+                                            REFERENCE_TIME periodicity,
+                                            const WAVEFORMATEX* format,
+                                            LPCGUID audio_session_guid) {
+  return S_OK;
+}
+
+IFACEMETHODIMP FakeIAudioClient::IsFormatSupported(
+    AUDCLNT_SHAREMODE share_mode,
+    const WAVEFORMATEX* format,
+    WAVEFORMATEX** closest_match) {
+  return E_NOTIMPL;
+}
+
+IFACEMETHODIMP FakeIAudioClient::Reset() {
+  return E_NOTIMPL;
+}
+
+IFACEMETHODIMP FakeIAudioClient::SetEventHandle(HANDLE event_handle) {
+  buffer_ready_event_handle_ = event_handle;
+  SetEvent(buffer_ready_event_handle_);
+  return S_OK;
+}
+
+IFACEMETHODIMP FakeIAudioClient::Start() {
+  StartDataStreaming();
+  return S_OK;
+}
+
+IFACEMETHODIMP FakeIAudioClient::Stop() {
+  should_stop_streaming_ = true;
+  return S_OK;
+}
+
+void FakeIAudioClient::StartDataStreaming() {
+  base::ThreadPool::PostTask(
+      FROM_HERE,
+      base::BindOnce(&FakeIAudioClient::StreamData, base::Unretained(this)));
+}
+
+void FakeIAudioClient::StreamData() {
+  // Realistically, the AudioCaptureClient would need to signal the
+  // `FakeIAudioClient` that new data is available. However, for testing
+  // purposes, the `FakeIAudioCaptureClient` data buffer's contents never
+  // change. Therefore, the `FakeIAudioClient` does not need to receive any type
+  // of notification to signal the consumer that it can access the next batch of
+  // data.
+  if (buffer_ready_event_handle_) {
+    SetEvent(buffer_ready_event_handle_);
+  }
+
+  if (should_stop_streaming_) {
+    return;
+  }
+
+  // Simulate new data coming in every `kSamplingPeriodMs` milliseconds.
+  base::ThreadPool::PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&FakeIAudioClient::StreamData, base::Unretained(this)),
+      base::Milliseconds(kSamplingPeriodMs));
+}
+
+}  // namespace media
diff --git a/media/audio/win/test_support/fake_iaudio_client.h b/media/audio/win/test_support/fake_iaudio_client.h
new file mode 100644
index 0000000..3332b5f
--- /dev/null
+++ b/media/audio/win/test_support/fake_iaudio_client.h
@@ -0,0 +1,69 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_WIN_TEST_SUPPORT_FAKE_IAUDIO_CLIENT_H_
+#define MEDIA_AUDIO_WIN_TEST_SUPPORT_FAKE_IAUDIO_CLIENT_H_
+
+#include <mmdeviceapi.h>
+#include <windows.h>
+
+#include <audioclient.h>
+#include <wrl.h>
+
+#include "base/task/sequenced_task_runner.h"
+
+namespace media {
+
+// FakeIAudioClient is a mock implementation of the IAudioClient interface.
+// It is used for testing purposes and simulates the behavior of an IAudioClient
+// without requiring actual audio hardware or a real WASAPI environment.
+class FakeIAudioClient
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+          Microsoft::WRL::FtmBase,
+          IAudioClient> {
+ public:
+  enum class ClientType { kDefaultDevice, kApplicationLoopbackDevice };
+
+  FakeIAudioClient(ClientType client_type);
+  FakeIAudioClient(const FakeIAudioClient&) = delete;
+  FakeIAudioClient& operator=(const FakeIAudioClient&) = delete;
+  ~FakeIAudioClient() override;
+
+  // IAudioClient methods
+  IFACEMETHODIMP(GetBufferSize)(UINT32* buffer_size) override;
+  IFACEMETHODIMP(GetCurrentPadding)(UINT32* padding) override;
+  IFACEMETHODIMP(GetDevicePeriod)(
+      REFERENCE_TIME* default_device_period,
+      REFERENCE_TIME* minimum_device_period) override;
+  IFACEMETHODIMP(GetMixFormat)(WAVEFORMATEX** device_format) override;
+  IFACEMETHODIMP(GetService)(REFIID riid, void** service) override;
+  IFACEMETHODIMP(GetStreamLatency)(REFERENCE_TIME* phnsLatency) override;
+  IFACEMETHODIMP(Initialize)(AUDCLNT_SHAREMODE share_mode,
+                             DWORD stream_flags,
+                             REFERENCE_TIME buffer_duration,
+                             REFERENCE_TIME periodicity,
+                             const WAVEFORMATEX* format,
+                             LPCGUID audio_session_guid) override;
+  IFACEMETHODIMP(IsFormatSupported)(AUDCLNT_SHAREMODE share_mode,
+                                    const WAVEFORMATEX* format,
+                                    WAVEFORMATEX** closest_match) override;
+  IFACEMETHODIMP(Reset)() override;
+  IFACEMETHODIMP(SetEventHandle)(HANDLE event_handle) override;
+  IFACEMETHODIMP(Start)() override;
+  IFACEMETHODIMP(Stop)() override;
+
+ private:
+  void StartDataStreaming();
+  void StreamData();
+
+  const ClientType client_type_;
+  HANDLE buffer_ready_event_handle_ = nullptr;
+  Microsoft::WRL::ComPtr<IAudioCaptureClient> audio_capture_client_ = nullptr;
+  bool should_stop_streaming_ = false;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_WIN_TEST_SUPPORT_FAKE_IAUDIO_CLIENT_H_
diff --git a/media/audio/win/test_support/fake_win_wasapi_environment.cc b/media/audio/win/test_support/fake_win_wasapi_environment.cc
new file mode 100644
index 0000000..cad78ba9
--- /dev/null
+++ b/media/audio/win/test_support/fake_win_wasapi_environment.cc
@@ -0,0 +1,89 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/win/test_support/fake_win_wasapi_environment.h"
+
+#include <mmdeviceapi.h>
+#include <windows.h>
+
+#include <wrl.h>
+
+#include "base/functional/bind.h"
+#include "base/task/thread_pool.h"
+#include "base/win/windows_types.h"
+#include "media/audio/win/audio_low_latency_input_win.h"
+#include "media/audio/win/test_support/fake_iactivate_audio_interface_async_operation.h"
+#include "media/audio/win/test_support/fake_iaudio_client.h"
+
+namespace media {
+
+namespace {
+
+void ActivateAudioInterface(
+    IActivateAudioInterfaceCompletionHandler* completionHandler,
+    IActivateAudioInterfaceAsyncOperation* activationOperation) {
+  activationOperation =
+      Microsoft::WRL::Make<FakeIActivateAudioInterfaceAsyncOperation>()
+          .Detach();
+  completionHandler->ActivateCompleted(activationOperation);
+}
+
+}  // namespace
+
+// static
+WASAPITestErrorCode FakeWinWASAPIEnvironment::error_ = WASAPITestErrorCode::kOk;
+
+FakeWinWASAPIEnvironment::FakeWinWASAPIEnvironment() {
+  // Set the error code to the default success value.
+  error_ = WASAPITestErrorCode::kOk;
+
+  // Override the callback for ActivateAudioInterfaceAsync to use our fake
+  // implementation.
+  WASAPIAudioInputStream::OverrideActivateAudioInterfaceAsyncCallbackForTesting(
+      base::BindRepeating(&ActivateAudioInterfaceAsync));
+}
+
+FakeWinWASAPIEnvironment::~FakeWinWASAPIEnvironment() {
+  // Reset the error code to the default success value.
+  error_ = WASAPITestErrorCode::kOk;
+
+  // Restore the original callback for ActivateAudioInterfaceAsync.
+  WASAPIAudioInputStream::OverrideActivateAudioInterfaceAsyncCallbackForTesting(
+      base::BindRepeating(&ActivateAudioInterfaceAsync));
+}
+
+// static
+HRESULT FakeWinWASAPIEnvironment::ActivateAudioInterfaceAsync(
+    LPCWSTR deviceInterfacePath,
+    REFIID riid,
+    PROPVARIANT* activationParams,
+    IActivateAudioInterfaceCompletionHandler* completionHandler,
+    IActivateAudioInterfaceAsyncOperation** activationOperation) {
+  if (error_ == WASAPITestErrorCode::kActivateAudioInterfaceAsyncFailed) {
+    return E_ILLEGAL_METHOD_CALL;
+  }
+
+  // COM interfaces are refcounted, so we need to wrap the pointers in ComPtr to
+  // ensure that they are properly managed by the callback.
+  Microsoft::WRL::ComPtr<IActivateAudioInterfaceCompletionHandler>
+      completionHandlerPtr(completionHandler);
+  Microsoft::WRL::ComPtr<IActivateAudioInterfaceAsyncOperation>
+      activationOperationPtr(*activationOperation);
+  auto activate_audio_interface_callback = base::BindOnce(
+      &ActivateAudioInterface, completionHandlerPtr, activationOperationPtr);
+
+  if (FakeWinWASAPIEnvironment::GetError() ==
+      WASAPITestErrorCode::kAudioClientActivationTimeout) {
+    base::ThreadPool::PostDelayedTask(
+        FROM_HERE, std::move(activate_audio_interface_callback),
+        base::Milliseconds(15000));
+    return S_OK;
+  }
+
+  base::ThreadPool::PostTask(FROM_HERE,
+                             std::move(activate_audio_interface_callback));
+  return S_OK;
+}
+
+}  // namespace media
diff --git a/media/audio/win/test_support/fake_win_wasapi_environment.h b/media/audio/win/test_support/fake_win_wasapi_environment.h
new file mode 100644
index 0000000..fc49be8
--- /dev/null
+++ b/media/audio/win/test_support/fake_win_wasapi_environment.h
@@ -0,0 +1,43 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_WIN_TEST_SUPPORT_FAKE_WIN_WASAPI_ENVIRONMENT_H_
+#define MEDIA_AUDIO_WIN_TEST_SUPPORT_FAKE_WIN_WASAPI_ENVIRONMENT_H_
+
+#include <mmdeviceapi.h>
+#include <windows.h>
+
+#include "base/win/windows_types.h"
+#include "media/audio/win/test_support/wasapi_test_error_code.h"
+
+namespace media {
+
+// This class is meant to mock the behavior of the WASAPI environment for
+// testing. It does that by simulating platform errors and providing fake
+// implementations of Windows APIs that return fake Windows interfaces.
+class FakeWinWASAPIEnvironment {
+ public:
+  FakeWinWASAPIEnvironment();
+  FakeWinWASAPIEnvironment(const FakeWinWASAPIEnvironment&) = delete;
+  FakeWinWASAPIEnvironment& operator=(const FakeWinWASAPIEnvironment&) = delete;
+  ~FakeWinWASAPIEnvironment();
+
+  static HRESULT ActivateAudioInterfaceAsync(
+      LPCWSTR deviceInterfacePath,
+      REFIID riid,
+      PROPVARIANT* activationParams,
+      IActivateAudioInterfaceCompletionHandler* completionHandler,
+      IActivateAudioInterfaceAsyncOperation** activationOperation);
+
+  void SimulateError(WASAPITestErrorCode error) { error_ = error; }
+
+  static WASAPITestErrorCode GetError() { return error_; }
+
+ private:
+  static WASAPITestErrorCode error_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_WIN_TEST_SUPPORT_FAKE_WIN_WASAPI_ENVIRONMENT_H_
diff --git a/media/audio/win/test_support/wasapi_test_error_code.h b/media/audio/win/test_support/wasapi_test_error_code.h
new file mode 100644
index 0000000..2bd8402
--- /dev/null
+++ b/media/audio/win/test_support/wasapi_test_error_code.h
@@ -0,0 +1,21 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_WIN_TEST_SUPPORT_WASAPI_TEST_ERROR_CODE_H_
+#define MEDIA_AUDIO_WIN_TEST_SUPPORT_WASAPI_TEST_ERROR_CODE_H_
+
+namespace media {
+
+enum class WASAPITestErrorCode {
+  kOk,
+  kActivateAudioInterfaceAsyncFailed,
+  kAudioClientActivationTimeout,
+  kAudioClientActivationAsyncOperationFailed,
+  kAudioClientActivationFailed,
+  kAudioClientGetBufferSizeFailed,
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_WIN_TEST_SUPPORT_WASAPI_TEST_ERROR_CODE_H_
diff --git a/media/gpu/mac/vt_video_encode_accelerator_mac.mm b/media/gpu/mac/vt_video_encode_accelerator_mac.mm
index 215c4bb..fa828d8 100644
--- a/media/gpu/mac/vt_video_encode_accelerator_mac.mm
+++ b/media/gpu/mac/vt_video_encode_accelerator_mac.mm
@@ -1073,19 +1073,21 @@
     return false;
   }
   // This property may suddenly become unsupported when a second compression
-  // session is created if the codec is H.265 and CPU arch is x64, so we can
-  // always check if the property is supported before setting it.
-  if (session_property_setter.IsSupported(
-          kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration)) {
-    if (!session_property_setter.Set(
-            kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, 240)) {
-      NotifyErrorStatus(
-          {EncoderStatus::Codes::kEncoderUnsupportedConfig,
-           "Failed to set max keyframe interval duration to 240 seconds"});
-      return false;
+  // session is created if the codec is H.265 and CPU arch is x64. Skip setting
+  // this property for H.265.
+  if (codec != VideoCodec::kHEVC) {
+    if (session_property_setter.IsSupported(
+            kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration)) {
+      if (!session_property_setter.Set(
+              kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, 240)) {
+        NotifyErrorStatus(
+            {EncoderStatus::Codes::kEncoderUnsupportedConfig,
+             "Failed to set max keyframe interval duration to 240 seconds"});
+        return false;
+      }
+    } else {
+      DLOG(WARNING) << "MaxKeyFrameIntervalDuration is not supported";
     }
-  } else {
-    DLOG(WARNING) << "MaxKeyFrameIntervalDuration is not supported";
   }
 
   if (session_property_setter.IsSupported(
diff --git a/net/base/features.cc b/net/base/features.cc
index 7041e67..52f3f2a5 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -305,12 +305,6 @@
     &kAvoidEntryCreationForNoStore, "AvoidEntryCreationForNoStoreCacheSize",
     1000};
 
-// Prefetch to follow normal semantics instead of 5-minute rule
-// https://crbug.com/1345207
-BASE_FEATURE(kPrefetchFollowsNormalCacheSemantics,
-             "PrefetchFollowsNormalCacheSemantics",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // A flag for new Kerberos feature, that suggests new UI
 // when Kerberos authentication in browser fails on ChromeOS.
 // b/260522530
diff --git a/net/base/features.h b/net/base/features.h
index 91bab060..47463c7 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -332,10 +332,6 @@
 NET_EXPORT extern const base::FeatureParam<int>
     kAvoidEntryCreationForNoStoreCacheSize;
 
-// Prefetch to follow normal semantics instead of 5-minute rule
-// https://crbug.com/1345207
-NET_EXPORT BASE_DECLARE_FEATURE(kPrefetchFollowsNormalCacheSemantics);
-
 // A flag for new Kerberos feature, that suggests new UI
 // when Kerberos authentication in browser fails on ChromeOS.
 // b/260522530
diff --git a/net/disk_cache/simple/simple_index.h b/net/disk_cache/simple/simple_index.h
index 98ddcf8..1f9c52fe 100644
--- a/net/disk_cache/simple/simple_index.h
+++ b/net/disk_cache/simple/simple_index.h
@@ -130,7 +130,7 @@
     INITIALIZE_METHOD_MAX = 3,
   };
   // Used in histograms. Please only add entries at the end.
-  enum IndexWriteToDiskReason {
+  enum IndexWriteToDiskReason : uint32_t {
     INDEX_WRITE_REASON_SHUTDOWN = 0,
     INDEX_WRITE_REASON_STARTUP_MERGE = 1,
     INDEX_WRITE_REASON_IDLE = 2,
diff --git a/net/filter/filter_source_stream_test_util.cc b/net/filter/filter_source_stream_test_util.cc
index 982cbda..af89153c 100644
--- a/net/filter/filter_source_stream_test_util.cc
+++ b/net/filter/filter_source_stream_test_util.cc
@@ -9,6 +9,8 @@
 
 #include "net/filter/filter_source_stream_test_util.h"
 
+#include <stdint.h>
+
 #include <cstring>
 
 #include "base/check_op.h"
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index fdc71064..04121d8 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -2938,24 +2938,6 @@
                           response_.request_time, response_.response_time,
                           cache_->clock_->Now());
 
-  base::TimeDelta response_time_in_cache =
-      cache_->clock_->Now() - response_.response_time;
-
-  if (!base::FeatureList::IsEnabled(
-          features::kPrefetchFollowsNormalCacheSemantics) &&
-      !(effective_load_flags_ & LOAD_PREFETCH) &&
-      (response_time_in_cache >= base::TimeDelta())) {
-    bool reused_within_time_window =
-        response_time_in_cache < base::Minutes(kPrefetchReuseMins);
-    bool first_reuse = response_.unused_since_prefetch;
-
-    // The first use of a resource after prefetch within a short window skips
-    // validation.
-    if (first_reuse && reused_within_time_window) {
-      return VALIDATION_NONE;
-    }
-  }
-
   if (validate_flag) {
     return VALIDATION_SYNCHRONOUS;
   }
diff --git a/net/http/http_cache_util.h b/net/http/http_cache_util.h
index 6641209..e5663f0 100644
--- a/net/http/http_cache_util.h
+++ b/net/http/http_cache_util.h
@@ -5,6 +5,7 @@
 #ifndef NET_HTTP_HTTP_CACHE_UTIL_H_
 #define NET_HTTP_HTTP_CACHE_UTIL_H_
 
+#include <array>
 #include <optional>
 #include <string_view>
 
diff --git a/net/http/http_stream_pool.cc b/net/http/http_stream_pool.cc
index 9f03b883..d1d38eaa 100644
--- a/net/http/http_stream_pool.cc
+++ b/net/http/http_stream_pool.cc
@@ -203,24 +203,22 @@
 int HttpStreamPool::Preconnect(HttpStreamPoolRequestInfo request_info,
                                size_t num_streams,
                                CompletionOnceCallback callback) {
-  std::vector<SSLConfig::CertAndStatus> allowed_bad_certs;
   auto controller = std::make_unique<JobController>(
       this, std::move(request_info), /*priority=*/RequestPriority::IDLE,
-      std::move(allowed_bad_certs),
+      /*allowed_bad_certs=*/std::vector<SSLConfig::CertAndStatus>(),
       /*enable_ip_based_pooling=*/true,
       /*enable_alternative_services=*/true);
   JobController* controller_raw_ptr = controller.get();
+  // Put `controller` into `job_controllers_` before calling Preconnect() to
+  // make sure `job_controllers_` always contains `controller` when
+  // OnJobControllerComplete() is called.
+  job_controllers_.emplace(std::move(controller));
   CHECK_EQ(controller_raw_ptr->respect_limits(), RespectLimits::kRespect);
-  // SAFETY: Using base::Unretained() is safe because `this` will own
-  // `controller` when Preconnect() return ERR_IO_PENDING.
-  int rv = controller_raw_ptr->Preconnect(
+  // SAFETY: Using base::Unretained() is safe because `this` owns `controller`.
+  return controller_raw_ptr->Preconnect(
       num_streams, base::BindOnce(&HttpStreamPool::OnPreconnectComplete,
                                   base::Unretained(this), controller_raw_ptr,
                                   std::move(callback)));
-  if (rv == ERR_IO_PENDING) {
-    job_controllers_.emplace(std::move(controller));
-  }
-  return rv;
 }
 
 bool HttpStreamPool::EnsureTotalActiveStreamCountBelowLimit() const {
diff --git a/net/http/http_stream_pool_attempt_manager.cc b/net/http/http_stream_pool_attempt_manager.cc
index d2fdb8c..68ac97d 100644
--- a/net/http/http_stream_pool_attempt_manager.cc
+++ b/net/http/http_stream_pool_attempt_manager.cc
@@ -304,7 +304,7 @@
 
   // If `job` is resumed, there could be enough streams at this point.
   if (group_->ActiveStreamSocketCount() >= job->num_streams()) {
-    NotifyJobOfPreconnectCompleteLater(job, OK);
+    NotifyJobOfPreconnectComplete(job, OK);
     return;
   }
 
@@ -1503,7 +1503,7 @@
   while (!preconnect_jobs_.empty()) {
     raw_ptr<Job> job =
         preconnect_jobs_.extract(preconnect_jobs_.begin()).value();
-    NotifyJobOfPreconnectCompleteLater(job, rv);
+    NotifyJobOfPreconnectComplete(std::move(job), rv);
   }
   // TODO(crbug.com/396998469): Do we still need this? Remove if this is not
   // needed.
@@ -1524,7 +1524,7 @@
     auto it = preconnect_jobs_.find(completed_job);
     CHECK(it != preconnect_jobs_.end());
     raw_ptr<Job> job = preconnect_jobs_.extract(it).value();
-    NotifyJobOfPreconnectCompleteLater(job, rv);
+    NotifyJobOfPreconnectComplete(std::move(job), rv);
   }
 
   // TODO(crbug.com/396998469): Do we still need this? Remove if this is not
@@ -1534,33 +1534,17 @@
   }
 }
 
-void HttpStreamPool::AttemptManager::NotifyJobOfPreconnectCompleteLater(
+void HttpStreamPool::AttemptManager::NotifyJobOfPreconnectComplete(
     raw_ptr<Job> job,
     int rv) {
   Job* raw_job = job.get();
   notified_jobs_.emplace(std::move(job));
-  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
-      FROM_HERE, base::BindOnce(&AttemptManager::NotifyJobOfPreconnectComplete,
-                                weak_ptr_factory_.GetWeakPtr(), raw_job, rv));
-}
-
-// TODO(crbug.com/396998469): Ensure `job` isn't a dangling pointer. There are
-// two paths to destroy `job`.
-// 1) JobController::OnPreconnectComplete() is called via
-//    Job::OnPreconnectComplete().
-// 2) JobController is destroyed as a part of HttpStreamPool destruction.
-//
-// In this method, we don't have to consider 1) because we are about to call
-// Job::OnPreconnectComplete(). If 2) happens, `this` should have been destroyed
-// too so we shouldn't reach here because we use "weak this" to post a task.
-void HttpStreamPool::AttemptManager::NotifyJobOfPreconnectComplete(Job* job,
-                                                                   int rv) {
   TRACE_EVENT_INSTANT("net.stream",
                       "AttemptManager::NotifyJobOfPreconnectComplete", track_,
-                      NetLogWithSourceToFlow(job->request_net_log()));
+                      NetLogWithSourceToFlow(raw_job->request_net_log()));
   // We don't need to call MaybeCompleteLater() here, since `job` will call
   // OnJobComplete() later.
-  job->OnPreconnectComplete(rv);
+  raw_job->OnPreconnectComplete(rv);
 }
 
 void HttpStreamPool::AttemptManager::CreateTextBasedStreamAndMaybeNotify(
diff --git a/net/http/http_stream_pool_attempt_manager.h b/net/http/http_stream_pool_attempt_manager.h
index b973d4f..bfb2c671 100644
--- a/net/http/http_stream_pool_attempt_manager.h
+++ b/net/http/http_stream_pool_attempt_manager.h
@@ -447,10 +447,8 @@
   void ProcessPreconnectsAfterAttemptComplete(int rv,
                                               size_t active_stream_count);
 
-  // Helper methods to post a task to notify a job of preconnect completion. If
-  // `this` is deleted the notification is canceled.
-  void NotifyJobOfPreconnectCompleteLater(raw_ptr<Job> job, int rv);
-  void NotifyJobOfPreconnectComplete(Job* job, int rv);
+  // Notifies a job of preconnect completion.
+  void NotifyJobOfPreconnectComplete(raw_ptr<Job> job, int rv);
 
   // Creates a text based stream. Notifies the highest priority job if there are
   // waiting jobs. Otherwise, `stream_socket` becomes an idle stream.
diff --git a/net/http/http_stream_pool_job_controller.cc b/net/http/http_stream_pool_job_controller.cc
index 50143cb..5314727 100644
--- a/net/http/http_stream_pool_job_controller.cc
+++ b/net/http/http_stream_pool_job_controller.cc
@@ -380,11 +380,10 @@
 }
 
 void HttpStreamPool::JobController::OnPreconnectComplete(Job* job, int status) {
-  CHECK(!alternative_job_);
-  CHECK_EQ(origin_job_.get(), job);
-  CHECK(preconnect_callback_);
-  origin_job_.reset();
-  std::move(preconnect_callback_).Run(status);
+  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&JobController::ResetJobAndInvokePreconnectCallback,
+                     weak_ptr_factory_.GetWeakPtr(), job, status));
 }
 
 LoadState HttpStreamPool::JobController::GetLoadState() const {
@@ -523,6 +522,16 @@
   delegate_->OnNeedsClientAuth(cert_info);
 }
 
+void HttpStreamPool::JobController::ResetJobAndInvokePreconnectCallback(
+    Job* job,
+    int status) {
+  CHECK(!alternative_job_);
+  CHECK_EQ(origin_job_.get(), job);
+  CHECK(preconnect_callback_);
+  origin_job_.reset();
+  std::move(preconnect_callback_).Run(status);
+}
+
 void HttpStreamPool::JobController::SetJobResult(Job* job, int status) {
   if (origin_job_.get() == job) {
     origin_job_result_ = status;
diff --git a/net/http/http_stream_pool_job_controller.h b/net/http/http_stream_pool_job_controller.h
index 960fc07e..c2c23c85 100644
--- a/net/http/http_stream_pool_job_controller.h
+++ b/net/http/http_stream_pool_job_controller.h
@@ -138,6 +138,9 @@
   // Calls the request's client auth callback.
   void CallOnNeedsClientAuth(SSLCertRequestInfo* cert_info);
 
+  // Resets `job` and invokes the preconnect callback.
+  void ResetJobAndInvokePreconnectCallback(Job* job, int status);
+
   // Sets the result of `job`.
   void SetJobResult(Job* job, int status);
 
diff --git a/net/log/BUILD.gn b/net/log/BUILD.gn
new file mode 100644
index 0000000..dc69014
--- /dev/null
+++ b/net/log/BUILD.gn
@@ -0,0 +1,15 @@
+# Copyright 2025 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+java_cpp_enum("net_log_capture_mode_srcjar") {
+  visibility = [ ":*" ]
+  sources = [ "net_log_capture_mode.h" ]
+}
+
+android_library("net_log_capture_mode_java") {
+  srcjar_deps = [ ":net_log_capture_mode_srcjar" ]
+  deps = [ "//third_party/androidx:androidx_annotation_annotation_java" ]
+}
diff --git a/net/log/net_log_capture_mode.h b/net/log/net_log_capture_mode.h
index 10cca0969..3a1bb4e 100644
--- a/net/log/net_log_capture_mode.h
+++ b/net/log/net_log_capture_mode.h
@@ -21,7 +21,9 @@
 //
 // Note the numeric values are used in a bitfield (NetLogCaptureModeSet) so must
 // be sequential starting from 0, and not exceed 31.
-enum class NetLogCaptureMode : uint32_t {
+//
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.net
+enum class NetLogCaptureMode : uint8_t {
   // Logging level that only allows fields that are known to be safe from a
   // privacy perspective.
   //
@@ -66,11 +68,11 @@
 // value "i".
 //
 // Use the NetLogCaptureModeSet*() functions to operate on it.
-using NetLogCaptureModeSet = uint32_t;
+using NetLogCaptureModeSet = uint8_t;
 
 inline NetLogCaptureModeSet NetLogCaptureModeToBit(
     NetLogCaptureMode capture_mode) {
-  return 1 << static_cast<uint32_t>(capture_mode);
+  return static_cast<uint8_t>(1 << static_cast<uint8_t>(capture_mode));
 }
 
 inline bool NetLogCaptureModeSetContains(NetLogCaptureMode capture_mode,
diff --git a/net/log/trace_net_log_observer.cc b/net/log/trace_net_log_observer.cc
index c11c876c..f8cf575 100644
--- a/net/log/trace_net_log_observer.cc
+++ b/net/log/trace_net_log_observer.cc
@@ -24,6 +24,8 @@
 
 // TraceLog category for NetLog events.
 constexpr const char kNetLogTracingCategory[] = "netlog";
+constexpr const char kSensitiveNetLogTracingCategory[] =
+    TRACE_DISABLED_BY_DEFAULT("netlog.sensitive");
 
 class TracedValue : public base::trace_event::ConvertableToTraceFormat {
  public:
@@ -48,7 +50,8 @@
 }  // namespace
 
 TraceNetLogObserver::TraceNetLogObserver(Options options)
-    : capture_mode_(options.capture_mode) {}
+    : capture_mode_(options.capture_mode),
+      use_sensitive_category_(options.use_sensitive_category) {}
 
 TraceNetLogObserver::~TraceNetLogObserver() {
   DCHECK(!net_log_to_watch_);
@@ -61,26 +64,40 @@
   params.Set("source_start_time",
              NetLog::TickCountToString(entry.source.start_time));
   const auto track = perfetto::Track(track_id_base_ + entry.source.id);
+
+  // The tracing category must be a compile-time constant, hence the macro to
+  // handle the sensitive vs non-sensitive categories.
+#define CALL_TRACE_EVENT(TRACE_EVENT, ...)                       \
+  do {                                                           \
+    if (use_sensitive_category_) {                               \
+      TRACE_EVENT(kSensitiveNetLogTracingCategory, __VA_ARGS__); \
+    } else {                                                     \
+      TRACE_EVENT(kNetLogTracingCategory, __VA_ARGS__);          \
+    }                                                            \
+  } while (false)
+
   switch (entry.phase) {
     case NetLogEventPhase::BEGIN:
-      TRACE_EVENT_BEGIN(
-          kNetLogTracingCategory,
+      CALL_TRACE_EVENT(
+          TRACE_EVENT_BEGIN,
           perfetto::StaticString(NetLogEventTypeToString(entry.type)), track,
           "source_type", NetLog::SourceTypeToString(entry.source.type),
           "params", std::make_unique<TracedValue>(std::move(params)));
       break;
     case NetLogEventPhase::END:
-      TRACE_EVENT_END(kNetLogTracingCategory, track, "params",
-                      std::make_unique<TracedValue>(std::move(params)));
+      CALL_TRACE_EVENT(TRACE_EVENT_END, track, "params",
+                       std::make_unique<TracedValue>(std::move(params)));
       break;
     case NetLogEventPhase::NONE:
-      TRACE_EVENT_INSTANT(
-          kNetLogTracingCategory,
+      CALL_TRACE_EVENT(
+          TRACE_EVENT_INSTANT,
           perfetto::StaticString(NetLogEventTypeToString(entry.type)), track,
           "source_type", NetLog::SourceTypeToString(entry.source.type),
           "params", std::make_unique<TracedValue>(std::move(params)));
       break;
   }
+
+#undef CALL_TRACE_EVENT
 }
 
 void TraceNetLogObserver::WatchForTraceStart(NetLog* netlog) {
diff --git a/net/log/trace_net_log_observer.h b/net/log/trace_net_log_observer.h
index ea9b2e75..846e94d 100644
--- a/net/log/trace_net_log_observer.h
+++ b/net/log/trace_net_log_observer.h
@@ -26,6 +26,16 @@
     static Options Default() { return {}; }
 
     NetLogCaptureMode capture_mode = NetLogCaptureMode::kDefault;
+
+    // If false, trace events will be logged under the "netlog" category.
+    // If true, trace events will be logged under the
+    // "disabled-by-default-netlog.sensitive" category.
+    //
+    // TODO(https://crbug.com/410018349): ideally this should be derived from
+    // `capture_mode`, i.e. we should treat this as true if `capture_mode` is
+    // not `kHeavilyRedacted`. We'd need to assess the consequences on current
+    // trace users, though.
+    bool use_sensitive_category = false;
   };
   explicit TraceNetLogObserver(Options options = Options::Default());
 
@@ -57,6 +67,7 @@
   const uint64_t track_id_base_ = base::RandUint64();
 
   const NetLogCaptureMode capture_mode_;
+  const bool use_sensitive_category_;
   raw_ptr<NetLog> net_log_to_watch_ = nullptr;
   base::WeakPtrFactory<TraceNetLogObserver> weak_factory_{this};
 };
diff --git a/services/network/public/mojom/net_log.mojom b/services/network/public/mojom/net_log.mojom
index 69cfbb6..5ed9cbd 100644
--- a/services/network/public/mojom/net_log.mojom
+++ b/services/network/public/mojom/net_log.mojom
@@ -72,7 +72,7 @@
 // changes.
 interface NetLogProxySource {
   // |modes| is a NetLogCaptureModeSet, a bitfield.
-  UpdateCaptureModes(uint32 modes);
+  UpdateCaptureModes(uint8 modes);
 };
 
 // Receives NetLog events from another process and inserts them into the main
diff --git a/services/viz/public/mojom/compositing/layer.mojom b/services/viz/public/mojom/compositing/layer.mojom
index 4e197e6..f737d0c 100644
--- a/services/viz/public/mojom/compositing/layer.mojom
+++ b/services/viz/public/mojom/compositing/layer.mojom
@@ -85,7 +85,6 @@
 // Extra fields in a cc::TextureLayerImpl that has been added to a tree or
 // modified in some interesting way since a prior tree update.
 struct TextureLayerExtra {
-  bool premultiplied_alpha;
   bool blend_background_color;
   bool force_texture_to_opaque;
   gfx.mojom.PointF uv_top_left;
diff --git a/services/webnn/coreml/context_impl_coreml.h b/services/webnn/coreml/context_impl_coreml.h
index 4bb76b44..98d616f 100644
--- a/services/webnn/coreml/context_impl_coreml.h
+++ b/services/webnn/coreml/context_impl_coreml.h
@@ -47,7 +47,6 @@
       WebNNGraphImpl::ComputeResourceInfo compute_resource_info,
       base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
           constant_operands,
-      base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands,
       CreateGraphImplCallback callback) override;
 
   void CreateTensorImpl(
diff --git a/services/webnn/coreml/context_impl_coreml.mm b/services/webnn/coreml/context_impl_coreml.mm
index 3343a65..93e45866 100644
--- a/services/webnn/coreml/context_impl_coreml.mm
+++ b/services/webnn/coreml/context_impl_coreml.mm
@@ -41,26 +41,17 @@
     WebNNGraphImpl::ComputeResourceInfo compute_resource_info,
     base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
         constant_operands,
-    base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands,
     CreateGraphImplCallback callback) {
   GraphImplCoreml::CreateAndBuild(
       std::move(receiver), this, std::move(graph_info),
       std::move(compute_resource_info), std::move(constant_operands),
-      std::move(constant_tensor_operands), options().Clone(), properties(),
-      std::move(callback));
+      options().Clone(), properties(), std::move(callback));
 }
 
 void ContextImplCoreml::CreateTensorImpl(
     mojo::PendingAssociatedReceiver<mojom::WebNNTensor> receiver,
     mojom::TensorInfoPtr tensor_info,
     CreateTensorImplCallback callback) {
-  // TODO(crbug.com/332350952): implement constant tensors for CoreML.
-  if (tensor_info->usage.Has(MLTensorUsageFlags::kGraphConstant)) {
-    std::move(callback).Run(base::unexpected(
-        mojom::Error::New(mojom::Error::Code::kNotSupportedError,
-                          "Creation of constant tensors is not supported.")));
-    return;
-  }
   std::move(callback).Run(TensorImplCoreml::Create(std::move(receiver), this,
                                                    std::move(tensor_info)));
 }
diff --git a/services/webnn/coreml/graph_impl_coreml.h b/services/webnn/coreml/graph_impl_coreml.h
index 447ca500..4639621d 100644
--- a/services/webnn/coreml/graph_impl_coreml.h
+++ b/services/webnn/coreml/graph_impl_coreml.h
@@ -48,7 +48,6 @@
       ComputeResourceInfo compute_resource_info,
       base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
           constant_operands,
-      base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands,
       mojom::CreateContextOptionsPtr context_options,
       ContextProperties context_properties,
       WebNNContextImpl::CreateGraphImplCallback callback);
diff --git a/services/webnn/coreml/graph_impl_coreml.mm b/services/webnn/coreml/graph_impl_coreml.mm
index ed9e1b7..3aec430f 100644
--- a/services/webnn/coreml/graph_impl_coreml.mm
+++ b/services/webnn/coreml/graph_impl_coreml.mm
@@ -374,7 +374,6 @@
     ComputeResourceInfo compute_resource_info,
     base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
         constant_operands,
-    base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands,
     mojom::CreateContextOptionsPtr context_options,
     ContextProperties context_properties,
     WebNNContextImpl::CreateGraphImplCallback callback) {
diff --git a/services/webnn/dml/context_impl_dml.cc b/services/webnn/dml/context_impl_dml.cc
index 42179a9..701eec3 100644
--- a/services/webnn/dml/context_impl_dml.cc
+++ b/services/webnn/dml/context_impl_dml.cc
@@ -613,7 +613,6 @@
     WebNNGraphImpl::ComputeResourceInfo compute_resource_info,
     base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
         constant_operands,
-    base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands,
     WebNNContextImpl::CreateGraphImplCallback callback) {
   if (g_backend_for_testing) {
     g_backend_for_testing->CreateGraphImpl(std::move(receiver), this,
@@ -625,8 +624,7 @@
   GraphImplDml::CreateAndBuild(
       std::move(receiver), adapter_, weak_factory_.GetWeakPtr(),
       std::move(graph_info), std::move(compute_resource_info),
-      std::move(constant_operands), std::move(constant_tensor_operands),
-      std::move(callback),
+      std::move(constant_operands), std::move(callback),
       gpu_feature_info_->IsWorkaroundEnabled(
           gpu::DISABLE_DML_META_COMMANDS_FOR_GPU));
 }
diff --git a/services/webnn/dml/context_impl_dml.h b/services/webnn/dml/context_impl_dml.h
index efeee68..82c6e7f6 100644
--- a/services/webnn/dml/context_impl_dml.h
+++ b/services/webnn/dml/context_impl_dml.h
@@ -90,7 +90,6 @@
       WebNNGraphImpl::ComputeResourceInfo compute_resource_info,
       base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
           constant_operands,
-      base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands,
       CreateGraphImplCallback callback) override;
 
   void CreateTensorImpl(
diff --git a/services/webnn/dml/context_impl_dml_test.cc b/services/webnn/dml/context_impl_dml_test.cc
index 4cfc4960..dbd067d5 100644
--- a/services/webnn/dml/context_impl_dml_test.cc
+++ b/services/webnn/dml/context_impl_dml_test.cc
@@ -123,7 +123,7 @@
           *OperandDescriptor::Create(webnn::GetContextPropertiesForTesting(),
                                      data_type, shape, "tensor"),
           MLTensorUsage{MLTensorUsageFlags::kWrite, MLTensorUsageFlags::kRead}),
-      mojo_base::BigBuffer(0), create_tensor_future.GetCallback());
+      create_tensor_future.GetCallback());
   mojom::CreateTensorResultPtr create_tensor_result =
       create_tensor_future.Take();
   mojo::AssociatedRemote<mojom::WebNNTensor> webnn_tensor_remote;
diff --git a/services/webnn/dml/graph_impl_dml.cc b/services/webnn/dml/graph_impl_dml.cc
index 2d666271..fc5dfac 100644
--- a/services/webnn/dml/graph_impl_dml.cc
+++ b/services/webnn/dml/graph_impl_dml.cc
@@ -5970,7 +5970,6 @@
     ComputeResourceInfo compute_resource_info,
     base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
         constant_operands,
-    base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands,
     base::expected<ComPtr<IDMLCompiledOperator>, HRESULT> compilation_result) {
   TRACE_EVENT0("gpu", "dml::GraphImplDml::OnCompilationComplete");
 
@@ -6114,23 +6113,6 @@
           std::move(buffer_binding);
     }
   }
-
-  // The tensors used for constants must be bound during operator initialization
-  // and not during execution.
-  for (auto& [constant_id, constant_tensor] : constant_tensor_operands) {
-    TensorImplDml* constant_tensor_impl =
-        static_cast<TensorImplDml*>(constant_tensor);
-    // Get the graph input index with the constant id.
-    const auto graph_input_index_iterator =
-        constant_id_to_input_index_map.find(constant_id);
-    CHECK(graph_input_index_iterator != constant_id_to_input_index_map.end());
-    input_buffer_binding[graph_input_index_iterator->second] =
-        DML_BUFFER_BINDING{
-            .Buffer = constant_tensor_impl->buffer(),
-            .Offset = 0,
-            .SizeInBytes = constant_tensor_impl->PackedByteLength()};
-  }
-
   DML_BUFFER_ARRAY_BINDING input_buffer_array_binding{
       .BindingCount = base::checked_cast<uint32_t>(input_buffer_binding.size()),
       .Bindings = input_buffer_binding.data()};
@@ -6330,7 +6312,6 @@
     mojom::GraphInfoPtr& graph_info,
     base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>&
         constant_operands,
-    const base::flat_map<OperandId, WebNNTensorImpl*>& constant_tensor_operands,
     GraphBuilderDml& graph_builder,
     absl::flat_hash_map<OperandId, uint32_t>& constant_id_to_input_index_map,
     GraphBufferBindingInfo& graph_buffer_binding_info) {
@@ -6363,19 +6344,6 @@
                        constant_id_to_input_index_map);
   }
 
-  // Add constant tensors which are considered read-only inputs that must be
-  // bound during graph initialization.
-  for (const auto& [constant_id, tensor_impl] : constant_tensor_operands) {
-    const Node* node = graph_builder.CreateInputNode();
-    constant_id_to_input_index_map[constant_id] =
-        node->AsInputNode()->GetGraphInputIndex();
-    TensorDesc tensor_desc(GetTensorDataType(tensor_impl->data_type()),
-                           DML_TENSOR_FLAG_OWNED_BY_DML, tensor_impl->shape());
-    const NodeOutput* output =
-        graph_builder.CreateNodeOutput(node, std::move(tensor_desc));
-    CHECK(id_to_node_output_map.try_emplace(constant_id, output).second);
-  }
-
   // Fuse the operations in `mojom::GraphInfo` wherever possible to optimize the
   // graph's compute performance.
   //
@@ -6805,7 +6773,6 @@
     ComputeResourceInfo compute_resource_info,
     base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
         constant_operands,
-    base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands,
     WebNNContextImpl::CreateGraphImplCallback callback,
     const bool disable_dml_meta_commands_for_gpu) {
   TRACE_EVENT0("gpu", "dml::GraphImplDml::CreateAndBuild");
@@ -6815,8 +6782,8 @@
   base::expected<void, mojom::ErrorPtr> create_operator_result =
       GraphImplDml::CreateAndBuildInternal(
           context->properties(), adapter, graph_info, constant_operands,
-          constant_tensor_operands, graph_builder,
-          constant_id_to_input_index_map, graph_buffer_binding_info);
+          graph_builder, constant_id_to_input_index_map,
+          graph_buffer_binding_info);
 
   // TODO(crbug.com/349649099): Handle context lost for operator creation
   // failures.
@@ -6847,8 +6814,7 @@
           std::move(adapter), std::move(context), std::move(callback),
           std::move(constant_id_to_input_index_map),
           std::move(graph_buffer_binding_info),
-          std::move(compute_resource_info), std::move(constant_operands),
-          std::move(constant_tensor_operands)));
+          std::move(compute_resource_info), std::move(constant_operands)));
 }
 
 void GraphImplDml::HandleDispatchFailure(std::string_view error_message,
diff --git a/services/webnn/dml/graph_impl_dml.h b/services/webnn/dml/graph_impl_dml.h
index 5ca1289..56ad8e78 100644
--- a/services/webnn/dml/graph_impl_dml.h
+++ b/services/webnn/dml/graph_impl_dml.h
@@ -80,8 +80,6 @@
       mojom::GraphInfoPtr& graph_info,
       base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>&
           constant_operands,
-      const base::flat_map<OperandId, WebNNTensorImpl*>&
-          constant_tensor_operands,
       GraphBuilderDml& graph_builder,
       absl::flat_hash_map<OperandId, uint32_t>& constant_id_to_input_index_map,
       GraphBufferBindingInfo& graph_buffer_binding_info);
@@ -100,7 +98,6 @@
       ComputeResourceInfo compute_resource_info,
       base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
           constant_operands,
-      base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands,
       WebNNContextImpl::CreateGraphImplCallback callback,
       bool disable_dml_meta_commands_for_gpu);
 
@@ -231,7 +228,6 @@
       ComputeResourceInfo compute_resource_info,
       base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
           constant_operands,
-      base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands,
       base::expected<Microsoft::WRL::ComPtr<IDMLCompiledOperator>, HRESULT>
           compilation_result);
 
diff --git a/services/webnn/public/cpp/ml_tensor_usage.h b/services/webnn/public/cpp/ml_tensor_usage.h
index 1e2a505..e6c0cc0 100644
--- a/services/webnn/public/cpp/ml_tensor_usage.h
+++ b/services/webnn/public/cpp/ml_tensor_usage.h
@@ -19,11 +19,8 @@
   // This tensor can be used with writeTensor().
   kWrite,
 
-  // This tensor can be used with constant().
-  kGraphConstant,
-
   kMinValue = kWebGpuInterop,
-  kMaxValue = kGraphConstant,
+  kMaxValue = kWrite,
 };
 
 using MLTensorUsage = base::EnumSet<MLTensorUsageFlags,
diff --git a/services/webnn/public/mojom/tensor_usage_mojom_traits.h b/services/webnn/public/mojom/tensor_usage_mojom_traits.h
index 97bbba2..2da64b0 100644
--- a/services/webnn/public/mojom/tensor_usage_mojom_traits.h
+++ b/services/webnn/public/mojom/tensor_usage_mojom_traits.h
@@ -25,10 +25,6 @@
     return usage.Has(webnn::MLTensorUsageFlags::kRead);
   }
 
-  static bool graph_constant(const webnn::MLTensorUsage& usage) {
-    return usage.Has(webnn::MLTensorUsageFlags::kGraphConstant);
-  }
-
   static bool Read(webnn::mojom::TensorUsageDataView data,
                    webnn::MLTensorUsage* out) {
     out->Clear();
@@ -45,13 +41,6 @@
       out->Put(webnn::MLTensorUsageFlags::kWrite);
     }
 
-    if (data.graph_constant()) {
-      if (data.read() || data.write()) {
-        return false;
-      }
-      out->Put(webnn::MLTensorUsageFlags::kGraphConstant);
-    }
-
     return true;
   }
 };
diff --git a/services/webnn/public/mojom/webnn_context.mojom b/services/webnn/public/mojom/webnn_context.mojom
index a546cb6..8cf41e1 100644
--- a/services/webnn/public/mojom/webnn_context.mojom
+++ b/services/webnn/public/mojom/webnn_context.mojom
@@ -4,7 +4,6 @@
 
 module webnn.mojom;
 
-import "mojo/public/mojom/base/big_buffer.mojom";
 import "services/webnn/public/mojom/features.mojom";
 import "services/webnn/public/mojom/webnn_tensor.mojom";
 import "services/webnn/public/mojom/webnn_error.mojom";
@@ -39,8 +38,5 @@
   // Called by the renderer process to create `WebNNTensor` message pipe for
   // creating platform specific tensors, the WebNN tensor will be validated and
   // created. This method guarantees memory allocation on the device.
-  // Optionally, non-empty tensor data containing values to initialize contents.
-  // Valid for tensor data to be empty when not being used as graph constants.
-  CreateTensor(TensorInfo tensor_info, mojo_base.mojom.BigBuffer tensor_data)
-    => (CreateTensorResult result);
+  CreateTensor(TensorInfo tensor_info) => (CreateTensorResult result);
 };
\ No newline at end of file
diff --git a/services/webnn/public/mojom/webnn_graph.mojom b/services/webnn/public/mojom/webnn_graph.mojom
index e212ae3..ffaaa86 100644
--- a/services/webnn/public/mojom/webnn_graph.mojom
+++ b/services/webnn/public/mojom/webnn_graph.mojom
@@ -1427,10 +1427,6 @@
   // which identifies the respective pending constant operand.
   map<uint32, blink.mojom.WebNNPendingConstantToken>
       constant_operand_ids_to_handles;
-  // A map of tensors used for graph constants. The key is the id of
-  // the constant operand, while the value is a handle to the tensor containing
-  // the weights.
-  map<uint32, blink.mojom.WebNNTensorToken> id_to_constant_tensor_operand_map;
 };
 
 // WebNNGraph runs in the GPU process and is called by the renderer process to
diff --git a/services/webnn/public/mojom/webnn_tensor.mojom b/services/webnn/public/mojom/webnn_tensor.mojom
index 79125f6..d08e120 100644
--- a/services/webnn/public/mojom/webnn_tensor.mojom
+++ b/services/webnn/public/mojom/webnn_tensor.mojom
@@ -16,9 +16,6 @@
   bool read;
   // This tensor can be used with writeTensor().
   bool write;
-  // This tensor is only allowed to be used as a graph constant.
-  // A graph constant cannot be modified after it is created.
-  bool graph_constant;
 };
 
 // Description of the WebNNTensor to create.
diff --git a/services/webnn/tflite/context_impl_tflite.cc b/services/webnn/tflite/context_impl_tflite.cc
index eab9d202..b705c87 100644
--- a/services/webnn/tflite/context_impl_tflite.cc
+++ b/services/webnn/tflite/context_impl_tflite.cc
@@ -38,25 +38,16 @@
     WebNNGraphImpl::ComputeResourceInfo compute_resource_info,
     base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
         constant_operands,
-    base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands,
     CreateGraphImplCallback callback) {
   std::move(callback).Run(GraphImplTflite::CreateAndBuild(
       std::move(receiver), std::move(graph_info),
-      std::move(compute_resource_info), std::move(constant_operands),
-      std::move(constant_tensor_operands), this));
+      std::move(compute_resource_info), std::move(constant_operands), this));
 }
 
 void ContextImplTflite::CreateTensorImpl(
     mojo::PendingAssociatedReceiver<mojom::WebNNTensor> receiver,
     mojom::TensorInfoPtr tensor_info,
     CreateTensorImplCallback callback) {
-  // TODO(crbug.com/332350952): implement constant tensors for TFLite.
-  if (tensor_info->usage.Has(MLTensorUsageFlags::kGraphConstant)) {
-    std::move(callback).Run(base::unexpected(
-        mojom::Error::New(mojom::Error::Code::kNotSupportedError,
-                          "Creation of constant tensors is not supported.")));
-    return;
-  }
   std::move(callback).Run(TensorImplTflite::Create(std::move(receiver), this,
                                                    std::move(tensor_info)));
 }
diff --git a/services/webnn/tflite/context_impl_tflite.h b/services/webnn/tflite/context_impl_tflite.h
index 60d21c08..1632943 100644
--- a/services/webnn/tflite/context_impl_tflite.h
+++ b/services/webnn/tflite/context_impl_tflite.h
@@ -39,7 +39,6 @@
       WebNNGraphImpl::ComputeResourceInfo compute_resource_info,
       base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
           constant_operands,
-      base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands,
       CreateGraphImplCallback callback) override;
 
   void CreateTensorImpl(
diff --git a/services/webnn/tflite/graph_impl_tflite.cc b/services/webnn/tflite/graph_impl_tflite.cc
index ec9e0e6..0c54e3f 100644
--- a/services/webnn/tflite/graph_impl_tflite.cc
+++ b/services/webnn/tflite/graph_impl_tflite.cc
@@ -289,7 +289,6 @@
     ComputeResourceInfo compute_resource_info,
     base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
         constant_operands,
-    base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands,
     ContextImplTflite* context) {
   ASSIGN_OR_RETURN(GraphBuilderTflite::Result result,
                    GraphBuilderTflite::CreateAndBuild(
diff --git a/services/webnn/tflite/graph_impl_tflite.h b/services/webnn/tflite/graph_impl_tflite.h
index a9803e0..64709e5e 100644
--- a/services/webnn/tflite/graph_impl_tflite.h
+++ b/services/webnn/tflite/graph_impl_tflite.h
@@ -40,7 +40,6 @@
       ComputeResourceInfo compute_resource_info,
       base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
           constant_operands,
-      base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands,
       ContextImplTflite* context);
 
   GraphImplTflite(const GraphImplTflite&) = delete;
diff --git a/services/webnn/webnn_context_impl.cc b/services/webnn/webnn_context_impl.cc
index c2f3a3f..487d153 100644
--- a/services/webnn/webnn_context_impl.cc
+++ b/services/webnn/webnn_context_impl.cc
@@ -86,7 +86,6 @@
 
 void WebNNContextImpl::CreateTensor(
     mojom::TensorInfoPtr tensor_info,
-    mojo_base::BigBuffer tensor_data,
     mojom::WebNNContext::CreateTensorCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -95,40 +94,17 @@
     return;
   }
 
-  if (tensor_info->usage.Has(MLTensorUsageFlags::kGraphConstant)) {
-    const base::expected<OperandDescriptor, std::string> validated_descriptor =
-        webnn::OperandDescriptor::Create(
-            properties_, tensor_info->descriptor.data_type(),
-            tensor_info->descriptor.shape(), "WebNNGraphConstant");
-    if (!validated_descriptor.has_value()) {
-      receiver_.ReportBadMessage(kBadMessageInvalidTensor);
-      return;
-    }
-
-    if (!properties_.data_type_limits.constant.Has(
-            validated_descriptor->data_type())) {
-      receiver_.ReportBadMessage(kBadMessageInvalidTensor);
-      return;
-    }
-
-    if (tensor_data.size() != validated_descriptor->PackedByteLength()) {
-      receiver_.ReportBadMessage(kBadMessageInvalidTensor);
-      return;
-    }
-  }
-
   mojo::PendingAssociatedRemote<mojom::WebNNTensor> remote;
   auto receiver = remote.InitWithNewEndpointAndPassReceiver();
-  CreateTensorImpl(std::move(receiver), std::move(tensor_info),
-                   base::BindOnce(&WebNNContextImpl::DidCreateWebNNTensorImpl,
-                                  AsWeakPtr(), std::move(callback),
-                                  std::move(remote), std::move(tensor_data)));
+  CreateTensorImpl(
+      std::move(receiver), std::move(tensor_info),
+      base::BindOnce(&WebNNContextImpl::DidCreateWebNNTensorImpl, AsWeakPtr(),
+                     std::move(callback), std::move(remote)));
 }
 
 void WebNNContextImpl::DidCreateWebNNTensorImpl(
     mojom::WebNNContext::CreateTensorCallback callback,
     mojo::PendingAssociatedRemote<mojom::WebNNTensor> remote,
-    mojo_base::BigBuffer tensor_data,
     base::expected<std::unique_ptr<WebNNTensorImpl>, mojom::ErrorPtr> result) {
   if (!result.has_value()) {
     std::move(callback).Run(
@@ -136,13 +112,6 @@
     return;
   }
 
-  // Write the specified values into the tensor. If `tensor_data` is empty,
-  // the tensor should be left initialized to zero. The `tensor_data` size
-  // should of been already validated in CreateTensor().
-  if (tensor_data.size() > 0) {
-    result.value()->WriteTensorImpl(std::move(tensor_data));
-  }
-
   auto success = mojom::CreateTensorSuccess::New(std::move(remote),
                                                  result.value()->handle());
   std::move(callback).Run(
diff --git a/services/webnn/webnn_context_impl.h b/services/webnn/webnn_context_impl.h
index 1b3aa5ab..ac9fa13 100644
--- a/services/webnn/webnn_context_impl.h
+++ b/services/webnn/webnn_context_impl.h
@@ -18,7 +18,6 @@
 #include "base/types/expected.h"
 #include "base/types/optional_ref.h"
 #include "base/types/pass_key.h"
-#include "mojo/public/cpp/base/big_buffer.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"
@@ -105,7 +104,6 @@
       WebNNGraphImpl::ComputeResourceInfo compute_resource_info,
       base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
           constant_operands,
-      base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands,
       CreateGraphImplCallback callback) = 0;
 
   // Pass ownership of a newly-created `graph_impl` to this context.
@@ -143,7 +141,6 @@
       mojo::PendingAssociatedReceiver<mojom::WebNNGraphBuilder> receiver)
       override;
   void CreateTensor(mojom::TensorInfoPtr tensor_info,
-                    mojo_base::BigBuffer tensor_data,
                     CreateTensorCallback callback) override;
 
   // This method will be called by `CreateTensor()` after the tensor info is
@@ -157,7 +154,6 @@
   void DidCreateWebNNTensorImpl(
       CreateTensorCallback callback,
       mojo::PendingAssociatedRemote<mojom::WebNNTensor> remote,
-      mojo_base::BigBuffer tensor_data,
       base::expected<std::unique_ptr<WebNNTensorImpl>, mojom::ErrorPtr> result);
 
   SEQUENCE_CHECKER(sequence_checker_);
diff --git a/services/webnn/webnn_graph_builder_impl.cc b/services/webnn/webnn_graph_builder_impl.cc
index 7207fd7..3add5a44 100644
--- a/services/webnn/webnn_graph_builder_impl.cc
+++ b/services/webnn/webnn_graph_builder_impl.cc
@@ -24,7 +24,6 @@
 #include "services/webnn/webnn_context_impl.h"
 #include "services/webnn/webnn_graph_impl.h"
 #include "services/webnn/webnn_pending_constant_operand.h"
-#include "services/webnn/webnn_tensor_impl.h"
 #include "services/webnn/webnn_utils.h"
 
 // Evaluate `condition`, and if it returns false then return false.
@@ -2693,11 +2692,9 @@
 WebNNGraphBuilderImpl::ValidateGraphSuccessResult::ValidateGraphSuccessResult(
     WebNNGraphImpl::ComputeResourceInfo compute_resource_info,
     base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
-        constant_operands,
-    base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands)
+        constant_operands)
     : compute_resource_info(std::move(compute_resource_info)),
-      constant_operands(std::move(constant_operands)),
-      constant_tensor_operands(std::move(constant_tensor_operands)) {}
+      constant_operands(std::move(constant_operands)) {}
 
 WebNNGraphBuilderImpl::ValidateGraphSuccessResult::ValidateGraphSuccessResult(
     ValidateGraphSuccessResult&&) = default;
@@ -2783,7 +2780,6 @@
       std::move(receiver), std::move(graph_info),
       std::move(validate_graph_result->compute_resource_info),
       std::move(validate_graph_result->constant_operands),
-      std::move(validate_graph_result->constant_tensor_operands),
       base::BindOnce(&WebNNGraphBuilderImpl::DidCreateGraph,
                      weak_factory_.GetWeakPtr(), std::move(callback),
                      std::move(remote)));
@@ -2871,9 +2867,6 @@
   std::vector<std::pair<OperandId, std::unique_ptr<WebNNConstantOperand>>>
       graph_constants;
   graph_constants.reserve(graph_info.constant_operand_ids_to_handles.size());
-  std::vector<std::pair<OperandId, WebNNTensorImpl*>> graph_constant_tensors;
-  graph_constant_tensors.reserve(
-      graph_info.id_to_constant_tensor_operand_map.size());
 
   for (size_t id = 0; id < graph_info.operands.size(); ++id) {
     const mojom::OperandPtr& operand = graph_info.operands[id];
@@ -2931,33 +2924,6 @@
           return std::nullopt;
         }
 
-        // Constants using tensors for weights.
-        if (auto id_and_handle_it =
-                graph_info.id_to_constant_tensor_operand_map.find(id);
-            id_and_handle_it !=
-            graph_info.id_to_constant_tensor_operand_map.end()) {
-          // `id` must correspond to a handle known by the context...
-          base::optional_ref<WebNNTensorImpl> tensor_impl =
-              context_->GetWebNNTensorImpl(id_and_handle_it->second);
-          if (!tensor_impl.has_value()) {
-            return std::nullopt;
-          }
-
-          // ...whose tensor must have the correct usage.
-          if (!tensor_impl->usage().Has(MLTensorUsageFlags::kGraphConstant)) {
-            return std::nullopt;
-          }
-
-          // ...whose data must be compatible with what `operand` expects.
-          if (!tensor_impl->IsValidWithDescriptor(operand->descriptor)) {
-            return std::nullopt;
-          }
-
-          graph_constant_tensors.emplace_back(id, tensor_impl.as_ptr());
-          processed_operands.insert(id);
-          break;
-        }
-
         // `id` must correspond to a pending constant operand handle...
         auto id_and_handle_it =
             graph_info.constant_operand_ids_to_handles.find(id);
@@ -3034,11 +3000,6 @@
     return std::nullopt;
   }
 
-  if (graph_constant_tensors.size() !=
-      graph_info.id_to_constant_tensor_operand_map.size()) {
-    return std::nullopt;
-  }
-
   // Validate the operations which are sorted in the topological order.
   std::optional<OperationValidationContext::ValidationResult> result =
       OperationValidationContext::ValidateOperationsAndGetDependencies(
@@ -3074,7 +3035,7 @@
           std::move(result->operand_to_dependent_operations),
           std::move(result->operand_to_producing_operation),
           base::PassKey<WebNNGraphBuilderImpl>()),
-      std::move(graph_constants), std::move(graph_constant_tensors)};
+      std::move(graph_constants)};
 }
 
 void WebNNGraphBuilderImpl::DestroySelf() {
diff --git a/services/webnn/webnn_graph_builder_impl.h b/services/webnn/webnn_graph_builder_impl.h
index 94a6866..e95c0d8 100644
--- a/services/webnn/webnn_graph_builder_impl.h
+++ b/services/webnn/webnn_graph_builder_impl.h
@@ -31,7 +31,6 @@
 
 class WebNNConstantOperand;
 class WebNNContextImpl;
-class WebNNTensorImpl;
 
 // Services-side connection to an `MLGraphBuilder`. Responsible for managing
 // data associated with the graph builder.
@@ -67,8 +66,7 @@
     ValidateGraphSuccessResult(
         WebNNGraphImpl::ComputeResourceInfo compute_resource_info,
         base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
-            constant_operands,
-        base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands);
+            constant_operands);
     ~ValidateGraphSuccessResult();
 
     ValidateGraphSuccessResult(const ValidateGraphSuccessResult&) = delete;
@@ -85,10 +83,6 @@
     // `keep_builder_resources_for_testing` is false.
     base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
         constant_operands;
-
-    // Constant tensors associated with this graph, which will be used during
-    // graph construction.
-    base::flat_map<OperandId, WebNNTensorImpl*> constant_tensor_operands;
   };
 
   // Transfer ownership of this builder's resources to a returned
diff --git a/services/webnn/webnn_graph_builder_impl_unittest.cc b/services/webnn/webnn_graph_builder_impl_unittest.cc
index c678aa06..7b7293e 100644
--- a/services/webnn/webnn_graph_builder_impl_unittest.cc
+++ b/services/webnn/webnn_graph_builder_impl_unittest.cc
@@ -92,8 +92,6 @@
       WebNNGraphImpl::ComputeResourceInfo compute_resource_info,
       base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
       /*constant_operands*/,
-      base::flat_map<OperandId, WebNNTensorImpl*>
-      /*constant_tensor_operands*/,
       CreateGraphImplCallback callback) override {
     // Asynchronously resolve `callback` so there's an opportunity for
     // subsequent messages to be (illegally) sent from the `WebNNGraphBuilder`
diff --git a/services/webnn/webnn_graph_impl.cc b/services/webnn/webnn_graph_impl.cc
index d769e719..3ba556a 100644
--- a/services/webnn/webnn_graph_impl.cc
+++ b/services/webnn/webnn_graph_impl.cc
@@ -128,14 +128,6 @@
     if (!input_tensor.has_value()) {
       return;
     }
-
-    // Input MLTensor is always dispatchable, which isn’t allowed when used as
-    // a graph constant.
-    if (input_tensor->usage().Has(MLTensorUsageFlags::kGraphConstant)) {
-      receiver_.ReportBadMessage(kBadMessageInvalidTensor);
-      return;
-    }
-
     name_to_input_tensors.emplace_back(name, input_tensor.as_ptr());
   }
   base::flat_map<std::string_view, WebNNTensorImpl*> name_to_input_tensor_map(
@@ -158,14 +150,6 @@
     if (!output_tensor.has_value()) {
       return;
     }
-
-    // Output MLTensor is always dispatchable, which isn’t allowed when used as
-    // a graph constant.
-    if (output_tensor->usage().Has(MLTensorUsageFlags::kGraphConstant)) {
-      receiver_.ReportBadMessage(kBadMessageInvalidTensor);
-      return;
-    }
-
     name_to_output_tensors.emplace_back(name, output_tensor.as_ptr());
   }
 
diff --git a/services/webnn/webnn_graph_impl_backend_test.cc b/services/webnn/webnn_graph_impl_backend_test.cc
index 2bde2b4d..f03d2b4 100644
--- a/services/webnn/webnn_graph_impl_backend_test.cc
+++ b/services/webnn/webnn_graph_impl_backend_test.cc
@@ -84,7 +84,7 @@
   mojo::AssociatedRemote<mojom::WebNNTensor> webnn_tensor_remote;
 
   base::test::TestFuture<mojom::CreateTensorResultPtr> create_tensor_future;
-  context_remote->CreateTensor(std::move(tensor_info), mojo_base::BigBuffer(0),
+  context_remote->CreateTensor(std::move(tensor_info),
                                create_tensor_future.GetCallback());
   mojom::CreateTensorResultPtr create_tensor_result =
       create_tensor_future.Take();
diff --git a/services/webnn/webnn_graph_impl_unittest.cc b/services/webnn/webnn_graph_impl_unittest.cc
index 8d5dcdd..f140df17 100644
--- a/services/webnn/webnn_graph_impl_unittest.cc
+++ b/services/webnn/webnn_graph_impl_unittest.cc
@@ -121,7 +121,6 @@
       base::flat_map<
           OperandId,
           std::unique_ptr<WebNNConstantOperand>> /*constant_operands*/,
-      base::flat_map<OperandId, WebNNTensorImpl*> /*constant_tensor_operands*/,
       CreateGraphImplCallback callback) override {
     FakeWebNNGraphImpl::CreateAndBuild(std::move(receiver), this, *graph_info,
                                        std::move(compute_resource_info),
@@ -176,7 +175,7 @@
       mojom::TensorInfo::New(
           OperandDescriptor::UnsafeCreateForTesting(data_type, shape),
           MLTensorUsage()),
-      mojo_base::BigBuffer(0), create_tensor_future.GetCallback());
+      create_tensor_future.GetCallback());
   mojom::CreateTensorResultPtr create_tensor_result =
       create_tensor_future.Take();
   mojo::AssociatedRemote<mojom::WebNNTensor> webnn_tensor;
diff --git a/services/webnn/webnn_graph_mojolpm_fuzzer.cc b/services/webnn/webnn_graph_mojolpm_fuzzer.cc
index 1373d2d..f34b5ab 100644
--- a/services/webnn/webnn_graph_mojolpm_fuzzer.cc
+++ b/services/webnn/webnn_graph_mojolpm_fuzzer.cc
@@ -152,7 +152,6 @@
     base::test::TestFuture<webnn::mojom::CreateTensorResultPtr>
         create_tensor_future;
     webnn_context_remote->CreateTensor(std::move(tensor_info),
-                                       mojo_base::BigBuffer(0),
                                        create_tensor_future.GetCallback());
     webnn::mojom::CreateTensorResultPtr create_tensor_result =
         create_tensor_future.Take();
@@ -187,7 +186,6 @@
     base::test::TestFuture<webnn::mojom::CreateTensorResultPtr>
         create_tensor_future;
     webnn_context_remote->CreateTensor(std::move(tensor_info),
-                                       mojo_base::BigBuffer(0),
                                        create_tensor_future.GetCallback());
     webnn::mojom::CreateTensorResultPtr create_tensor_result =
         create_tensor_future.Take();
diff --git a/services/webnn/webnn_tensor_impl.cc b/services/webnn/webnn_tensor_impl.cc
index 2063c0a..f8ba14f68 100644
--- a/services/webnn/webnn_tensor_impl.cc
+++ b/services/webnn/webnn_tensor_impl.cc
@@ -26,11 +26,6 @@
 
 WebNNTensorImpl::~WebNNTensorImpl() = default;
 
-bool WebNNTensorImpl::IsValidWithDescriptor(
-    const OperandDescriptor& descriptor) const {
-  return descriptor_ == descriptor;
-}
-
 void WebNNTensorImpl::ReadTensor(ReadTensorCallback callback) {
   if (!usage().Has(MLTensorUsageFlags::kRead)) {
     receiver_.ReportBadMessage(kBadMessageInvalidTensor);
diff --git a/services/webnn/webnn_tensor_impl.h b/services/webnn/webnn_tensor_impl.h
index ae02f3a..7850e49 100644
--- a/services/webnn/webnn_tensor_impl.h
+++ b/services/webnn/webnn_tensor_impl.h
@@ -43,13 +43,6 @@
     return weak_factory_.GetWeakPtr();
   }
 
-  bool IsValidWithDescriptor(const OperandDescriptor& descriptor) const;
-
-  // This method will be called by `WriteTensor()` after the write info is
-  // validated. A backend subclass should implement this method to write data
-  // to a platform specific buffer.
-  virtual void WriteTensorImpl(mojo_base::BigBuffer src_buffer) = 0;
-
  protected:
   // This method will be called by `ReadTensor()` after the read info is
   // validated. A backend subclass should implement this method to read data
@@ -57,6 +50,11 @@
   virtual void ReadTensorImpl(
       mojom::WebNNTensor::ReadTensorCallback callback) = 0;
 
+  // This method will be called by `WriteTensor()` after the write info is
+  // validated. A backend subclass should implement this method to write data
+  // to a platform specific buffer.
+  virtual void WriteTensorImpl(mojo_base::BigBuffer src_buffer) = 0;
+
   // WebNNContextImpl owns this object.
   const raw_ptr<WebNNContextImpl> context_;
 
diff --git a/services/webnn/webnn_tensor_impl_backend_test.cc b/services/webnn/webnn_tensor_impl_backend_test.cc
index 1e4ec90a..25411fc 100644
--- a/services/webnn/webnn_tensor_impl_backend_test.cc
+++ b/services/webnn/webnn_tensor_impl_backend_test.cc
@@ -202,7 +202,6 @@
                   mojom::TensorInfoPtr tensor_info) {
   base::test::TestFuture<mojom::CreateTensorResultPtr> create_tensor_future;
   webnn_context_remote->CreateTensor(std::move(tensor_info),
-                                     mojo_base::BigBuffer(0),
                                      create_tensor_future.GetCallback());
   mojom::CreateTensorResultPtr create_tensor_result =
       create_tensor_future.Take();
@@ -313,7 +312,7 @@
       mojom::TensorInfo::New(OperandDescriptor::UnsafeCreateForTesting(
                                  OperandDataType::kUint8, large_shape),
                              MLTensorUsage{MLTensorUsageFlags::kWrite}),
-      mojo_base::BigBuffer(0), std::move(create_tensor_callback));
+      std::move(create_tensor_callback));
 
   webnn_context_remote.FlushForTesting();
   EXPECT_EQ(bad_message_helper.GetLastBadMessage(), kBadMessageInvalidTensor);
diff --git a/skia/features.gni b/skia/features.gni
index 0b5d2853..20b90e3 100644
--- a/skia/features.gni
+++ b/skia/features.gni
@@ -15,9 +15,8 @@
       (is_ios && use_blink) || (is_linux && !is_castos) || is_desktop_android
 
   # Enable experimental Skia Graphite Metal backend. Intended only for debugging
-  # on non-official developer builds on Mac, and on iOS Blink until the Dawn
-  # backend can be brought up.
-  skia_use_metal = (is_mac && !is_official_build) || (is_ios && use_blink)
+  # on non-official developer builds on Mac and iOS Blink.
+  skia_use_metal = (is_mac || (is_ios && use_blink)) && !is_official_build
 
   # Enable gtests using SkiaRenderer on Skia Graphite.
   enable_skia_graphite_gtests = is_mac || (is_ios && use_blink)
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 4a4b7c4..645d950 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -5388,6 +5388,23 @@
             ]
         }
     ],
+    "ClipboardChangeEvent": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ClipboardChangeEvent"
+                    ]
+                }
+            ]
+        }
+    ],
     "ClipboardDeemphasisAndroid": [
         {
             "platforms": [
@@ -5889,6 +5906,23 @@
             ]
         }
     ],
+    "ComposeV3Migration": [
+        {
+            "platforms": [
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ComposeV3Migration"
+                    ]
+                }
+            ]
+        }
+    ],
     "CompositeBackgroundColorAnimation": [
         {
             "platforms": [
@@ -8288,28 +8322,6 @@
             ]
         }
     ],
-    "DropInputEventsWhilePaintHolding": [
-        {
-            "platforms": [
-                "android",
-                "android_weblayer",
-                "android_webview",
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "DropInputEventsWhilePaintHolding"
-                    ]
-                }
-            ]
-        }
-    ],
     "DwaFeature": [
         {
             "platforms": [
diff --git a/third_party/angle b/third_party/angle
index 9b16a3e..db71e8f 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 9b16a3e925e7763e0c4b45807abcf65864688762
+Subproject commit db71e8fa7c26d18f76d7b9e9474447b20f1c73b3
diff --git a/third_party/blink/public/common/storage_key/OWNERS b/third_party/blink/public/common/storage_key/OWNERS
index 58b907f7..532f4f78 100644
--- a/third_party/blink/public/common/storage_key/OWNERS
+++ b/third_party/blink/public/common/storage_key/OWNERS
@@ -4,7 +4,7 @@
 # Secondary:
 arichiv@chromium.org
 bingler@chromium.org
-wanderview@chromium.org
+wanderview@meta.com
 
 per-file *_mojom_traits*.*=set noparent
 per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/renderer/core/clipboard/system_clipboard.cc b/third_party/blink/renderer/core/clipboard/system_clipboard.cc
index 1097114..df739b93 100644
--- a/third_party/blink/renderer/core/clipboard/system_clipboard.cc
+++ b/third_party/blink/renderer/core/clipboard/system_clipboard.cc
@@ -34,6 +34,7 @@
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/clipboard/clipboard_constants.h"
+#include "ui/base/ui_base_features.h"
 
 namespace blink {
 
@@ -63,7 +64,8 @@
 }  // namespace
 
 SystemClipboard::SystemClipboard(LocalFrame* frame)
-    : clipboard_(frame->DomWindow()) {
+    : clipboard_(frame->DomWindow()),
+      clipboard_listener_receiver_(this, frame->DomWindow()) {
   frame->GetBrowserInterfaceBroker().GetInterface(
       clipboard_.BindNewPipeAndPassReceiver(
           frame->GetTaskRunner(TaskType::kUserInteraction)));
@@ -443,7 +445,9 @@
 }
 
 void SystemClipboard::Trace(Visitor* visitor) const {
+  PlatformEventDispatcher::Trace(visitor);
   visitor->Trace(clipboard_);
+  visitor->Trace(clipboard_listener_receiver_);
 }
 
 bool SystemClipboard::IsValidBufferType(mojom::blink::ClipboardBuffer buffer) {
@@ -613,6 +617,31 @@
   custom_data_.Set(type, data);
 }
 
+void SystemClipboard::OnClipboardDataChanged() {
+  // If we're not listening (receiver not bound), don't notify controllers
+  if (!clipboard_listener_receiver_.is_bound()) {
+    return;
+  }
+  NotifyControllers();
+}
+
+void SystemClipboard::StartListening(LocalDOMWindow* window) {
+  if (!base::FeatureList::IsEnabled(features::kClipboardChangeEvent)) {
+    return;
+  }
+
+  // If we're already listening (receiver is bound), no need to register again
+  if (!clipboard_listener_receiver_.is_bound() && clipboard_.is_bound()) {
+    clipboard_->RegisterClipboardListener(
+        clipboard_listener_receiver_.BindNewPipeAndPassRemote(
+            window->GetTaskRunner(TaskType::kUserInteraction)));
+  }
+}
+
+void SystemClipboard::StopListening() {
+  clipboard_listener_receiver_.reset();
+}
+
 // static
 mojom::blink::ClipboardFilesPtr SystemClipboard::Snapshot::CloneFiles(
     mojom::blink::ClipboardFilesPtr& files) {
diff --git a/third_party/blink/renderer/core/clipboard/system_clipboard.h b/third_party/blink/renderer/core/clipboard/system_clipboard.h
index c1c7a83a4..bcd4a18d 100644
--- a/third_party/blink/renderer/core/clipboard/system_clipboard.h
+++ b/third_party/blink/renderer/core/clipboard/system_clipboard.h
@@ -10,7 +10,9 @@
 
 #include "third_party/blink/public/mojom/clipboard/clipboard.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/frame/platform_event_dispatcher.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h"
 #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
@@ -31,7 +33,9 @@
 //
 // All calls to write functions must be followed by a call to CommitWrite().
 class CORE_EXPORT SystemClipboard final
-    : public GarbageCollected<SystemClipboard> {
+    : public GarbageCollected<SystemClipboard>,
+      public PlatformEventDispatcher,
+      public mojom::blink::ClipboardListener {
  public:
   enum SmartReplaceOption { kCanSmartReplace, kCannotSmartReplace };
 
@@ -39,6 +43,13 @@
   SystemClipboard(const SystemClipboard&) = delete;
   SystemClipboard& operator=(const SystemClipboard&) = delete;
 
+  // Inherited from PlatformEventDispatcher.
+  void StartListening(LocalDOMWindow*) override;
+  void StopListening() override;
+
+  // Inherited from ClipboardListener.
+  void OnClipboardDataChanged() override;
+
   ClipboardSequenceNumberToken SequenceNumber();
   bool IsSelectionMode() const;
   void SetSelectionMode(bool);
@@ -99,7 +110,7 @@
   void WriteUnsanitizedCustomFormat(const String& type,
                                     mojo_base::BigBuffer data);
 
-  void Trace(Visitor*) const;
+  void Trace(Visitor*) const override;
 
  private:
   friend class ScopedSystemClipboardSnapshot;
@@ -194,6 +205,9 @@
   void DropSnapshot();
 
   HeapMojoRemote<mojom::blink::ClipboardHost> clipboard_;
+  HeapMojoReceiver<mojom::blink::ClipboardListener, SystemClipboard>
+      clipboard_listener_receiver_;
+
   // In some Linux environments, |buffer_| may equal ClipboardBuffer::kStandard
   // or kSelection.  In other platforms |buffer_| always equals
   // ClipboardBuffer::kStandard.
diff --git a/third_party/blink/renderer/core/clipboard/system_clipboard_test.cc b/third_party/blink/renderer/core/clipboard/system_clipboard_test.cc
index 406d116..185ee71d 100644
--- a/third_party/blink/renderer/core/clipboard/system_clipboard_test.cc
+++ b/third_party/blink/renderer/core/clipboard/system_clipboard_test.cc
@@ -7,14 +7,18 @@
 #include <memory>
 
 #include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/platform_event_controller.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -35,6 +39,24 @@
   return mojom::blink::ClipboardFiles::New(std::move(vec), "file_system_id");
 }
 
+// Mock event controller to verify change notifications
+class MockPlatformEventController
+    : public GarbageCollected<MockPlatformEventController>,
+      public PlatformEventController {
+ public:
+  explicit MockPlatformEventController(LocalDOMWindow& window)
+      : PlatformEventController(window) {}
+
+  MOCK_METHOD(void, DidUpdateData, (), (override));
+  MOCK_METHOD(void, RegisterWithDispatcher, (), (override));
+  MOCK_METHOD(void, UnregisterWithDispatcher, (), (override));
+  MOCK_METHOD(bool, HasLastData, (), (override));
+
+  void Trace(Visitor* visitor) const override {
+    PlatformEventController::Trace(visitor);
+  }
+};
+
 }  // namespace
 
 class SystemClipboardTest : public testing::Test {
@@ -44,6 +66,8 @@
     clipboard_provider_ =
         std::make_unique<PageTestBase::MockClipboardHostProvider>(
             page_holder_.get()->GetFrame().GetBrowserInterfaceBroker());
+    controller_ = MakeGarbageCollected<MockPlatformEventController>(
+        *page_holder_->GetFrame().DomWindow());
   }
 
  protected:
@@ -68,8 +92,42 @@
 
   void RunUntilIdle() { test::RunPendingTasks(); }
 
+  MockPlatformEventController* controller() { return controller_.Get(); }
+
+  LocalDOMWindow* dom_window() { return page_holder_->GetFrame().DomWindow(); }
+
+  // Helper to register controller, process events, and return a cleanup guard
+  class ScopedControllerRegistration {
+   public:
+    ScopedControllerRegistration(SystemClipboard* clipboard,
+                                 MockPlatformEventController* controller,
+                                 LocalDOMWindow* window)
+        : clipboard_(clipboard), controller_(controller) {
+      clipboard_->AddController(controller, window);
+      test::RunPendingTasks();
+    }
+
+    ~ScopedControllerRegistration() {
+      if (clipboard_ && controller_) {
+        clipboard_->RemoveController(controller_);
+        test::RunPendingTasks();
+      }
+    }
+
+   private:
+    WeakPersistent<SystemClipboard> clipboard_;
+    WeakPersistent<MockPlatformEventController> controller_;
+  };
+
+  // Helper to trigger clipboard change notification and wait for processing
+  void TriggerClipboardChangeAndWait() {
+    mock_clipboard_host()->OnClipboardDataChanged();
+    RunUntilIdle();
+  }
+
  private:
   test::TaskEnvironment task_environment;
+  Persistent<MockPlatformEventController> controller_;
 
   std::unique_ptr<DummyPageHolder> page_holder_;
   std::unique_ptr<PageTestBase::MockClipboardHostProvider> clipboard_provider_;
@@ -511,4 +569,47 @@
   auto sequence_number_after_reset = system_clipboard().SequenceNumber();
   EXPECT_NE(sequence_number_after_write, sequence_number_after_reset);
 }
+
+TEST_F(SystemClipboardTest, ClipboardChangeNotification) {
+  // GIVEN: Feature flag is enabled
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(features::kClipboardChangeEvent);
+  auto* mock_controller = controller();
+
+  // EXPECT: Controller should receive exactly one update notification
+  EXPECT_CALL(*mock_controller, DidUpdateData()).Times(1);
+
+  // WHEN: Controller is registered and clipboard data changes
+  {
+    ScopedControllerRegistration registration(&system_clipboard(),
+                                              mock_controller, dom_window());
+    TriggerClipboardChangeAndWait();
+  }
+  // Controller automatically unregistered when scope ends
+}
+
+TEST_F(SystemClipboardTest, ClipboardChangeNotification_MultipleRegistrations) {
+  // GIVEN: Feature flag is enabled
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(features::kClipboardChangeEvent);
+  auto* mock_controller = controller();
+
+  // EXPECT: Controller should receive notifications after each registration
+  EXPECT_CALL(*mock_controller, DidUpdateData()).Times(2);
+
+  // WHEN: First registration cycle
+  {
+    ScopedControllerRegistration registration(&system_clipboard(),
+                                              mock_controller, dom_window());
+    TriggerClipboardChangeAndWait();
+  }
+
+  // AND WHEN: Second registration cycle
+  {
+    ScopedControllerRegistration registration(&system_clipboard(),
+                                              mock_controller, dom_window());
+    TriggerClipboardChangeAndWait();
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 3e4576d..1e8a0d0 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -1612,7 +1612,7 @@
                                 /*include_event_handler_text=*/true,
                                 &GetDocument())) {
       popover->HidePopoverInternal(
-          HidePopoverFocusBehavior::kFocusPreviousElement,
+          /*invoker=*/this, HidePopoverFocusBehavior::kFocusPreviousElement,
           HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
           /*exception_state=*/nullptr);
     }
diff --git a/third_party/blink/renderer/core/editing/ime/cached_text_input_info.cc b/third_party/blink/renderer/core/editing/ime/cached_text_input_info.cc
index 21420fe..affabe78 100644
--- a/third_party/blink/renderer/core/editing/ime/cached_text_input_info.cc
+++ b/third_party/blink/renderer/core/editing/ime/cached_text_input_info.cc
@@ -133,12 +133,14 @@
 
   if (RuntimeEnabledFeatures::FastSelectionSyncEnabled()) {
     if (const auto* text_control = EnclosingTextControl(&container)) {
-      text_control->ComputeValueLengthAndUpdateOffsetMap(&offset_map_);
+      text_control->AnalyzeInnerEditorValue(&offset_map_);
       if (IsEditable(*container_)) {
         // We assume this function is called after `TextControlElement::
-        // SubtreeHasChanged()`. So we can avoid the slow InnerEditorValue().
-        text_ = text_control->EditingValue();
-        DCHECK(EqualIgnoringNullity(text_, text_control->InnerEditorValue()));
+        // SubtreeHasChanged()`. So we can avoid the slow
+        // SerializeInnerEditorValue().
+        text_ = text_control->InnerEditorValue();
+        DCHECK(EqualIgnoringNullity(text_,
+                                    text_control->SerializeInnerEditorValue()));
       }
       return;
     }
diff --git a/third_party/blink/renderer/core/events/event_type_names.json5 b/third_party/blink/renderer/core/events/event_type_names.json5
index a2187f4..43d78551 100644
--- a/third_party/blink/renderer/core/events/event_type_names.json5
+++ b/third_party/blink/renderer/core/events/event_type_names.json5
@@ -74,6 +74,7 @@
     "chargingtimechange",
     "checking",
     "click",
+    "clipboardchange",
     "close",
     "closing",
     "command",
diff --git a/third_party/blink/renderer/core/events/toggle_event.cc b/third_party/blink/renderer/core/events/toggle_event.cc
index 8d6940a..93c4804b 100644
--- a/third_party/blink/renderer/core/events/toggle_event.cc
+++ b/third_party/blink/renderer/core/events/toggle_event.cc
@@ -5,8 +5,11 @@
 #include "third_party/blink/renderer/core/events/toggle_event.h"
 
 #include "third_party/blink/renderer/bindings/core/v8/v8_toggle_event_init.h"
+#include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/event_interface_names.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 
@@ -15,10 +18,15 @@
 ToggleEvent::ToggleEvent(const AtomicString& type,
                          Event::Cancelable cancelable,
                          const String& old_state,
-                         const String& new_state)
+                         const String& new_state,
+                         Element* source)
     : Event(type, Bubbles::kNo, cancelable),
       old_state_(old_state),
-      new_state_(new_state) {
+      new_state_(new_state),
+      source_(source) {
+  if (!RuntimeEnabledFeatures::ToggleEventSourceEnabled()) {
+    source_ = nullptr;
+  }
   DCHECK(old_state == "closed" || old_state == "open")
       << " old_state should be \"closed\" or \"open\". Was: " << old_state;
   DCHECK(new_state == "closed" || new_state == "open")
@@ -46,12 +54,21 @@
   return new_state_;
 }
 
+Element* ToggleEvent::source() const {
+  CHECK(RuntimeEnabledFeatures::ToggleEventSourceEnabled());
+  if (source_ && currentTarget()) {
+    return &currentTarget()->ToNode()->GetTreeScope().Retarget(*source_);
+  }
+  return source_;
+}
+
 const AtomicString& ToggleEvent::InterfaceName() const {
   return event_interface_names::kToggleEvent;
 }
 
 void ToggleEvent::Trace(Visitor* visitor) const {
   Event::Trace(visitor);
+  visitor->Trace(source_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/events/toggle_event.h b/third_party/blink/renderer/core/events/toggle_event.h
index a5fa756..8560435 100644
--- a/third_party/blink/renderer/core/events/toggle_event.h
+++ b/third_party/blink/renderer/core/events/toggle_event.h
@@ -9,6 +9,7 @@
 
 namespace blink {
 
+class Element;
 class ToggleEventInit;
 
 class ToggleEvent final : public Event {
@@ -22,9 +23,10 @@
   static ToggleEvent* Create(const AtomicString& type,
                              Event::Cancelable cancelable,
                              const String& old_state,
-                             const String& new_state) {
+                             const String& new_state,
+                             Element* source) {
     auto* event = MakeGarbageCollected<ToggleEvent>(type, cancelable, old_state,
-                                                    new_state);
+                                                    new_state, source);
     DCHECK(!event->bubbles());
     return event;
   }
@@ -33,12 +35,14 @@
   ToggleEvent(const AtomicString& type,
               Event::Cancelable cancelable,
               const String& old_state,
-              const String& new_state);
+              const String& new_state,
+              Element* source);
   ToggleEvent(const AtomicString& type, const ToggleEventInit* initializer);
   ~ToggleEvent() override;
 
   const String& oldState() const;
   const String& newState() const;
+  Element* source() const;
 
   const AtomicString& InterfaceName() const override;
 
@@ -47,6 +51,7 @@
  private:
   String old_state_;
   String new_state_;
+  Member<Element> source_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/events/toggle_event.idl b/third_party/blink/renderer/core/events/toggle_event.idl
index af6e9770e..92086f18 100644
--- a/third_party/blink/renderer/core/events/toggle_event.idl
+++ b/third_party/blink/renderer/core/events/toggle_event.idl
@@ -8,4 +8,5 @@
     constructor(DOMString type, optional ToggleEventInit eventInitDict = {});
     readonly attribute DOMString oldState;
     readonly attribute DOMString newState;
+    [RuntimeEnabled=ToggleEventSource] readonly attribute Element source;
 };
diff --git a/third_party/blink/renderer/core/html/canvas/DEPS b/third_party/blink/renderer/core/html/canvas/DEPS
index ef890ea..dbdf98ed 100644
--- a/third_party/blink/renderer/core/html/canvas/DEPS
+++ b/third_party/blink/renderer/core/html/canvas/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+"+cc/layers/texture_layer_client.h",
 "+components/viz/common/resources/shared_image_format.h",
 "+components/viz/common/resources/shared_image_format_utils.h",
 ]
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 732c2a4..fa00d84 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -107,6 +107,7 @@
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_host.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/image_data_buffer.h"
@@ -131,6 +132,8 @@
 
 namespace {
 
+constexpr unsigned kMaxCanvasAnimationBacklog = 2;
+
 // These two constants determine if a newly created canvas starts with
 // acceleration disabled. Specifically:
 // 1. More than `kDisableAccelerationThreshold` canvases have been created.
@@ -259,6 +262,15 @@
   bool transfer_to_gpu_texture_was_invoked_ = false;
 };
 
+// Adapter for wrapping a CanvasResourceReleaseCallback into a
+// viz::ReleaseCallback
+void ReleaseCanvasResource(CanvasResource::ReleaseCallback callback,
+                           scoped_refptr<CanvasResource> canvas_resource,
+                           const gpu::SyncToken& sync_token,
+                           bool is_lost) {
+  std::move(callback).Run(std::move(canvas_resource), sync_token, is_lost);
+}
+
 void UmaHistogramCompressionRatio(
     std::string_view histogram_name,
     const String& data_url,
@@ -340,6 +352,72 @@
   }
 }
 
+bool HTMLCanvasElement::PrepareTransferableResource(
+    viz::TransferableResource* out_resource,
+    viz::ReleaseCallback* out_release_callback) {
+  CHECK(cc_layer_);  // This explodes if FinalizeFrame() was not called.
+
+  frames_since_last_commit_ = 0;
+  if (rate_limiter_) {
+    rate_limiter_->Reset();
+  }
+
+  // If hibernating but not hidden, we want to wake up from hibernation.
+  if (IsHibernating() && !IsPageVisible()) {
+    return false;
+  }
+
+  if (!IsResourceValid()) {
+    return false;
+  }
+
+  // The beforeprint event listener is sometimes scheduled in the same task
+  // as BeginFrame, which means that this code may sometimes be called between
+  // the event listener and its associated FinalizeFrame call. So in order to
+  // preserve the display list for printing, FlushRecording needs to know
+  // whether any printing occurred in the current task.
+  FlushReason reason = FlushReason::kCanvasPushFrame;
+  if (PrintedInCurrentTask() || IsPrinting()) {
+    reason = FlushReason::kCanvasPushFrameWhilePrinting;
+  }
+  FlushRecording(reason);
+
+  // If the context is lost, we don't know if we should be producing GPU or
+  // software frames, until we get a new context, since the compositor will
+  // be trying to get a new context and may change modes.
+  if (!GetOrCreateCanvasResourceProvider()) {
+    return false;
+  }
+
+  scoped_refptr<CanvasResource> frame =
+      ResourceProvider()->ProduceCanvasResource(reason);
+  if (!frame || !frame->IsValid()) {
+    return false;
+  }
+
+  CanvasResource::ReleaseCallback release_callback;
+  if (!frame->PrepareTransferableResource(out_resource, &release_callback,
+                                          /*needs_verified_synctoken=*/false) ||
+      *out_resource == cc_layer_->current_transferable_resource()) {
+    // If the resource did not change, the release will be handled correctly
+    // when the callback from the previous frame is dispatched. But run the
+    // |release_callback| to release the ref acquired above.
+    std::move(release_callback)
+        .Run(std::move(frame), gpu::SyncToken(), false /* is_lost */);
+    return false;
+  }
+  // TODO(https://crbug.com/1475955): HDR metadata should be propagated to
+  // `frame`, and should be populated by the above call to
+  // CanvasResource::PrepareTransferableResource, rather than be inserted
+  // here.
+  out_resource->hdr_metadata = hdr_metadata_;
+  // Note: frame is kept alive via a reference kept in out_release_callback.
+  *out_release_callback = base::BindOnce(
+      ReleaseCanvasResource, std::move(release_callback), std::move(frame));
+
+  return true;
+}
+
 void HTMLCanvasElement::Dispose() {
   disposing_ = true;
   // We need to record metrics before we dispose of anything
@@ -656,7 +734,7 @@
     NOTIMPLEMENTED();
   }
 
-  CanvasResourceHost::SetHdrMetadata(hdr_metadata);
+  hdr_metadata_ = hdr_metadata;
   if (context_ && (IsWebGL() || IsWebGPU())) {
     context_->SetHdrMetadata(hdr_metadata);
   }
@@ -1574,6 +1652,28 @@
   SetPreferred2DRasterMode(hint);
 }
 
+SharedContextRateLimiter* HTMLCanvasElement::RateLimiter() const {
+  return rate_limiter_.get();
+}
+
+void HTMLCanvasElement::CreateRateLimiter() {
+  rate_limiter_ =
+      std::make_unique<SharedContextRateLimiter>(kMaxCanvasAnimationBacklog);
+}
+
+void HTMLCanvasElement::SetIsDisplayed(bool displayed) {
+  is_displayed_ = displayed;
+  // If the canvas is no longer being displayed, stop using the rate
+  // limiter.
+  if (!is_displayed_) {
+    frames_since_last_commit_ = 0;
+    if (rate_limiter_) {
+      rate_limiter_->Reset();
+      rate_limiter_.reset(nullptr);
+    }
+  }
+}
+
 cc::TextureLayer* HTMLCanvasElement::GetOrCreateCcLayerIfNeeded() {
   if (!IsComposited()) {
     return nullptr;
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
index 4bfe7dc..9a4ba51 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
@@ -31,6 +31,7 @@
 #include <memory>
 
 #include "base/gtest_prod_util.h"
+#include "cc/layers/texture_layer_client.h"
 #include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
 #include "third_party/blink/public/common/privacy_budget/identifiable_token.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
@@ -71,6 +72,7 @@
 class HTMLCanvasElement;
 class ImageBitmapOptions;
 class StaticBitmapImageToVideoFrameCopier;
+class SharedContextRateLimiter;
 
 class
     CanvasRenderingContext2DOrWebGLRenderingContextOrWebGL2RenderingContextOrImageBitmapRenderingContextOrGPUCanvasContext;
@@ -87,6 +89,7 @@
       public ExecutionContextLifecycleObserver,
       public PageVisibilityObserver,
       public CanvasRenderingContextHost,
+      public cc::TextureLayerClient,
       public WebSurfaceLayerBridgeObserver,
       public OffscreenCanvasPlaceholder {
   DEFINE_WRAPPERTYPEINFO();
@@ -98,6 +101,11 @@
   explicit HTMLCanvasElement(Document&);
   ~HTMLCanvasElement() override;
 
+  // cc::TextureLayerClient implementation.
+  bool PrepareTransferableResource(
+      viz::TransferableResource* out_resource,
+      viz::ReleaseCallback* out_release_callback) override;
+
   // Attributes and functions exposed to script
   unsigned width() const { return Size().width(); }
   unsigned height() const { return Size().height(); }
@@ -168,6 +176,16 @@
 
   CanvasHibernationHandler* GetHibernationHandler() const;
 
+  unsigned IncrementFramesSinceLastCommit() {
+    return ++frames_since_last_commit_;
+  }
+
+  SharedContextRateLimiter* RateLimiter() const;
+  void CreateRateLimiter();
+
+  void SetIsDisplayed(bool);
+  bool IsDisplayed() const { return is_displayed_; }
+
   cc::TextureLayer* GetOrCreateCcLayerIfNeeded();
   cc::TextureLayer* GetCcLayerForTesting() { return cc_layer_.get(); }
   Canvas2DLayerBridge* GetOrCreateCanvas2DLayerBridge();
@@ -420,6 +438,10 @@
   bool ignore_reset_ = false;
   gfx::Rect dirty_rect_;
 
+  bool is_displayed_ = false;
+  unsigned frames_since_last_commit_ = 0;
+  std::unique_ptr<SharedContextRateLimiter> rate_limiter_;
+  gfx::HDRMetadata hdr_metadata_;
   bool origin_clean_;
   bool needs_unbuffered_input_ = false;
   bool style_is_visible_ = false;
diff --git a/third_party/blink/renderer/core/html/forms/html_text_area_element.cc b/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
index 044d8fb..88fa67e 100644
--- a/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
@@ -471,7 +471,7 @@
 }
 
 void HTMLTextAreaElement::UpdateValue() {
-  value_ = InnerEditorValue();
+  value_ = SerializeInnerEditorValue();
   NotifyFormStateChanged();
   is_dirty_ = true;
   UpdatePlaceholderVisibility();
@@ -732,7 +732,7 @@
   return Value().empty();
 }
 
-String HTMLTextAreaElement::EditingValue() const {
+String HTMLTextAreaElement::InnerEditorValue() const {
   return Value();
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/html_text_area_element.h b/third_party/blink/renderer/core/html/forms/html_text_area_element.h
index 4b3530c2..7faa5c6 100644
--- a/third_party/blink/renderer/core/html/forms/html_text_area_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_text_area_element.h
@@ -56,6 +56,7 @@
   String defaultValue() const;
   void setDefaultValue(const String&);
   int textLength() const { return Value().length(); }
+  String InnerEditorValue() const override;
 
   // Sets the suggested value and puts the element into
   // WebAutofillState::kPreviewed state if |value| is non-empty, or
@@ -100,7 +101,6 @@
   String GetPlaceholderValue() const final;
   HTMLElement* UpdatePlaceholderText() override;
   bool IsInnerEditorValueEmpty() const final;
-  String EditingValue() const override;
   void CreateInnerEditorElementIfNecessary() const final;
 
   bool IsOptionalFormControl() const override {
diff --git a/third_party/blink/renderer/core/html/forms/option_list.cc b/third_party/blink/renderer/core/html/forms/option_list.cc
index 49807dc0..4355e299 100644
--- a/third_party/blink/renderer/core/html/forms/option_list.cc
+++ b/third_party/blink/renderer/core/html/forms/option_list.cc
@@ -86,7 +86,7 @@
   Element* current;
   if (next) {
     DCHECK_EQ(next->OwnerSelectElement(), select_);
-    current = ElementTraversal::PreviousAbsoluteSibling(*next, &select_);
+    current = ElementTraversal::Previous(*next, &select_);
   } else {
     current = ElementTraversal::LastChild(select_);
   }
diff --git a/third_party/blink/renderer/core/html/forms/option_list_test.cc b/third_party/blink/renderer/core/html/forms/option_list_test.cc
index 1f7146a..fcc761b 100644
--- a/third_party/blink/renderer/core/html/forms/option_list_test.cc
+++ b/third_party/blink/renderer/core/html/forms/option_list_test.cc
@@ -122,4 +122,30 @@
   EXPECT_EQ("o1", Id(*it));
 }
 
+TEST_F(OptionListTest, RetreatWithOptgroups) {
+  Select().setInnerHTML(R"HTML(
+    <optgroup>
+      <option id=o1>one</option>
+    </optgroup>
+    <optgroup>
+      <option id=o2>two</option>
+    </optgroup>
+    <optgroup>
+      <option id=o3>three</option>
+    </optgroup>
+  )HTML");
+
+  OptionList list = Select().GetOptionList();
+  OptionList::Iterator it = list.begin();
+  EXPECT_EQ("o1", Id(*it));
+  ++it;
+  EXPECT_EQ("o2", Id(*it));
+  ++it;
+  EXPECT_EQ("o3", Id(*it));
+  --it;
+  EXPECT_EQ("o2", Id(*it));
+  --it;
+  EXPECT_EQ("o1", Id(*it));
+}
+
 }  // naemespace blink
diff --git a/third_party/blink/renderer/core/html/forms/select_type.cc b/third_party/blink/renderer/core/html/forms/select_type.cc
index bf19a88..8b07498 100644
--- a/third_party/blink/renderer/core/html/forms/select_type.cc
+++ b/third_party/blink/renderer/core/html/forms/select_type.cc
@@ -179,10 +179,11 @@
     }
   }
 
-  void HidePopoverInternal(HidePopoverFocusBehavior focus_behavior,
+  void HidePopoverInternal(Element* invoker,
+                           HidePopoverFocusBehavior focus_behavior,
                            HidePopoverTransitionBehavior event_firing,
                            ExceptionState* exception_state) override {
-    HTMLDivElement::HidePopoverInternal(focus_behavior, event_firing,
+    HTMLDivElement::HidePopoverInternal(invoker, focus_behavior, event_firing,
                                         exception_state);
     if (auto* select = ParentSelect()) {
       // Focus the select when the popover is hidden.
@@ -759,7 +760,7 @@
       // opening. Per spec, we close it in that case, to avoid circularity.
       PostChangingAppearanceConsoleWarning(*select_);
       popover_->HidePopoverInternal(
-          HidePopoverFocusBehavior::kNone,
+          /*invoker=*/nullptr, HidePopoverFocusBehavior::kNone,
           HidePopoverTransitionBehavior::kNoEventsNoWaiting,
           /*exception_state=*/nullptr);
     }
@@ -824,6 +825,7 @@
       popover_->popoverOpen()) {
     bool normal_behavior = behavior == SelectPopupHideBehavior::kNormal;
     popover_->HidePopoverInternal(
+        /*invoker=*/nullptr,
         normal_behavior ? HidePopoverFocusBehavior::kFocusPreviousElement
                         : HidePopoverFocusBehavior::kNone,
         normal_behavior
@@ -955,7 +957,7 @@
     } else {
       autofill_popover_text_->setInnerText(g_empty_string);
       autofill_popover_->HidePopoverInternal(
-          HidePopoverFocusBehavior::kNone,
+          /*invoker=*/nullptr, HidePopoverFocusBehavior::kNone,
           HidePopoverTransitionBehavior::kNoEventsNoWaiting,
           /*exception_state=*/nullptr);
     }
diff --git a/third_party/blink/renderer/core/html/forms/text_control_element.cc b/third_party/blink/renderer/core/html/forms/text_control_element.cc
index b8863b8..7af62f8 100644
--- a/third_party/blink/renderer/core/html/forms/text_control_element.cc
+++ b/third_party/blink/renderer/core/html/forms/text_control_element.cc
@@ -952,7 +952,7 @@
   if (!IsTextControl() || OpenShadowRoot())
     return;
 
-  bool text_is_changed = value != InnerEditorValue();
+  bool text_is_changed = value != SerializeInnerEditorValue();
   HTMLElement* inner_editor = EnsureInnerEditorElement();
   if (!text_is_changed && inner_editor->HasChildren())
     return;
@@ -1006,7 +1006,7 @@
   }
 }
 
-String TextControlElement::InnerEditorValue() const {
+String TextControlElement::SerializeInnerEditorValue() const {
   DCHECK(!OpenShadowRoot());
   HTMLElement* inner_editor = InnerEditorElement();
   if (!inner_editor || !IsTextControl())
@@ -1028,8 +1028,8 @@
   }
 
   if (RuntimeEnabledFeatures::TextareaLineEndingsAsBrEnabled()) {
-    auto [length, is_8bit] = ComputeValueLengthAndUpdateOffsetMap(nullptr);
-    return ComputeValue(length, is_8bit);
+    auto [length, is_8bit] = AnalyzeInnerEditorValue(nullptr);
+    return SerializeInnerEditorValueInternal(length, is_8bit);
   }
 
   StringBuilder result;
@@ -1052,8 +1052,7 @@
   return result.ToString();
 }
 
-std::pair<wtf_size_t, bool>
-TextControlElement::ComputeValueLengthAndUpdateOffsetMap(
+std::pair<wtf_size_t, bool> TextControlElement::AnalyzeInnerEditorValue(
     HeapHashMap<Member<const Text>, unsigned>* offset_map) const {
   const HTMLElement* inner_editor = InnerEditorElement();
   if (!inner_editor) {
@@ -1076,7 +1075,9 @@
   return {offset, is_8bit};
 }
 
-String TextControlElement::ComputeValue(wtf_size_t length, bool is_8bit) const {
+String TextControlElement::SerializeInnerEditorValueInternal(
+    wtf_size_t length,
+    bool is_8bit) const {
   if (length == 0u) {
     return g_empty_string;
   }
@@ -1118,8 +1119,8 @@
   return buffer.Release();
 }
 
-String TextControlElement::EditingValue() const {
-  return InnerEditorValue();
+String TextControlElement::InnerEditorValue() const {
+  return SerializeInnerEditorValue();
 }
 
 String TextControlElement::ValueWithHardLineBreaks() const {
diff --git a/third_party/blink/renderer/core/html/forms/text_control_element.h b/third_party/blink/renderer/core/html/forms/text_control_element.h
index 5225fd18..2a609bc 100644
--- a/third_party/blink/renderer/core/html/forms/text_control_element.h
+++ b/third_party/blink/renderer/core/html/forms/text_control_element.h
@@ -146,18 +146,17 @@
 
   virtual void SetInnerEditorValue(const String&);
   static void AppendTextOrBr(const String& value, ContainerNode& container);
-  // Serialize the user-visible editing text.
-  String InnerEditorValue() const;
-  // Returns the length of the value, and is_8bit flag.
-  // `offset_map` can be nullptr.
-  std::pair<wtf_size_t, bool> ComputeValueLengthAndUpdateOffsetMap(
-      HeapHashMap<Member<const Text>, unsigned>* offset_map) const;
-  // Returns the value string. `length` and `is_8bit` must be computed by
-  // ComputeValueLengthAndUpdateOffsetMap().
-  String ComputeValue(wtf_size_t length, bool is_8bit) const;
   // Returns the user-visible editing text.
-  // This should be faster than InnerEdtitorValue().
-  virtual String EditingValue() const;
+  // This cost should be O(1), and may be faster than
+  // SerializeInnerEdtitorValue().
+  virtual String InnerEditorValue() const;
+  // Serialize the user-visible editing text.
+  // This cost might be O(N) where N is the number of InnerEditor children.
+  String SerializeInnerEditorValue() const;
+  // Returns the length of the user-visible editing text, and its is_8bit flag
+  // without serializing the text. `offset_map` can be nullptr.
+  std::pair<wtf_size_t, bool> AnalyzeInnerEditorValue(
+      HeapHashMap<Member<const Text>, unsigned>* offset_map) const;
 
   Node* CreatePlaceholderBreakElement() const;
   // Returns true if the specified node was created by
@@ -213,6 +212,10 @@
   void CloneNonAttributePropertiesFrom(const Element&,
                                        NodeCloningData&) override;
 
+  // Returns the value string. `length` and `is_8bit` must be computed by
+  // AnalyzeInnerEditorValue().
+  String SerializeInnerEditorValueInternal(wtf_size_t length,
+                                           bool is_8bit) const;
   // Returns true if the inner-editor value is empty. This may be cheaper
   // than calling InnerEditorValue(), and InnerEditorValue() returns
   // the wrong thing if the editor hasn't been created yet.
diff --git a/third_party/blink/renderer/core/html/forms/text_control_element_test.cc b/third_party/blink/renderer/core/html/forms/text_control_element_test.cc
index 389b24c..151a04e 100644
--- a/third_party/blink/renderer/core/html/forms/text_control_element_test.cc
+++ b/third_party/blink/renderer/core/html/forms/text_control_element_test.cc
@@ -78,7 +78,9 @@
   EXPECT_EQ(0u, TextControl().selectionStart());
   EXPECT_EQ(0u, TextControl().selectionEnd());
 
-  TextControl().SetInnerEditorValue("Hello, text form.");
+  TextControl().SetValue("Hello, text form.",
+                         TextFieldEventBehavior::kDispatchNoEvent,
+                         TextControlSetValueSelection::kSetSelectionToStart);
   EXPECT_EQ(0u, TextControl().selectionStart());
   EXPECT_EQ(0u, TextControl().selectionEnd());
 
diff --git a/third_party/blink/renderer/core/html/html_details_element.cc b/third_party/blink/renderer/core/html/html_details_element.cc
index df48883..8fd0c63 100644
--- a/third_party/blink/renderer/core/html/html_details_element.cc
+++ b/third_party/blink/renderer/core/html/html_details_element.cc
@@ -176,7 +176,7 @@
     }
     pending_toggle_event_ =
         ToggleEvent::Create(event_type_names::kToggle, Event::Cancelable::kNo,
-                            old_state, new_state);
+                            old_state, new_state, /*source=*/nullptr);
     pending_event_task_ = PostCancellableTask(
         *GetDocument().GetTaskRunner(TaskType::kDOMManipulation), FROM_HERE,
         WTF::BindOnce(&HTMLDetailsElement::DispatchPendingEvent,
@@ -358,20 +358,31 @@
 
   if (command == CommandEventType::kToggle) {
     ToggleOpen();
-    return true;
   } else if (command == CommandEventType::kClose) {
     if (is_open_) {
       setAttribute(html_names::kOpenAttr, g_null_atom);
     }
-    return true;
   } else if (command == CommandEventType::kOpen) {
     if (!is_open_) {
       setAttribute(html_names::kOpenAttr, g_empty_atom);
     }
-    return true;
+  } else {
+    return false;
   }
 
-  return false;
+  if (RuntimeEnabledFeatures::ToggleEventSourceEnabled() &&
+      pending_toggle_event_) {
+    // pending_toggle_event_ is created inside the attribute handling code which
+    // we can't pass the invoker to, so we set it here instead.
+    pending_toggle_event_ = ToggleEvent::Create(
+        pending_toggle_event_->type(),
+        pending_toggle_event_->cancelable() ? Event::Cancelable::kYes
+                                            : Event::Cancelable::kNo,
+        pending_toggle_event_->oldState(), pending_toggle_event_->newState(),
+        &invoker);
+  }
+
+  return true;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_dialog_element.cc b/third_party/blink/renderer/core/html/html_dialog_element.cc
index 4f92113..4b40a48 100644
--- a/third_party/blink/renderer/core/html/html_dialog_element.cc
+++ b/third_party/blink/renderer/core/html/html_dialog_element.cc
@@ -144,6 +144,7 @@
 }
 
 void HTMLDialogElement::close(const String& return_value,
+                              Element* invoker,
                               bool open_attribute_being_removed) {
   DCHECK(!open_attribute_being_removed ||
          RuntimeEnabledFeatures::DialogCloseWhenOpenRemovedEnabled());
@@ -165,7 +166,7 @@
     Document& document = GetDocument();
     HTMLDialogElement* old_modal_dialog = document.ActiveModalDialog();
 
-    DispatchToggleEvents(/*opening=*/false);
+    DispatchToggleEvents(/*opening=*/false, invoker);
     if (!IsOpen() && !open_attribute_being_removed) {
       return;
     }
@@ -369,7 +370,7 @@
 
   if (command == CommandEventType::kClose) {
     if (open) {
-      close(return_value);
+      close(return_value, &invoker);
       return true;
     } else {
       AddConsoleMessage(
@@ -390,7 +391,7 @@
     }
   } else if (command == CommandEventType::kShowModal) {
     if (isConnected() && !open) {
-      showModal(ASSERT_NO_EXCEPTION);
+      showModal(ASSERT_NO_EXCEPTION, &invoker);
       return true;
     } else {
       AddConsoleMessage(
@@ -426,7 +427,7 @@
     return;
   }
 
-  if (!DispatchToggleEvents(/*opening=*/true)) {
+  if (!DispatchToggleEvents(/*opening=*/true, /*invoker=*/nullptr)) {
     return;
   }
   SetBooleanAttribute(html_names::kOpenAttr, true);
@@ -517,7 +518,8 @@
   close_watcher_->addEventListener(event_type_names::kCancel, event_listener);
 }
 
-void HTMLDialogElement::showModal(ExceptionState& exception_state) {
+void HTMLDialogElement::showModal(ExceptionState& exception_state,
+                                  Element* invoker) {
   if (IsOpen()) {
     if (!IsModal()) {
       exception_state.ThrowDOMException(
@@ -544,7 +546,7 @@
         DOMExceptionCode::kInvalidStateError,
         "Invalid for dialogs within documents that are not fully active.");
   }
-  if (!DispatchToggleEvents(/*opening=*/true, /*asModal=*/true)) {
+  if (!DispatchToggleEvents(/*opening=*/true, invoker, /*asModal=*/true)) {
     return;
   }
 
@@ -674,7 +676,9 @@
 
 // Returns false if beforetoggle was canceled, otherwise true. Queues a toggle
 // event if beforetoggle was not canceled.
-bool HTMLDialogElement::DispatchToggleEvents(bool opening, bool asModal) {
+bool HTMLDialogElement::DispatchToggleEvents(bool opening,
+                                             Element* source,
+                                             bool asModal) {
   if (!RuntimeEnabledFeatures::DialogElementToggleEventsEnabled()) {
     return true;
   }
@@ -685,7 +689,7 @@
   if (DispatchEvent(*ToggleEvent::Create(
           event_type_names::kBeforetoggle,
           opening ? Event::Cancelable::kYes : Event::Cancelable::kNo, old_state,
-          new_state)) != DispatchEventResult::kNotCanceled) {
+          new_state, source)) != DispatchEventResult::kNotCanceled) {
     return false;
   }
   if (opening) {
@@ -701,8 +705,9 @@
   if (pending_toggle_event_) {
     old_state = pending_toggle_event_->oldState();
   }
-  pending_toggle_event_ = ToggleEvent::Create(
-      event_type_names::kToggle, Event::Cancelable::kNo, old_state, new_state);
+  pending_toggle_event_ =
+      ToggleEvent::Create(event_type_names::kToggle, Event::Cancelable::kNo,
+                          old_state, new_state, source);
   pending_toggle_event_task_ = PostCancellableTask(
       *GetDocument().GetTaskRunner(TaskType::kDOMManipulation), FROM_HERE,
       WTF::BindOnce(&HTMLDialogElement::DispatchPendingToggleEvent,
@@ -751,7 +756,8 @@
             "The open attribute was removed from a dialog element while it was "
             "open. This is not recommended - some closing behaviors will not "
             "occur. Please close dialogs using dialog.close().");
-        close(/*return_value=*/String(), /*open_attribute_being_removed=*/true);
+        close(/*return_value=*/String(), /*invoker=*/nullptr,
+              /*open_attribute_being_removed=*/true);
       } else {
         DCHECK(GetDocument().AllOpenDialogs().Contains(this));
         GetDocument().AllOpenDialogs().erase(this);
diff --git a/third_party/blink/renderer/core/html/html_dialog_element.h b/third_party/blink/renderer/core/html/html_dialog_element.h
index cc56400..33dcc05 100644
--- a/third_party/blink/renderer/core/html/html_dialog_element.h
+++ b/third_party/blink/renderer/core/html/html_dialog_element.h
@@ -56,13 +56,14 @@
   // open_attribute_being_removed should only be true when `close()` is being
   // run from the attribute change steps for the `open` attribute.
   void close(const String& return_value = String(),
+             Element* invoker = nullptr,
              bool open_attribute_being_removed = false);
   void requestClose(ExceptionState& exception_state) {
     requestClose(String(), exception_state);
   }
   void requestClose(const String& return_value, ExceptionState&);
   void show(ExceptionState&);
-  void showModal(ExceptionState&);
+  void showModal(ExceptionState&, Element* invoker = nullptr);
   InsertionNotificationRequest InsertedInto(ContainerNode&) override;
   void RemovedFrom(ContainerNode&) override;
 
@@ -121,7 +122,9 @@
   void SetIsModal(bool is_modal);
   void ScheduleCloseEvent();
 
-  bool DispatchToggleEvents(bool opening, bool asModal = false);
+  bool DispatchToggleEvents(bool opening,
+                            Element* source,
+                            bool asModal = false);
   void DispatchPendingToggleEvent();
 
   bool is_modal_;
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index 7f881d96..efc854d 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -190,7 +190,7 @@
     // Don't do anything in response to cancel events, as per the HTML spec
     if (event->type() == event_type_names::kClose) {
       popover_->HidePopoverInternal(
-          HidePopoverFocusBehavior::kFocusPreviousElement,
+          /*invoker=*/nullptr, HidePopoverFocusBehavior::kFocusPreviousElement,
           HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
           /*exception_state=*/nullptr);
     }
@@ -1234,7 +1234,7 @@
     // If the popover type is changing, hide it.
     if (popoverOpen()) {
       HidePopoverInternal(
-          HidePopoverFocusBehavior::kFocusPreviousElement,
+          /*invoker=*/nullptr, HidePopoverFocusBehavior::kFocusPreviousElement,
           HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
           /*exception_state=*/nullptr);
       // Event handlers could have changed the popover, including by removing
@@ -1483,7 +1483,7 @@
   // Fire the "opening" beforetoggle event.
   auto* event = ToggleEvent::Create(
       event_type_names::kBeforetoggle, Event::Cancelable::kYes,
-      /*old_state*/ "closed", /*new_state*/ "open");
+      /*old_state*/ "closed", /*new_state*/ "open", invoker);
   CHECK(!event->bubbles());
   CHECK(event->cancelable());
   CHECK_EQ(event->oldState(), "closed");
@@ -1657,7 +1657,7 @@
   }
   after_event = ToggleEvent::Create(event_type_names::kToggle,
                                     Event::Cancelable::kNo, old_state,
-                                    /*new_state*/ "open");
+                                    /*new_state*/ "open", invoker);
   CHECK_EQ(after_event->newState(), "open");
   CHECK_EQ(after_event->oldState(), old_state);
   CHECK(!after_event->bubbles());
@@ -1692,8 +1692,9 @@
   while (!stack.empty()) {
     // TODO(masonf) If a popover's beforetoggle handler opens a new popover, it
     // is possible to get an infinite loop here. Need to break that loop.
-    stack.back()->HidePopoverInternal(focus_behavior, transition_behavior,
-                                      /*exception_state*/ nullptr);
+    stack.back()->HidePopoverInternal(
+        /*invoker=*/nullptr, focus_behavior, transition_behavior,
+        /*exception_state=*/nullptr);
   }
 }
 
@@ -1758,8 +1759,9 @@
       }
       while (last_to_hide && last_to_hide->popoverOpen()) {
         CHECK(!stack.empty());
-        stack.back()->HidePopoverInternal(focus_behavior, transition_behavior,
-                                          exception_state);
+        stack.back()->HidePopoverInternal(
+            /*invoker=*/nullptr, focus_behavior, transition_behavior,
+            exception_state);
       }
       // Now check if we're left with endpoint at the top of the stack.
       CHECK(!repeating_hide || stack.back() == endpoint);
@@ -1801,12 +1803,13 @@
 
 void HTMLElement::hidePopover(ExceptionState& exception_state) {
   HidePopoverInternal(
-      HidePopoverFocusBehavior::kFocusPreviousElement,
+      /*invoker=*/nullptr, HidePopoverFocusBehavior::kFocusPreviousElement,
       HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
       &exception_state);
 }
 
 void HTMLElement::HidePopoverInternal(
+    Element* invoker,
     HidePopoverFocusBehavior focus_behavior,
     HidePopoverTransitionBehavior transition_behavior,
     ExceptionState* exception_state) {
@@ -1859,7 +1862,7 @@
     // Fire the "closing" beforetoggle event.
     auto* event = ToggleEvent::Create(
         event_type_names::kBeforetoggle, Event::Cancelable::kNo,
-        /*old_state*/ "open", /*new_state*/ "closed");
+        /*old_state*/ "open", /*new_state*/ "closed", invoker);
     CHECK(!event->bubbles());
     CHECK(!event->cancelable());
     CHECK_EQ(event->oldState(), "open");
@@ -1907,7 +1910,7 @@
     }
     after_event = ToggleEvent::Create(event_type_names::kToggle,
                                       Event::Cancelable::kNo, old_state,
-                                      /*new_state*/ "closed");
+                                      /*new_state*/ "closed", invoker);
     CHECK_EQ(after_event->newState(), "closed");
     CHECK_EQ(after_event->oldState(), old_state);
     CHECK(!after_event->bubbles());
@@ -2370,7 +2373,7 @@
        command == CommandEventType::kHidePopover);
   if (can_hide) {
     HidePopoverInternal(
-        HidePopoverFocusBehavior::kFocusPreviousElement,
+        &invoker, HidePopoverFocusBehavior::kFocusPreviousElement,
         HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
         /*exception_state=*/nullptr);
     return true;
@@ -2660,9 +2663,10 @@
     bool was_in_document = insertion_point.isConnected();
     if (was_in_document) {
       // We can't run focus event handlers while removing elements.
-      HidePopoverInternal(HidePopoverFocusBehavior::kNone,
-                          HidePopoverTransitionBehavior::kNoEventsNoWaiting,
-                          /*exception_state=*/nullptr);
+      HidePopoverInternal(
+          /*invoker=*/nullptr, HidePopoverFocusBehavior::kNone,
+          HidePopoverTransitionBehavior::kNoEventsNoWaiting,
+          /*exception_state=*/nullptr);
     }
   }
 
diff --git a/third_party/blink/renderer/core/html/html_element.h b/third_party/blink/renderer/core/html/html_element.h
index aa52f54..20336f15 100644
--- a/third_party/blink/renderer/core/html/html_element.h
+++ b/third_party/blink/renderer/core/html/html_element.h
@@ -270,7 +270,8 @@
   // response to clicking a button with popovershowtarget.
   virtual void ShowPopoverInternal(Element* invoker,
                                    ExceptionState* exception_state);
-  virtual void HidePopoverInternal(HidePopoverFocusBehavior focus_behavior,
+  virtual void HidePopoverInternal(Element* invoker,
+                                   HidePopoverFocusBehavior focus_behavior,
                                    HidePopoverTransitionBehavior event_firing,
                                    ExceptionState* exception_state);
   void PopoverHideFinishIfNeeded(bool immediate);
diff --git a/third_party/blink/renderer/core/html/html_element_test.cc b/third_party/blink/renderer/core/html/html_element_test.cc
index e20dad17..cad46362 100644
--- a/third_party/blink/renderer/core/html/html_element_test.cc
+++ b/third_party/blink/renderer/core/html/html_element_test.cc
@@ -408,8 +408,9 @@
   // HidePopoverInternal causes :closed to match immediately, but schedules
   // the removal from the top layer.
   target->HidePopoverInternal(
-      HidePopoverFocusBehavior::kFocusPreviousElement,
-      HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions, nullptr);
+      /*invoker=*/nullptr, HidePopoverFocusBehavior::kFocusPreviousElement,
+      HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
+      /*exception_state*/ nullptr);
   EXPECT_FALSE(target->popoverOpen());
   EXPECT_TRUE(target->IsInTopLayer());
   UpdateAllLifecyclePhasesForTest();
diff --git a/third_party/blink/renderer/core/url_pattern/OWNERS b/third_party/blink/renderer/core/url_pattern/OWNERS
index be4adb6..657b6c2 100644
--- a/third_party/blink/renderer/core/url_pattern/OWNERS
+++ b/third_party/blink/renderer/core/url_pattern/OWNERS
@@ -3,4 +3,4 @@
 
 # secondary
 jbroman@chromium.org
-wanderview@chromium.org
+wanderview@meta.com
diff --git a/third_party/blink/renderer/core/workers/OWNERS b/third_party/blink/renderer/core/workers/OWNERS
index 6f6f7daf..86d6497 100644
--- a/third_party/blink/renderer/core/workers/OWNERS
+++ b/third_party/blink/renderer/core/workers/OWNERS
@@ -1,5 +1,5 @@
 hiroshige@chromium.org
 japhet@chromium.org
 nhiroki@chromium.org
-wanderview@chromium.org
+wanderview@meta.com
 yyanagisawa@chromium.org
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
index 161f454..46f427b 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -471,7 +471,7 @@
   validate_and_create_params.default_color_space =
       GetDefaultImageDataColorSpace();
 
-  if (isContextLost() || !CanCreateCanvas2dResourceProvider()) [[unlikely]] {
+  if (isContextLost()) {
     return ImageData::ValidateAndCreate(
         sw, sh, std::nullopt, image_data_settings, validate_and_create_params,
         exception_state);
@@ -564,6 +564,10 @@
         image_data_rect.bottom() > snapshot->Size().height()) {
       validate_and_create_params.zero_initialize = true;
     }
+  } else {
+    // If there's no snapshot, the buffer will not be overwritten and hence must
+    // be zero-initialized.
+    validate_and_create_params.zero_initialize = true;
   }
 
   ImageData* image_data =
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
index a87cee6..23735c5 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
@@ -782,7 +782,7 @@
     }
   }
 
-  CanvasRenderingContextHost* host = Host();
+  HTMLCanvasElement* host = canvas();
   CHECK(host);
 
   host->FlushRecording(reason);
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
index 4fd7404f..b9ca78a 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
@@ -2141,6 +2141,35 @@
   EXPECT_EQ(CanvasElement().GetRasterMode(), RasterMode::kGPU);
 }
 
+TEST_P(CanvasRenderingContext2DTestAccelerated,
+       GetImageDataDoesntEndHibernation) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
+
+  CreateContext(kNonOpaque);
+  CanvasElement().GetOrCreateCanvasResourceProvider();
+
+  auto& handler = CHECK_DEREF(CanvasElement().GetHibernationHandler());
+  ASSERT_FALSE(handler.IsHibernating());
+
+  GetDocument().GetPage()->SetVisibilityState(
+      mojom::blink::PageVisibilityState::kHidden,
+      /*is_initial_state=*/false);
+
+  // Run the task that initiates hibernation, which has been posted as an idle
+  // task.
+  ThreadScheduler::Current()
+      ->ToMainThreadScheduler()
+      ->StartIdlePeriodForTesting();
+  blink::test::RunPendingTasks();
+  ASSERT_TRUE(handler.IsHibernating());
+
+  NonThrowableExceptionState exception_state;
+  Context2D()->getImageData(0, 0, 1, 1, exception_state);
+
+  EXPECT_TRUE(handler.IsHibernating());
+}
+
 // https://crbug.com/708445: When the canvas hibernates or wakes up from
 // hibernation, the compositing reasons for the canvas element may change. In
 // these cases, the element should request a compositing update.
diff --git a/third_party/blink/renderer/modules/clipboard/BUILD.gn b/third_party/blink/renderer/modules/clipboard/BUILD.gn
index 11dee0f..b63ec51d 100644
--- a/third_party/blink/renderer/modules/clipboard/BUILD.gn
+++ b/third_party/blink/renderer/modules/clipboard/BUILD.gn
@@ -8,6 +8,8 @@
   sources = [
     "clipboard.cc",
     "clipboard.h",
+    "clipboard_change_event_controller.cc",
+    "clipboard_change_event_controller.h",
     "clipboard_item.cc",
     "clipboard_item.h",
     "clipboard_promise.cc",
@@ -27,6 +29,8 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "clipboard_change_event_controller_unittest.cc",
+    "clipboard_test_utils.h",
     "clipboard_unittest.cc",
     "mock_clipboard_permission_service.cc",
     "mock_clipboard_permission_service.h",
diff --git a/third_party/blink/renderer/modules/clipboard/DEPS b/third_party/blink/renderer/modules/clipboard/DEPS
index 4dc7b35..907ec5db 100644
--- a/third_party/blink/renderer/modules/clipboard/DEPS
+++ b/third_party/blink/renderer/modules/clipboard/DEPS
@@ -8,4 +8,5 @@
     "+third_party/blink/renderer/modules/clipboard",
     "+third_party/blink/renderer/modules/permissions",
     "+ui/base/clipboard",
+    "+ui/base/ui_base_features.h",
 ]
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard.cc b/third_party/blink/renderer/modules/clipboard/clipboard.cc
index c1192f9..d0079a0 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard.cc
+++ b/third_party/blink/renderer/modules/clipboard/clipboard.cc
@@ -8,11 +8,14 @@
 
 #include "net/base/mime_util.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/event_target_names.h"
+#include "third_party/blink/renderer/core/event_type_names.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/navigator.h"
 #include "third_party/blink/renderer/modules/clipboard/clipboard_promise.h"
 #include "ui/base/clipboard/clipboard_constants.h"
+#include "ui/base/ui_base_features.h"
 
 namespace blink {
 
@@ -58,6 +61,45 @@
                                              script_state, exception_state);
 }
 
+void Clipboard::AddedEventListener(
+    const AtomicString& event_type,
+    RegisteredEventListener& registered_listener) {
+  EventTarget::AddedEventListener(event_type, registered_listener);
+
+  if (!base::FeatureList::IsEnabled(features::kClipboardChangeEvent) ||
+      event_type != event_type_names::kClipboardchange) {
+    return;
+  }
+
+  if (!clipboard_change_event_controller_) {
+    Navigator& navigator = *GetSupplementable();
+    if (navigator.DomWindow()) {
+      clipboard_change_event_controller_ =
+          MakeGarbageCollected<ClipboardChangeEventController>(navigator, this);
+    }
+  }
+
+  if (clipboard_change_event_controller_) {
+    clipboard_change_event_controller_->RegisterWithDispatcher();
+  }
+}
+
+void Clipboard::RemovedEventListener(
+    const AtomicString& event_type,
+    const RegisteredEventListener& registered_listener) {
+  EventTarget::RemovedEventListener(event_type, registered_listener);
+
+  if (!base::FeatureList::IsEnabled(features::kClipboardChangeEvent) ||
+      event_type != event_type_names::kClipboardchange) {
+    return;
+  }
+
+  if (clipboard_change_event_controller_ &&
+      !HasEventListeners(event_type_names::kClipboardchange)) {
+    clipboard_change_event_controller_->UnregisterWithDispatcher();
+  }
+}
+
 ScriptPromise<IDLUndefined> Clipboard::write(
     ScriptState* script_state,
     const HeapVector<Member<ClipboardItem>>& data,
@@ -102,6 +144,7 @@
 void Clipboard::Trace(Visitor* visitor) const {
   EventTarget::Trace(visitor);
   Supplement<Navigator>::Trace(visitor);
+  visitor->Trace(clipboard_change_event_controller_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard.h b/third_party/blink/renderer/modules/clipboard/clipboard.h
index 7deb048..d8ecad45 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard.h
+++ b/third_party/blink/renderer/modules/clipboard/clipboard.h
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/fileapi/blob.h"
+#include "third_party/blink/renderer/modules/clipboard/clipboard_change_event_controller.h"
 #include "third_party/blink/renderer/modules/clipboard/clipboard_item.h"
 #include "third_party/blink/renderer/platform/supplementable.h"
 
@@ -44,6 +45,7 @@
   ScriptPromise<IDLUndefined> writeText(ScriptState*,
                                         const String&,
                                         ExceptionState&);
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(clipboardchange, kClipboardchange)
 
   // EventTarget
   const AtomicString& InterfaceName() const override;
@@ -55,6 +57,15 @@
   static String ParseWebCustomFormat(const String& format);
 
   void Trace(Visitor*) const override;
+
+  // EventTarget callbacks.
+  void AddedEventListener(const AtomicString& event_type,
+                          RegisteredEventListener&) override;
+  void RemovedEventListener(const AtomicString& event_type,
+                            const RegisteredEventListener&) override;
+
+ private:
+  Member<ClipboardChangeEventController> clipboard_change_event_controller_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard.idl b/third_party/blink/renderer/modules/clipboard/clipboard.idl
index 4689697..631a0e1 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard.idl
+++ b/third_party/blink/renderer/modules/clipboard/clipboard.idl
@@ -37,4 +37,6 @@
      CallWith=ScriptState,
      RaisesException
     ] Promise<undefined> writeText(DOMString data);
+
+    [RuntimeEnabled=ClipboardChangeEvent] attribute EventHandler onclipboardchange;
 };
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_change_event_controller.cc b/third_party/blink/renderer/modules/clipboard/clipboard_change_event_controller.cc
new file mode 100644
index 0000000..f79fb70d
--- /dev/null
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_change_event_controller.cc
@@ -0,0 +1,97 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/clipboard/clipboard_change_event_controller.h"
+
+#include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/event_type_names.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/frame/navigator.h"
+
+namespace blink {
+
+ClipboardChangeEventController::ClipboardChangeEventController(
+    Navigator& navigator,
+    EventTarget* event_target)
+    : Supplement<Navigator>(navigator),
+      PlatformEventController(*navigator.DomWindow()),
+      FocusChangedObserver(navigator.DomWindow()->GetFrame()->GetPage()),
+      event_target_(event_target) {}
+
+void ClipboardChangeEventController::FocusedFrameChanged() {
+  ExecutionContext* context = GetExecutionContext();
+  if (!context) {
+    return;
+  }
+  LocalDOMWindow& window = *To<LocalDOMWindow>(context);
+  if (window.document()->hasFocus()) {
+    if (fire_clipboardchange_on_focus_) {
+      OnClipboardChanged();
+    }
+  }
+}
+
+ExecutionContext* ClipboardChangeEventController::GetExecutionContext() const {
+  return GetSupplementable()->DomWindow();
+}
+
+void ClipboardChangeEventController::DidUpdateData() {
+  OnClipboardChanged();
+}
+
+bool ClipboardChangeEventController::HasLastData() {
+  return true;
+}
+
+void ClipboardChangeEventController::RegisterWithDispatcher() {
+  SystemClipboard* clipboard = GetSystemClipboard();
+  if (clipboard) {
+    clipboard->AddController(this, GetSupplementable()->DomWindow());
+  }
+}
+
+void ClipboardChangeEventController::UnregisterWithDispatcher() {
+  SystemClipboard* clipboard = GetSystemClipboard();
+  if (clipboard) {
+    clipboard->RemoveController(this);
+  }
+}
+
+SystemClipboard* ClipboardChangeEventController::GetSystemClipboard() const {
+  ExecutionContext* context = GetExecutionContext();
+  if (!context) {
+    return nullptr;
+  }
+  LocalFrame* local_frame = To<LocalDOMWindow>(context)->GetFrame();
+  return local_frame->GetSystemClipboard();
+}
+
+void ClipboardChangeEventController::Trace(Visitor* visitor) const {
+  Supplement<Navigator>::Trace(visitor);
+  PlatformEventController::Trace(visitor);
+  FocusChangedObserver::Trace(visitor);
+  visitor->Trace(event_target_);
+}
+
+void ClipboardChangeEventController::OnClipboardChanged() {
+  ExecutionContext* context = GetExecutionContext();
+  if (!context) {
+    return;
+  }
+  LocalDOMWindow& window = *To<LocalDOMWindow>(context);
+  CHECK(window.IsSecureContext());  // [SecureContext] in IDL
+
+  if (window.document()->hasFocus()) {
+    fire_clipboardchange_on_focus_ = false;
+    if (event_target_) {
+      event_target_->DispatchEvent(
+          *Event::Create(event_type_names::kClipboardchange));
+    }
+  } else {
+    // Schedule a clipboardchange event when the page regains focus
+    fire_clipboardchange_on_focus_ = true;
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_change_event_controller.h b/third_party/blink/renderer/modules/clipboard/clipboard_change_event_controller.h
new file mode 100644
index 0000000..f9a570b
--- /dev/null
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_change_event_controller.h
@@ -0,0 +1,60 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_CLIPBOARD_CLIPBOARD_CHANGE_EVENT_CONTROLLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_CLIPBOARD_CLIPBOARD_CHANGE_EVENT_CONTROLLER_H_
+
+#include "third_party/blink/renderer/core/clipboard/system_clipboard.h"
+#include "third_party/blink/renderer/core/dom/events/event_target.h"
+#include "third_party/blink/renderer/core/frame/navigator.h"
+#include "third_party/blink/renderer/core/frame/platform_event_controller.h"
+#include "third_party/blink/renderer/core/page/focus_changed_observer.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/supplementable.h"
+
+namespace blink {
+
+class MODULES_EXPORT ClipboardChangeEventController final
+    : public GarbageCollected<ClipboardChangeEventController>,
+      public Supplement<Navigator>,
+      public PlatformEventController,
+      public FocusChangedObserver {
+ public:
+  static const char kSupplementName[];
+  explicit ClipboardChangeEventController(Navigator& navigator,
+                                          EventTarget* eventTarget);
+
+  // FocusChangedObserver overrides.
+  void FocusedFrameChanged() override;
+
+  ClipboardChangeEventController(const ClipboardChangeEventController&) =
+      delete;
+  ClipboardChangeEventController& operator=(
+      const ClipboardChangeEventController&) = delete;
+
+  ExecutionContext* GetExecutionContext() const;
+
+  // PlatformEventController overrides.
+  void DidUpdateData() override;
+  void RegisterWithDispatcher() override;
+  void UnregisterWithDispatcher() override;
+  bool HasLastData() override;
+
+  void Trace(Visitor*) const override;
+
+ private:
+  // Fires the clipboardchange event after page focus check.
+  void OnClipboardChanged();
+
+  // Gets the SystemClipboard from the execution context.
+  SystemClipboard* GetSystemClipboard() const;
+
+  bool fire_clipboardchange_on_focus_ = false;
+  Member<EventTarget> event_target_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_CLIPBOARD_CLIPBOARD_CHANGE_EVENT_CONTROLLER_H_
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_change_event_controller_unittest.cc b/third_party/blink/renderer/modules/clipboard/clipboard_change_event_controller_unittest.cc
new file mode 100644
index 0000000..0358a72e
--- /dev/null
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_change_event_controller_unittest.cc
@@ -0,0 +1,87 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/clipboard/clipboard_change_event_controller.h"
+
+#include "third_party/blink/renderer/modules/clipboard/clipboard_test_utils.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+
+namespace blink {
+
+class ClipboardChangeEventTest : public ClipboardTestBase {};
+
+TEST_F(ClipboardChangeEventTest, ClipboardChangeEventFiresWhenFocused) {
+  ExecutionContext* executionContext = GetFrame().DomWindow();
+
+  // Clipboardchange event requires a secure origin and page in focus to work.
+  SetSecureOrigin(executionContext);
+  SetPageFocus(true);
+
+  auto* clipboard_change_event_handler =
+      MakeGarbageCollected<EventCountingListener>();
+  GetDocument().addEventListener(event_type_names::kClipboardchange,
+                                 clipboard_change_event_handler, false);
+  auto* clipboard_change_event_controller =
+      MakeGarbageCollected<ClipboardChangeEventController>(
+          *GetFrame().DomWindow()->navigator(), &GetDocument());
+
+  EXPECT_EQ(clipboard_change_event_handler->Count(), 0);
+
+  // Simulate a clipboard change event from the system clipboard.
+  clipboard_change_event_controller->DidUpdateData();
+
+  // Wait for any internal callbacks to run.
+  test::RunPendingTasks();
+
+  // Expect a single clipboardchange event to be fired.
+  EXPECT_EQ(clipboard_change_event_handler->Count(), 1);
+
+  // Clean up the event listener
+  GetDocument().removeEventListener(event_type_names::kClipboardchange,
+                                    clipboard_change_event_handler, false);
+}
+
+TEST_F(ClipboardChangeEventTest, ClipboardChangeEventNotFiredWhenNotFocused) {
+  ExecutionContext* executionContext = GetFrame().DomWindow();
+
+  SetSecureOrigin(executionContext);
+  SetPageFocus(false);
+
+  auto* clipboard_change_event_handler =
+      MakeGarbageCollected<EventCountingListener>();
+  GetDocument().addEventListener(event_type_names::kClipboardchange,
+                                 clipboard_change_event_handler, false);
+  auto* clipboard_change_event_controller =
+      MakeGarbageCollected<ClipboardChangeEventController>(
+          *GetFrame().DomWindow()->navigator(), &GetDocument());
+
+  EXPECT_EQ(clipboard_change_event_handler->Count(), 0);
+
+  // Simulate a clipboard change event from the system clipboard.
+  clipboard_change_event_controller->DidUpdateData();
+
+  // Wait for any internal callbacks to run.
+  test::RunPendingTasks();
+
+  // Expect no clipboardchange event to be fired since the page is not focused.
+  EXPECT_EQ(clipboard_change_event_handler->Count(), 0);
+
+  // Simulate more clipboard updates
+  clipboard_change_event_controller->DidUpdateData();
+  clipboard_change_event_controller->DidUpdateData();
+
+  // Focus back and wait for any internal callbacks to run.
+  SetPageFocus(true);
+  test::RunPendingTasks();
+
+  // Expect a single clipboardchange event to be fired
+  // now that the page is focused.
+  EXPECT_EQ(clipboard_change_event_handler->Count(), 1);
+
+  // Clean up the event listener
+  GetDocument().removeEventListener(event_type_names::kClipboardchange,
+                                    clipboard_change_event_handler, false);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_test_utils.h b/third_party/blink/renderer/modules/clipboard/clipboard_test_utils.h
new file mode 100644
index 0000000..1de448a
--- /dev/null
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_test_utils.h
@@ -0,0 +1,54 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_CLIPBOARD_CLIPBOARD_TEST_UTILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_CLIPBOARD_CLIPBOARD_TEST_UTILS_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
+#include "third_party/blink/renderer/core/clipboard/system_clipboard.h"
+#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/page/focus_controller.h"
+#include "third_party/blink/renderer/core/testing/page_test_base.h"
+
+namespace blink {
+
+// This is a helper class which provides utility methods
+// for testing the Async Clipboard API.
+class ClipboardTestBase : public PageTestBase {
+ public:
+  void SetPageFocus(bool focused) {
+    GetPage().GetFocusController().SetActive(focused);
+    GetPage().GetFocusController().SetFocused(focused);
+  }
+
+  void SetSecureOrigin(ExecutionContext* executionContext) {
+    KURL page_url("https://example.com");
+    scoped_refptr<SecurityOrigin> page_origin =
+        SecurityOrigin::Create(page_url);
+    executionContext->GetSecurityContext().SetSecurityOriginForTesting(nullptr);
+    executionContext->GetSecurityContext().SetSecurityOrigin(page_origin);
+  }
+
+  void WritePlainTextToClipboard(const String& text, V8TestingScope& scope) {
+    scope.GetFrame().GetSystemClipboard()->WritePlainText(text);
+  }
+};
+
+class EventCountingListener final : public NativeEventListener {
+ public:
+  void Invoke(ExecutionContext*, Event* event) override { count_++; }
+
+  int Count() const { return count_; }
+
+ private:
+  int count_ = 0;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_CLIPBOARD_CLIPBOARD_TEST_UTILS_H_
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc
index 7b798c8..068f2ef 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc
@@ -139,9 +139,10 @@
     if (event_listener_)
       event_listener_->StopListening();
     if (popoverOpen()) {
-      HidePopoverInternal(HidePopoverFocusBehavior::kNone,
-                          HidePopoverTransitionBehavior::kNoEventsNoWaiting,
-                          nullptr);
+      HidePopoverInternal(
+          /*invoker=*/nullptr, HidePopoverFocusBehavior::kNone,
+          HidePopoverTransitionBehavior::kNoEventsNoWaiting,
+          /*exception_state=*/nullptr);
     }
   }
 }
diff --git a/third_party/blink/renderer/modules/ml/ml_context.cc b/third_party/blink/renderer/modules/ml/ml_context.cc
index b386bec..aff31ec 100644
--- a/third_party/blink/renderer/modules/ml/ml_context.cc
+++ b/third_party/blink/renderer/modules/ml/ml_context.cc
@@ -1025,7 +1025,7 @@
   //
   // This assertion protects against the usage flags changing without updating
   // this mapping.
-  static_assert(base::to_underlying(webnn::MLTensorUsageFlags::kMaxValue) == 3);
+  static_assert(base::to_underlying(webnn::MLTensorUsageFlags::kMaxValue) == 2);
   webnn::MLTensorUsage usage;
   if (descriptor->exportableToGPU()) {
     usage.Put(webnn::MLTensorUsageFlags::kWebGpuInterop);
@@ -1037,9 +1037,6 @@
     usage.Put(webnn::MLTensorUsageFlags::kWrite);
   }
 
-  // MLTensorUsageFlags::kGraphConstant is only assigned for
-  // createConstantTensor().
-
   auto tensor_info =
       webnn::mojom::blink::TensorInfo::New(validated_descriptor, usage);
 
@@ -1049,84 +1046,7 @@
 
   // Use `WebNNContext` to create `WebNNTensor` message pipe.
   context_remote_->CreateTensor(
-      std::move(tensor_info), mojo_base::BigBuffer(0),
-      WTF::BindOnce(&MLContext::DidCreateWebNNTensor, WrapPersistent(this),
-                    std::move(scoped_trace), WrapPersistent(resolver),
-                    std::move(validated_descriptor), usage));
-
-  return resolver->Promise();
-}
-
-ScriptPromise<MLTensor> MLContext::createConstantTensor(
-    ScriptState* script_state,
-    const MLOperandDescriptor* descriptor,
-    AllowSharedBufferSource* src_data,
-    ExceptionState& exception_state) {
-  webnn::ScopedTrace scoped_trace("MLContext::createConstantTensor");
-  if (!script_state->ContextIsValid()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "Invalid script state");
-    return EmptyPromise();
-  }
-
-  if (!base::FeatureList::IsEnabled(
-          webnn::mojom::features::kWebMachineLearningNeuralNetwork)) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
-                                      "Not implemented");
-    return EmptyPromise();
-  }
-
-  if (!context_remote_.is_bound()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "Context is lost.");
-    return EmptyPromise();
-  }
-
-  ASSIGN_OR_RETURN(
-      webnn::OperandDescriptor validated_descriptor,
-      webnn::OperandDescriptor::Create(
-          properties_, FromBlinkDataType(descriptor->dataType().AsEnum()),
-          descriptor->shape(), "constant_tensor"),
-      [&exception_state](std::string error) {
-        exception_state.ThrowTypeError(String(error));
-        return ScriptPromise<MLTensor>();
-      });
-
-  RETURN_IF_ERROR(webnn::ValidateTensor(properties_, validated_descriptor),
-                  [&exception_state](std::string error) {
-                    exception_state.ThrowTypeError(String(error));
-                    return ScriptPromise<MLTensor>();
-                  });
-
-  base::span<const uint8_t> bytes = AsByteSpan(*src_data);
-  if (validated_descriptor.PackedByteLength() != bytes.size()) {
-    exception_state.ThrowTypeError(
-        String::Format("The source data byte length (%zu) doesn't match the "
-                       "expected byte length (%zu).",
-                       bytes.size(), validated_descriptor.PackedByteLength()));
-    return ScriptPromise<MLTensor>();
-  }
-
-  if (!properties_.data_type_limits.constant.Has(
-          validated_descriptor.data_type())) {
-    exception_state.ThrowTypeError(String(webnn::NotSupportedConstantTypeError(
-        validated_descriptor.data_type(),
-        properties_.data_type_limits.constant)));
-    return ScriptPromise<MLTensor>();
-  }
-
-  webnn::MLTensorUsage usage =
-      webnn::MLTensorUsage{webnn::MLTensorUsageFlags::kGraphConstant};
-  auto tensor_info =
-      webnn::mojom::blink::TensorInfo::New(validated_descriptor, usage);
-
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver<MLTensor>>(
-      script_state, exception_state.GetContext());
-  pending_resolvers_.insert(resolver);
-
-  // Use `WebNNContext` to create `WebNNTensor` message pipe.
-  context_remote_->CreateTensor(
-      std::move(tensor_info), bytes,
+      std::move(tensor_info),
       WTF::BindOnce(&MLContext::DidCreateWebNNTensor, WrapPersistent(this),
                     std::move(scoped_trace), WrapPersistent(resolver),
                     std::move(validated_descriptor), usage));
diff --git a/third_party/blink/renderer/modules/ml/ml_context.h b/third_party/blink/renderer/modules/ml/ml_context.h
index 875d1a7..20be025 100644
--- a/third_party/blink/renderer/modules/ml/ml_context.h
+++ b/third_party/blink/renderer/modules/ml/ml_context.h
@@ -77,12 +77,6 @@
                                        const MLTensorDescriptor* descriptor,
                                        ExceptionState& exception_state);
 
-  ScriptPromise<MLTensor> createConstantTensor(
-      ScriptState* script_state,
-      const MLOperandDescriptor* descriptor,
-      AllowSharedBufferSource* src_data,
-      ExceptionState& exception_state);
-
   void writeTensor(ScriptState* script_state,
                    MLTensor* dst_tensor,
                    AllowSharedBufferSource* src_data,
diff --git a/third_party/blink/renderer/modules/ml/ml_context.idl b/third_party/blink/renderer/modules/ml/ml_context.idl
index 0649d86..0dcdf6eb 100644
--- a/third_party/blink/renderer/modules/ml/ml_context.idl
+++ b/third_party/blink/renderer/modules/ml/ml_context.idl
@@ -303,14 +303,6 @@
     RuntimeEnabled=MachineLearningNeuralNetwork,
     CallWith=ScriptState,
     RaisesException
-  ] Promise<MLTensor> createConstantTensor(
-        MLOperandDescriptor descriptor,
-        AllowSharedBufferSource sourceData);
-
-  [
-    RuntimeEnabled=MachineLearningNeuralNetwork,
-    CallWith=ScriptState,
-    RaisesException
   ] void writeTensor(MLTensor dstTensor, AllowSharedBufferSource srcData);
 
   [
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_constant_operand.cc b/third_party/blink/renderer/modules/ml/webnn/ml_constant_operand.cc
index ae545e6..ca734e1 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_constant_operand.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_constant_operand.cc
@@ -7,7 +7,6 @@
 #include "services/webnn/public/mojom/webnn_graph.mojom-blink.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_operand.h"
-#include "third_party/blink/renderer/modules/ml/webnn/ml_tensor.h"
 
 namespace blink {
 
@@ -17,16 +16,9 @@
                 webnn::mojom::blink::Operand::Kind::kConstant,
                 std::move(descriptor)) {}
 
-MLConstantOperand::MLConstantOperand(MLGraphBuilder* builder, MLTensor* tensor)
-    : MLOperand(builder,
-                webnn::mojom::blink::Operand::Kind::kConstant,
-                tensor->Descriptor()),
-      tensor_(tensor) {}
-
 MLConstantOperand::~MLConstantOperand() = default;
 
 void MLConstantOperand::Trace(Visitor* visitor) const {
-  visitor->Trace(tensor_);
   MLOperand::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_constant_operand.h b/third_party/blink/renderer/modules/ml/webnn/ml_constant_operand.h
index 5544010..c17f0fe1 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_constant_operand.h
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_constant_operand.h
@@ -14,7 +14,6 @@
 namespace blink {
 
 class MLGraphBuilder;
-class MLTensor;
 
 // Represents an `MLOperand` created from the `MLGraphBuilder.constant()`
 // method. See https://www.w3.org/TR/webnn/#api-mlgraphbuilder-constant.
@@ -27,9 +26,6 @@
   MLConstantOperand(MLGraphBuilder* builder,
                     webnn::OperandDescriptor descriptor);
 
-  // Similar to above but uses a tensor for weight data.
-  MLConstantOperand(MLGraphBuilder* builder, MLTensor* tensor);
-
   MLConstantOperand(const MLConstantOperand&) = delete;
   MLConstantOperand& operator=(const MLConstantOperand&) = delete;
 
@@ -39,13 +35,9 @@
 
   const WebNNPendingConstantToken& handle() const { return handle_; }
 
-  const MLTensor* tensor() const { return tensor_; }
-
  private:
   // Identifies this constant operand in the WebNN service.
   const WebNNPendingConstantToken handle_;
-
-  Member<MLTensor> tensor_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph.cc
index 0bbe3ec..a1d29d0e 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph.cc
@@ -186,11 +186,6 @@
       return;
     }
 
-    if (input_tensor->Usage().Has(webnn::MLTensorUsageFlags::kGraphConstant)) {
-      exception_state.ThrowTypeError("Invalid input tensor usage");
-      return;
-    }
-
     mojo_inputs.insert(name, input_tensor->handle());
   }
 
@@ -202,11 +197,6 @@
       return;
     }
 
-    if (output_tensor->Usage().Has(webnn::MLTensorUsageFlags::kGraphConstant)) {
-      exception_state.ThrowTypeError("Invalid output tensor usage");
-      return;
-    }
-
     mojo_outputs.insert(name, output_tensor->handle());
   }
 
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc
index 62cf744..a012d86f 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc
@@ -69,7 +69,6 @@
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph_utils.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_operand.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_operator.h"
-#include "third_party/blink/renderer/modules/ml/webnn/ml_tensor.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
@@ -1470,16 +1469,8 @@
           webnn::OperandId operand_id = AddOperand(
               *graph_info,
               mojo::ConvertTo<blink_mojom::OperandPtr>(operand.Get()));
-          // Build the map of constant operands for this graph with the id.
-          MLConstantOperand const* constant_operand =
-              operand->AsConstantOperand();
-          if (constant_operand->tensor()) {
-            graph_info->id_to_constant_tensor_operand_map.insert(
-                operand_id, constant_operand->tensor()->handle());
-          } else {
-            graph_info->constant_operand_ids_to_handles.insert(
-                operand_id, operand->AsConstantOperand()->handle());
-          }
+          graph_info->constant_operand_ids_to_handles.insert(
+              operand_id, operand->AsConstantOperand()->handle());
           operand_to_id_map.insert(operand, operand_id);
           break;
         }
@@ -1731,33 +1722,6 @@
   return constant;
 }
 
-MLOperand* MLGraphBuilder::constant(ScriptState* script_state,
-                                    MLTensor* tensor,
-                                    ExceptionState& exception_state) {
-  THROW_AND_RETURN_IF_ERROR(ValidateGraphBuilderState(), nullptr);
-
-  if (tensor->context() != ml_context_) {
-    exception_state.ThrowTypeError(
-        "The tensor wasn't created with this context.");
-    return nullptr;
-  }
-
-  if (!tensor->IsValid()) {
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kInvalidStateError,
-        "Tensor has been destroyed or context is lost.");
-    return nullptr;
-  }
-
-  if (!tensor->Usage().Has(webnn::MLTensorUsageFlags::kGraphConstant)) {
-    exception_state.ThrowTypeError(
-        "Tensor was not created by createConstantTensor.");
-    return nullptr;
-  }
-
-  return MakeGarbageCollected<MLConstantOperand>(this, tensor);
-}
-
 MLOperand* MLGraphBuilder::argMin(MLOperand* input,
                                   const uint32_t axis,
                                   const MLArgMinMaxOptions* options,
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.h b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.h
index f91215e5..bbdee03 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.h
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.h
@@ -54,7 +54,6 @@
 class MLScatterOptions;
 class MLSliceOptions;
 class MLSplitOptions;
-class MLTensor;
 class MLTransposeOptions;
 class MLTriangularOptions;
 class MLOperand;
@@ -102,9 +101,6 @@
                       const MLOperandDescriptor* desc,
                       AllowSharedBufferSource* buffer,
                       ExceptionState& exception_state);
-  MLOperand* constant(ScriptState* script_state,
-                      MLTensor* tensor,
-                      ExceptionState& exception_state);
 
   // The order of operations declaration is the same as spec.
   MLOperand* argMin(MLOperand* input,
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.idl b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.idl
index e16d732..509880c 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.idl
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.idl
@@ -241,11 +241,6 @@
     RaisesException
   ] MLOperand constant(MLOperandDescriptor desc, AllowSharedBufferSource buffer);
 
-  [
-    CallWith=ScriptState,
-    RaisesException
-  ] MLOperand constant(MLTensor tensor);
-
   [RaisesException] MLOperand argMin(MLOperand input, [EnforceRange] unsigned long axis, optional MLArgMinMaxOptions options = {});
   [RaisesException] MLOperand argMax(MLOperand input, [EnforceRange] unsigned long axis, optional MLArgMinMaxOptions options = {});
 
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_test.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_test.cc
index 679839f3..3b4e2c3 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_test.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_test.cc
@@ -499,7 +499,6 @@
   }
 
   void CreateTensor(blink_mojom::TensorInfoPtr tensor_info,
-                    mojo_base::BigBuffer tensor_data,
                     CreateTensorCallback callback) override {
     mojo::PendingAssociatedRemote<blink_mojom::WebNNTensor> blink_remote;
     auto blink_receiver = blink_remote.InitWithNewEndpointAndPassReceiver();
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_tensor.cc b/third_party/blink/renderer/modules/ml/webnn/ml_tensor.cc
index ca7eb7de..b3023fd3 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_tensor.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_tensor.cc
@@ -76,10 +76,6 @@
   return usage_.Has(webnn::MLTensorUsageFlags::kWrite);
 }
 
-bool MLTensor::constant() const {
-  return usage_.Has(webnn::MLTensorUsageFlags::kGraphConstant);
-}
-
 void MLTensor::destroy() {
   // Calling OnConnectionError() will disconnect and destroy the tensor in
   // the service. The remote tensor must remain unbound after calling
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_tensor.h b/third_party/blink/renderer/modules/ml/webnn/ml_tensor.h
index b6e8a68..976b04d5 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_tensor.h
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_tensor.h
@@ -60,7 +60,6 @@
   bool exportableToGPU() const;
   bool readable() const;
   bool writable() const;
-  bool constant() const;
 
   void destroy();
 
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_tensor.idl b/third_party/blink/renderer/modules/ml/webnn/ml_tensor.idl
index 817b8e03..c86b419 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_tensor.idl
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_tensor.idl
@@ -13,7 +13,6 @@
   readonly attribute boolean exportableToGPU;
   readonly attribute boolean readable;
   readonly attribute boolean writable;
-  readonly attribute boolean constant;
 
   void destroy();
 };
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.cc b/third_party/blink/renderer/modules/webaudio/audio_context.cc
index e27966f..e812d3e 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_context.cc
@@ -1448,6 +1448,7 @@
   // https://webaudio.github.io/web-audio-api/#error-handling-on-a-running-audio-context
   switch (ContextState()) {
     case V8AudioContextState::Enum::kRunning:
+    case V8AudioContextState::Enum::kInterrupted:
       // TODO(https://crbug.com/353641602): starting or stopping the renderer
       // should happen on the render thread, but this is the current convention.
       destination()->GetAudioDestinationHandler().StopRendering();
@@ -1460,7 +1461,6 @@
       DispatchEvent(*Event::Create(event_type_names::kError));
       return;
     case V8AudioContextState::Enum::kClosed:
-    case V8AudioContextState::Enum::kInterrupted:
       return;
   }
   NOTREACHED();
diff --git a/third_party/blink/renderer/platform/audio/audio_destination.cc b/third_party/blink/renderer/platform/audio/audio_destination.cc
index 6843b9d..95815bc 100644
--- a/third_party/blink/renderer/platform/audio/audio_destination.cc
+++ b/third_party/blink/renderer/platform/audio/audio_destination.cc
@@ -599,7 +599,7 @@
   // FIFO contains audio at the output device sample rate.
   base::TimeDelta fifo_delay = audio_utilities::FramesToTime(
       fifo_->GetFramesAvailable(), web_audio_device_->SampleRate());
-  uma_reporter_.UpdateFifoDelay(fifo_delay);
+  uma_reporter_.AddFifoDelay(fifo_delay);
   if (has_fifo_underrun_occurred) {
     uma_reporter_.IncreaseFifoUnderrunCount();
   }
@@ -668,7 +668,7 @@
 
 void AudioDestination::PullFromCallback(AudioBus* destination_bus,
                                         base::TimeDelta delay) {
-  uma_reporter_.UpdateTotalPlayoutDelay(delay);
+  uma_reporter_.AddTotalPlayoutDelay(delay);
   callback_->Render(destination_bus, render_quantum_frames_, output_position_,
                     metric_reporter_.GetMetric(), delay,
                     glitch_info_to_report_.GetAndReset());
diff --git a/third_party/blink/renderer/platform/audio/audio_destination_uma_reporter.cc b/third_party/blink/renderer/platform/audio/audio_destination_uma_reporter.cc
index cb3cb69..3d77413 100644
--- a/third_party/blink/renderer/platform/audio/audio_destination_uma_reporter.cc
+++ b/third_party/blink/renderer/platform/audio/audio_destination_uma_reporter.cc
@@ -92,13 +92,13 @@
   }
 }
 
-void AudioDestinationUmaReporter::UpdateFifoDelay(base::TimeDelta fifo_delay) {
-  fifo_delay_ = fifo_delay;
+void AudioDestinationUmaReporter::AddFifoDelay(base::TimeDelta fifo_delay) {
+  fifo_delay_sum_ += fifo_delay;
 }
 
-void AudioDestinationUmaReporter::UpdateTotalPlayoutDelay(
+void AudioDestinationUmaReporter::AddTotalPlayoutDelay(
     base::TimeDelta total_playout_delay) {
-  total_playout_delay_ = total_playout_delay;
+  total_playout_delay_sum_ += total_playout_delay;
 }
 
 void AudioDestinationUmaReporter::IncreaseFifoUnderrunCount() {
@@ -106,18 +106,26 @@
 }
 
 void AudioDestinationUmaReporter::Report() {
-  base::UmaHistogramCustomCounts(fifo_delay_histogram_name_,
-                                 fifo_delay_.InMilliseconds(), 1, 1000, 50);
-  base::UmaHistogramCustomCounts(fifo_delay_histogram_name_with_latency_tag_,
-                                 fifo_delay_.InMilliseconds(), 1, 1000, 50);
-
-  base::UmaHistogramCustomCounts(total_playout_delay_histogram_name_,
-                                 total_playout_delay_.InMilliseconds(), 1, 1000,
-                                 50);
-  base::UmaHistogramCustomCounts(
-      total_playout_delay_histogram_name_with_latency_tag_,
-      total_playout_delay_.InMilliseconds(), 1, 1000, 50);
   if (++callback_count_ >= kMetricsReportCycle) {
+    base::UmaHistogramCustomCounts(
+        fifo_delay_histogram_name_,
+        fifo_delay_sum_.InMilliseconds() / kMetricsReportCycle, 1, 1000, 50);
+    base::UmaHistogramCustomCounts(
+        fifo_delay_histogram_name_with_latency_tag_,
+        fifo_delay_sum_.InMilliseconds() / kMetricsReportCycle, 1, 1000, 50);
+
+    base::UmaHistogramCustomCounts(
+        total_playout_delay_histogram_name_,
+        total_playout_delay_sum_.InMilliseconds() / kMetricsReportCycle, 1,
+        1000, 50);
+    base::UmaHistogramCustomCounts(
+        total_playout_delay_histogram_name_with_latency_tag_,
+        total_playout_delay_sum_.InMilliseconds() / kMetricsReportCycle, 1,
+        1000, 50);
+
+    fifo_delay_sum_ = base::TimeDelta();
+    total_playout_delay_sum_ = base::TimeDelta();
+
     base::UmaHistogramCustomCounts(fifo_underrun_histogram_name_,
                                    fifo_underrun_count_, 1, 1000, 50);
     base::UmaHistogramCustomCounts(
diff --git a/third_party/blink/renderer/platform/audio/audio_destination_uma_reporter.h b/third_party/blink/renderer/platform/audio/audio_destination_uma_reporter.h
index 45a9a29..5113fa0 100644
--- a/third_party/blink/renderer/platform/audio/audio_destination_uma_reporter.h
+++ b/third_party/blink/renderer/platform/audio/audio_destination_uma_reporter.h
@@ -26,8 +26,8 @@
 
   // These methods are not thread-safe and must be called within
   // `AudioDestination::RequestRender()` method for correct instrumentation.
-  void UpdateFifoDelay(base::TimeDelta fifo_delay);
-  void UpdateTotalPlayoutDelay(base::TimeDelta total_playout_delay);
+  void AddFifoDelay(base::TimeDelta fifo_delay);
+  void AddTotalPlayoutDelay(base::TimeDelta total_playout_delay);
   void IncreaseFifoUnderrunCount();
   void UpdateMetricNameForDualThreadMode();
   void Report();
@@ -57,7 +57,7 @@
 
  private:
   // Calculates the percentage of `delta` relative to the expected callback
-  // interval, scaled by kMetricsReportCycle for reporting.
+  // interval, scaled by `kMetricsReportCycle` for reporting.
   // Returns the percentage (0-100).
   int PercentOfCallbackInterval(base::TimeDelta duration);
 
@@ -71,13 +71,14 @@
   bool use_audio_worklet_ = false;
 
   // The audio delay (ms) computed the number of available frames of the
-  // PushPUllFIFO in AudioDestination. Measured and reported at every audio
-  // callback.
-  base::TimeDelta fifo_delay_;
+  // PushPUllFIFO in AudioDestination. Averaged, reported and reset at every
+  // `kMetricsReportCycle` audio callbacks.
+  base::TimeDelta fifo_delay_sum_;
 
   // The audio delay (ms) covers the whole pipeline from the WebAudio graph to
-  // the speaker. Measured and reported at every audio callback.
-  base::TimeDelta total_playout_delay_;
+  // the speaker. Averaged, reported and reset at every 'kMetricsReportCycle'
+  // audio callbacks.
+  base::TimeDelta total_playout_delay_sum_;
 
   // Histogram names for metrics reported by `AudioDestinationUmaReporter`.
   // These names are constructed during initialization (or updated in
diff --git a/third_party/blink/renderer/platform/audio/audio_destination_uma_reporter_test.cc b/third_party/blink/renderer/platform/audio/audio_destination_uma_reporter_test.cc
index c6b2edbd..c37fd63 100644
--- a/third_party/blink/renderer/platform/audio/audio_destination_uma_reporter_test.cc
+++ b/third_party/blink/renderer/platform/audio/audio_destination_uma_reporter_test.cc
@@ -86,10 +86,10 @@
       fake_callback_interval * 0.1;
 
   for (int i = 0; i < 1000; i++) {
-    base::TimeDelta fifo_delay = base::Milliseconds(i % 2 ? 10 : 40);
-    uma_reporter_->UpdateFifoDelay(fifo_delay);
-    base::TimeDelta infra_delay = base::Milliseconds(i % 2 ? 1 : 10);
-    uma_reporter_->UpdateTotalPlayoutDelay(fifo_delay + infra_delay);
+    base::TimeDelta fifo_delay = base::Milliseconds(50);
+    uma_reporter_->AddFifoDelay(fifo_delay);
+    base::TimeDelta infra_delay = base::Milliseconds(20);
+    uma_reporter_->AddTotalPlayoutDelay(fifo_delay + infra_delay);
     uma_reporter_->AddRenderDuration(fake_render_duration);
     uma_reporter_->AddRequestRenderDuration(fake_request_render_duration);
     uma_reporter_->AddRequestRenderGapDuration(
@@ -98,21 +98,14 @@
   }
 
   histogram_tester_.ExpectBucketCount(GetExpectedHistogramName(kFifoDelayBase),
-                                      10, 500);
+                                      50, 1);
   histogram_tester_.ExpectBucketCount(
-      GetExpectedHistogramName(kFifoDelayBase) + latency_tag_, 10, 500);
-  histogram_tester_.ExpectBucketCount(GetExpectedHistogramName(kFifoDelayBase),
-                                      40, 500);
+      GetExpectedHistogramName(kFifoDelayBase) + latency_tag_, 50, 1);
+
   histogram_tester_.ExpectBucketCount(
-      GetExpectedHistogramName(kFifoDelayBase) + latency_tag_, 40, 500);
+      GetExpectedHistogramName(kTotalPlayoutDelayBase), 70, 1);
   histogram_tester_.ExpectBucketCount(
-      GetExpectedHistogramName(kTotalPlayoutDelayBase), 11, 500);
-  histogram_tester_.ExpectBucketCount(
-      GetExpectedHistogramName(kTotalPlayoutDelayBase) + latency_tag_, 11, 500);
-  histogram_tester_.ExpectBucketCount(
-      GetExpectedHistogramName(kTotalPlayoutDelayBase), 50, 500);
-  histogram_tester_.ExpectBucketCount(
-      GetExpectedHistogramName(kTotalPlayoutDelayBase) + latency_tag_, 50, 500);
+      GetExpectedHistogramName(kTotalPlayoutDelayBase) + latency_tag_, 70, 1);
 
   // Due to the static_cast<int> conversion in PercentOfCallbackInterval, the
   // floating-point part of the double result is truncated (rounded down).
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc
index 9b3ecb8..b10ffcd 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc
@@ -6,15 +6,12 @@
 
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
-#include "third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
 
 namespace blink {
 
 namespace {
 
-constexpr unsigned kMaxCanvasAnimationBacklog = 2;
-
 bool CanUseGPU() {
   base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper =
       SharedGpuContext::ContextProviderWrapper();
@@ -75,28 +72,6 @@
          !LowLatencyEnabled();
 }
 
-void CanvasResourceHost::SetIsDisplayed(bool displayed) {
-  is_displayed_ = displayed;
-  // If the canvas is no longer being displayed, stop using the rate
-  // limiter.
-  if (!is_displayed_) {
-    frames_since_last_commit_ = 0;
-    if (rate_limiter_) {
-      rate_limiter_->Reset();
-      rate_limiter_.reset(nullptr);
-    }
-  }
-}
-
-SharedContextRateLimiter* CanvasResourceHost::RateLimiter() const {
-  return rate_limiter_.get();
-}
-
-void CanvasResourceHost::CreateRateLimiter() {
-  rate_limiter_ =
-      std::make_unique<SharedContextRateLimiter>(kMaxCanvasAnimationBacklog);
-}
-
 RasterMode CanvasResourceHost::GetRasterMode() const {
   if (IsHibernating()) {
     return RasterMode::kCPU;
@@ -134,89 +109,6 @@
   }
 }
 
-void CanvasResourceHost::SetHdrMetadata(const gfx::HDRMetadata& hdr_metadata) {
-  hdr_metadata_ = hdr_metadata;
-}
-
-namespace {
-
-// Adapter for wrapping a CanvasResourceReleaseCallback into a
-// viz::ReleaseCallback
-void ReleaseCanvasResource(CanvasResource::ReleaseCallback callback,
-                           scoped_refptr<CanvasResource> canvas_resource,
-                           const gpu::SyncToken& sync_token,
-                           bool is_lost) {
-  std::move(callback).Run(std::move(canvas_resource), sync_token, is_lost);
-}
-
-}  // unnamed namespace
-
-bool CanvasResourceHost::PrepareTransferableResource(
-    viz::TransferableResource* out_resource,
-    viz::ReleaseCallback* out_release_callback) {
-  CHECK(cc_layer_);  // This explodes if FinalizeFrame() was not called.
-
-  frames_since_last_commit_ = 0;
-  if (rate_limiter_) {
-    rate_limiter_->Reset();
-  }
-
-  // If hibernating but not hidden, we want to wake up from hibernation.
-  if (IsHibernating() && !IsPageVisible()) {
-    return false;
-  }
-
-  if (!IsResourceValid()) {
-    return false;
-  }
-
-  // The beforeprint event listener is sometimes scheduled in the same task
-  // as BeginFrame, which means that this code may sometimes be called between
-  // the event listener and its associated FinalizeFrame call. So in order to
-  // preserve the display list for printing, FlushRecording needs to know
-  // whether any printing occurred in the current task.
-  FlushReason reason = FlushReason::kCanvasPushFrame;
-  if (PrintedInCurrentTask() || IsPrinting()) {
-    reason = FlushReason::kCanvasPushFrameWhilePrinting;
-  }
-  FlushRecording(reason);
-
-  // If the context is lost, we don't know if we should be producing GPU or
-  // software frames, until we get a new context, since the compositor will
-  // be trying to get a new context and may change modes.
-  if (!GetOrCreateCanvasResourceProvider()) {
-    return false;
-  }
-
-  scoped_refptr<CanvasResource> frame =
-      resource_provider_->ProduceCanvasResource(reason);
-  if (!frame || !frame->IsValid()) {
-    return false;
-  }
-
-  CanvasResource::ReleaseCallback release_callback;
-  if (!frame->PrepareTransferableResource(out_resource, &release_callback,
-                                          /*needs_verified_synctoken=*/false) ||
-      *out_resource == cc_layer_->current_transferable_resource()) {
-    // If the resource did not change, the release will be handled correctly
-    // when the callback from the previous frame is dispatched. But run the
-    // |release_callback| to release the ref acquired above.
-    std::move(release_callback)
-        .Run(std::move(frame), gpu::SyncToken(), false /* is_lost */);
-    return false;
-  }
-  // TODO(https://crbug.com/1475955): HDR metadata should be propagated to
-  // `frame`, and should be populated by the above call to
-  // CanvasResource::PrepareTransferableResource, rather than be inserted
-  // here.
-  out_resource->hdr_metadata = hdr_metadata_;
-  // Note: frame is kept alive via a reference kept in out_release_callback.
-  *out_release_callback = base::BindOnce(
-      ReleaseCanvasResource, std::move(release_callback), std::move(frame));
-
-  return true;
-}
-
 void CanvasResourceHost::DoPaintInvalidation(const gfx::Rect& dirty_rect) {
   if (cc_layer_ && IsComposited()) {
     cc_layer_->SetNeedsDisplayRect(dirty_rect);
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_host.h b/third_party/blink/renderer/platform/graphics/canvas_resource_host.h
index 85ee3f7..29dd9193 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_host.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_host.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "cc/layers/texture_layer.h"
-#include "cc/layers/texture_layer_client.h"
 #include "third_party/blink/renderer/platform/graphics/flush_reason.h"
 #include "third_party/blink/renderer/platform/graphics/opacity_mode.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
@@ -22,7 +21,6 @@
 namespace blink {
 
 class CanvasResourceProvider;
-class SharedContextRateLimiter;
 
 // Specifies whether the provider should rasterize paint commands on the CPU
 // or GPU. This is used to support software raster with GPU compositing.
@@ -36,15 +34,10 @@
   kPreferCPU,
 };
 
-class PLATFORM_EXPORT CanvasResourceHost : public cc::TextureLayerClient {
+class PLATFORM_EXPORT CanvasResourceHost {
  public:
   explicit CanvasResourceHost(gfx::Size size);
-  ~CanvasResourceHost() override;
-
-  // cc::TextureLayerClient implementation.
-  bool PrepareTransferableResource(
-      viz::TransferableResource* out_resource,
-      viz::ReleaseCallback* out_release_callback) override;
+  virtual ~CanvasResourceHost();
 
   virtual void NotifyGpuContextLost() = 0;
   // TODO(crbug.com/399587138): Delete once `cc::Layer` related code is moved to
@@ -69,9 +62,6 @@
   gfx::Size Size() const { return size_; }
   virtual void SetSize(gfx::Size size) { size_ = size; }
 
-  void SetHdrMetadata(const gfx::HDRMetadata& hdr_metadata);
-  const gfx::HDRMetadata& GetHDRMetadata() const { return hdr_metadata_; }
-
   virtual bool LowLatencyEnabled() const { return false; }
 
   CanvasResourceProvider* ResourceProvider() const {
@@ -85,9 +75,6 @@
 
   virtual void DiscardResourceProvider();
 
-  void SetIsDisplayed(bool);
-  bool IsDisplayed() const { return is_displayed_; }
-
   virtual bool IsPageVisible() const = 0;
 
   virtual bool IsPrinting() const { return false; }
@@ -97,12 +84,6 @@
   bool ShouldTryToUseGpuRaster() const;
   void SetPreferred2DRasterMode(RasterModeHint);
 
-  // Temporary plumbing while relocating code from Canvas2DLayerBridge.
-  SharedContextRateLimiter* RateLimiter() const;
-  void CreateRateLimiter();
-  unsigned IncrementFramesSinceLastCommit() {
-    return ++frames_since_last_commit_;
-  }
   void AlwaysEnableRasterTimersForTesting() {
     always_enable_raster_timers_for_testing_ = true;
   }
@@ -129,12 +110,8 @@
   scoped_refptr<cc::TextureLayer> cc_layer_;
 
  private:
-  bool is_displayed_ = false;
   bool is_opaque_ = false;
-  unsigned frames_since_last_commit_ = 0;
-  std::unique_ptr<SharedContextRateLimiter> rate_limiter_;
   std::unique_ptr<CanvasResourceProvider> resource_provider_;
-  gfx::HDRMetadata hdr_metadata_;
   RasterModeHint preferred_2d_raster_mode_ = RasterModeHint::kPreferCPU;
   gfx::Size size_;
   bool always_enable_raster_timers_for_testing_ = false;
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index 107c037..310a3a76 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -481,6 +481,14 @@
     }
   }
 
+  // If staging_texture_ exists, then premultiplication
+  // has already been handled via CopySubTextureCHROMIUM.
+  // TODO(crbug.com/410591523): Always set this field when not using
+  // `staging_texture_` under a killswitch.
+  if (!staging_texture_ && requested_alpha_type_ == kUnpremul_SkAlphaType) {
+    out_resource->alpha_type = requested_alpha_type_;
+  }
+
   return true;
 }
 
@@ -1213,15 +1221,6 @@
     layer_->SetContentsOpaque(requested_alpha_type_ == kOpaque_SkAlphaType);
     layer_->SetBlendBackgroundColor(requested_alpha_type_ !=
                                     kOpaque_SkAlphaType);
-    if (staging_texture_) {
-      // If staging_texture_ exists, then premultiplication
-      // has already been handled via CopySubTextureCHROMIUM.
-      DCHECK(requested_alpha_type_ == kUnpremul_SkAlphaType);
-      layer_->SetPremultipliedAlpha(true);
-    } else {
-      layer_->SetPremultipliedAlpha(requested_alpha_type_ !=
-                                    kUnpremul_SkAlphaType);
-    }
   }
 
   return layer_.get();
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index fa936dae..89e57b14 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -323,7 +323,12 @@
       // OriginTrialContext::CanEnableTrialFromName limits access to extensions.
       origin_trial_feature_name: "AIPromptAPIForExtension",
       origin_trial_allows_third_party: true,
-      status: "experimental",
+      status: {
+        "Win": "experimental",
+        "Mac": "experimental",
+        "Linux": "experimental",
+        "default": "",
+      },
       base_feature_status: "enabled",
       copied_from_base_feature_if: "overridden",
       implied_by: ["AIPromptAPIMultimodalInput", "AIPromptAPIForExtension"],
@@ -333,7 +338,12 @@
       name: "AIPromptAPIForExtension",
       origin_trial_feature_name: "AIPromptAPIForExtension",
       origin_trial_allows_third_party: true,
-      status: "experimental",
+      status: {
+        "Win": "experimental",
+        "Mac": "experimental",
+        "Linux": "experimental",
+        "default": "",
+      },
       base_feature_status: "enabled",
       copied_from_base_feature_if: "overridden",
     },
@@ -343,13 +353,23 @@
     },
     {
       name: "AIPromptAPIMultimodalInput",
-      status: "experimental",
+      status: {
+        "Win": "experimental",
+        "Mac": "experimental",
+        "Linux": "experimental",
+        "default": "",
+      },
     },
     {
       name: "AIPromptAPIStructuredOutput",
       origin_trial_feature_name: "AIPromptAPIForExtension",
       origin_trial_allows_third_party: true,
-      status: "experimental",
+      status: {
+        "Win": "experimental",
+        "Mac": "experimental",
+        "Linux": "experimental",
+        "default": "",
+      },
       base_feature_status: "enabled",
       copied_from_base_feature_if: "overridden",
       implied_by: ["AIPromptAPI"],
@@ -360,7 +380,12 @@
     },
     {
       name: "AIRewriterAPI",
-      status: "experimental",
+      status: {
+        "Win": "experimental",
+        "Mac": "experimental",
+        "Linux": "experimental",
+        "default": "",
+      },
       origin_trial_feature_name: "AIRewriterAPI",
       origin_trial_allows_third_party: true,
       base_feature_status: "enabled",
@@ -372,7 +397,12 @@
     },
     {
       name: "AISummarizationAPI",
-      status: "experimental",
+      status: {
+        "Win": "experimental",
+        "Mac": "experimental",
+        "Linux": "experimental",
+        "default": "",
+      },
       origin_trial_feature_name: "AISummarizationAPI",
       origin_trial_allows_third_party: true,
       base_feature_status: "enabled",
@@ -384,7 +414,12 @@
     },
     {
       name: "AIWriterAPI",
-      status: "experimental",
+      status: {
+        "Win": "experimental",
+        "Mac": "experimental",
+        "Linux": "experimental",
+        "default": "",
+      },
       origin_trial_feature_name: "AIWriterAPI",
       origin_trial_allows_third_party: true,
       base_feature_status: "enabled",
@@ -4760,6 +4795,15 @@
       status: "experimental",
     },
     {
+      // Adds an "source" attribute to ToggleEvent which is set to the button
+      // that invoked the element which the toggle event was fired on, if
+      // appropriate.
+      // https://issues.chromium.org/issues/408018828
+      // https://github.com/whatwg/html/issues/9111
+      name: "ToggleEventSource",
+      status: "experimental",
+    },
+    {
       name: "TopicsAPI",
       base_feature: "none",
       public: true,
@@ -4853,7 +4897,12 @@
     },
     {
       name: "TranslationAPI",
-      status: "experimental",
+      status: {
+        "Win": "experimental",
+        "Mac": "experimental",
+        "Linux": "experimental",
+        "default": "",
+      },
       origin_trial_feature_name: "TranslationAPI",
       origin_trial_allows_third_party: true,
       base_feature_status: "enabled",
@@ -4866,7 +4915,12 @@
     },
     {
       name: "TranslationAPIV1",
-      status: "experimental",
+      status: {
+        "Win": "experimental",
+        "Mac": "experimental",
+        "Linux": "experimental",
+        "default": "",
+      },
       copied_from_base_feature_if: "overridden",
     },
     {
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index 744c794..8807619 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -2249,10 +2249,14 @@
             'third_party/blink/renderer/modules/webgpu',
         ],
         'allowed': [
+            'base::BindOnce',
+            'cc::TextureLayerClient',
+            'viz::ReleaseCallback',
             'viz::SharedImageFormat',
             'viz::SinglePlaneFormat',
             'viz::SkColorTypeToSinglePlaneSharedImageFormat',
             'viz::ToClosestSkColorType',
+            'viz::TransferableResource',
         ],
     },
     {
diff --git a/third_party/blink/web_tests/FlagExpectations/trees-in-viz b/third_party/blink/web_tests/FlagExpectations/trees-in-viz
index b3097b7..b286749 100644
--- a/third_party/blink/web_tests/FlagExpectations/trees-in-viz
+++ b/third_party/blink/web_tests/FlagExpectations/trees-in-viz
@@ -19,6 +19,36 @@
 [ Mac15-arm64 ] fast/canvas/webgl/draw-webgl-to-canvas-2d.html [ Pass ]
 [ Mac14-arm64 ] fast/canvas/webgl/webgl-layout-on-out-of-band-resize.html [ Pass ]
 [ Mac15-arm64 ] fast/canvas/webgl/webgl-layout-on-out-of-band-resize.html [ Pass ]
+[ Mac14-arm64 ] fast/mediacapturefromelement/CanvasCaptureMediaStream-webgl-events.html [ Pass ]
+[ Mac15-arm64 ] fast/mediacapturefromelement/CanvasCaptureMediaStream-webgl-events.html [ Pass ]
+[ Mac14-arm64 ] fast/peerconnection/RTCEncodedVideoFrame-set-metadata.html [ Pass ]
+[ Mac15-arm64 ] fast/peerconnection/RTCEncodedVideoFrame-set-metadata.html [ Pass ]
+[ Mac14-arm64 ] fast/peerconnection/RTCPeerConnection-applyConstraints-remoteVideoTrack.html [ Pass ]
+[ Mac15-arm64 ] fast/peerconnection/RTCPeerConnection-applyConstraints-remoteVideoTrack.html [ Pass ]
+[ Mac14-arm64 ] fast/peerconnection/RTCPeerConnection-datachannel.html [ Pass ]
+[ Mac15-arm64 ] fast/peerconnection/RTCPeerConnection-datachannel.html [ Pass ]
+[ Mac14-arm64 ] fast/peerconnection/RTCPeerConnection-iframe-gc.html [ Pass ]
+[ Mac15-arm64 ] fast/peerconnection/RTCPeerConnection-iframe-gc.html [ Pass ]
+[ Mac14-arm64 ] fast/peerconnection/RTCPeerConnection-lifetime.html [ Pass ]
+[ Mac15-arm64 ] fast/peerconnection/RTCPeerConnection-lifetime.html [ Pass ]
+[ Mac14-arm64 ] fast/spatial-navigation/snav-disappear-after-hover.html [ Pass ]
+[ Mac15-arm64 ] fast/spatial-navigation/snav-disappear-after-hover.html [ Pass ]
+[ Mac14-arm64 ] fast/webgl/canvas-to-data-url.html [ Pass ]
+[ Mac15-arm64 ] fast/webgl/canvas-to-data-url.html [ Pass ]
+[ Mac14-arm64 ] fast/webgl/context-destroyed-crash.html [ Pass ]
+[ Mac15-arm64 ] fast/webgl/context-destroyed-crash.html [ Pass ]
+[ Mac14-arm64 ] fast/webgl/offscreenCanvas-image-rendering.html [ Pass ]
+[ Mac15-arm64 ] fast/webgl/offscreenCanvas-image-rendering.html [ Pass ]
+[ Mac14-arm64 ] fast/webgl/offscreenCanvas-transferToImageBitmap-invalid-mailbox.html [ Pass ]
+[ Mac15-arm64 ] fast/webgl/offscreenCanvas-transferToImageBitmap-invalid-mailbox.html [ Pass ]
+[ Mac14-arm64 ] fast/webgl/texImage-imageBitmap-from-image.html [ Pass ]
+[ Mac15-arm64 ] fast/webgl/texImage-imageBitmap-from-image.html [ Pass ]
+[ Mac14-arm64 ] fast/webgl/webgl-shadow-no-alpha.html [ Pass ]
+[ Mac15-arm64 ] fast/webgl/webgl-shadow-no-alpha.html [ Pass ]
+[ Mac14-arm64 ] fast/webgl/webgl-texture-binding-preserved.html [ Pass ]
+[ Mac15-arm64 ] fast/webgl/webgl-texture-binding-preserved.html [ Pass ]
+[ Mac14-arm64 ] fast/webgl/webgl-viewport-parameters-preserved.html [ Pass ]
+[ Mac15-arm64 ] fast/webgl/webgl-viewport-parameters-preserved.html [ Pass ]
 
 # TODO(crbug.com/401453196): Resolve failing layout tests. Refine into child
 # bugs if/as it becomes clear that tests are failing due to distinct issues.
@@ -27,3 +57,60 @@
 crbug.com/401453196 fast/canvas/canvas-hidpi-blurry.html [ Failure Timeout ]
 crbug.com/401453196 fast/dom/focus-contenteditable.html [ Failure ]
 crbug.com/401453196 fast/dom/inert/inert-node-is-uneditable.html [ Timeout ]
+crbug.com/401453196 fast/backgrounds/gradient-background-leakage-hidpi.html [ Failure Timeout ]
+crbug.com/401453196 fast/dynamic/anchor-lock.html [ Failure Timeout ]
+crbug.com/401453196 fast/events/reveal-link-when-focused.html [ Failure Timeout ]
+crbug.com/401453196 fast/events/wheel/wheel-target-detached.html [ Crash ]
+crbug.com/401453196 fast/forms/color-scheme/media/video-overlay-menu.html [ Failure Timeout ]
+crbug.com/401453196 fast/forms/color-scheme/media/video-overlay-play-button.html [ Failure Timeout ]
+crbug.com/401453196 fast/forms/color-scheme/media/video-playback-speed-menu.html [ Failure Timeout ]
+crbug.com/401453196 fast/forms/color/color-picker-from-color-suggestion-picker.html [ Failure Timeout ]
+crbug.com/401453196 fast/forms/color/input-color-under-shadow-popup-crash.html [ Failure Timeout ]
+crbug.com/401453196 fast/forms/huge-mac-input-clamped-height.html [ Failure Timeout ]
+crbug.com/401453196 fast/forms/huge-mac-input-clamped-width.html [ Failure Timeout ]
+crbug.com/401453196 fast/forms/number/number-spinbutton-changeevent-trigger-without-mousemove.html [ Failure Timeout ]
+crbug.com/401453196 fast/forms/pseudo-open.html [ Failure Timeout ]
+crbug.com/401453196 fast/forms/select/customizable-select/appearance-base-select-usecounter.html [ Failure Timeout ]
+crbug.com/401453196 fast/forms/textarea/textarea-resize-below-custom-resizer-size.html [ Failure Timeout ]
+crbug.com/401453196 fast/forms/textarea/textarea-resize-below-min-intrinsic-size.html [ Failure Timeout ]
+crbug.com/401453196 fast/forms/textarea/textarea-resize-below-resizer-size.html [ Failure Timeout ]
+crbug.com/401453196 fast/frames/frame-set-scaling-centered.html [ Failure Timeout ]
+crbug.com/401453196 fast/frames/frame-set-scaling.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/background-tile-bleed.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/border-background-align.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/broken-image-icon-hidpi.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/broken-image-with-size-hidpi.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/clip-text-in-hidpi.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/device-scale-factor-paint.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/focus-rings.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/gradient-with-scaled-ancestor.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/image-set-background-dynamic.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/image-set-background-repeat-without-size.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/image-set-background-repeat.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/image-set-background.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/image-set-border-image-button.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/image-set-border-image-comparison.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/image-set-border-image-dynamic.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/image-set-border-image.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/image-set-content-dynamic.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/image-set-content-with-width.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/image-set-content.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/image-set-fallback.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/image-set-list-style-image.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/image-set-out-of-order.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/offset-path-ray.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/offset-path-string.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/resize-corner-hidpi.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/scrollbar-appearance-decrease-device-scale-factor.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/scrollbar-appearance-increase-device-scale-factor.html [ Failure Timeout ]
+crbug.com/401453196 fast/hidpi/video-controls-in-hidpi.html [ Failure Timeout ]
+crbug.com/401453196 fast/layers/scroll-rect-to-visible.html [ Failure Timeout ]
+crbug.com/401453196 fast/overflow/clip-rects-fixed-ancestor.html [ Failure Timeout ]
+crbug.com/401453196 fast/overflow/overflow-of-video-outline.html [ Failure Timeout ]
+crbug.com/401453196 fast/overflow/position-fixed-transform-clipping.html [ Failure Timeout ]
+crbug.com/401453196 fast/overflow/scrollRevealButton.html [ Failure Timeout ]
+crbug.com/401453196 fast/peerconnection/RTCPeerConnection-createDTMFSender.html [ Failure Timeout ]
+crbug.com/401453196 fast/peerconnection/RTCPeerconnection-insertable-streams.html [ Failure Timeout ]
+crbug.com/401453196 fast/scrolling/overlay-scrollbars.html [ Failure Timeout ]
+crbug.com/401453196 fast/scrolling/scroll-then-composite.html [ Failure Timeout ]
+crbug.com/401453196 fast/scrolling/scrollbars/mouse-scrolling-on-div-custom-scrollbar.html [ Failure Timeout ]
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index ac9738df..9f0b763 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -1891,3 +1891,9 @@
 
 # Servo browser specific
 external/wpt/html/webappapis/animation-frames/spurious-frame-callbacks-optimization.html [ Skip ]
+
+# Clipboardchange is not yet supported on these platforms.
+crbug.com/41442253 [ Android ] external/wpt/clipboard-apis/async-navigator-clipboard-change-event.tentative.https.html [ Skip ]
+crbug.com/41442253 [ Fuchsia ] external/wpt/clipboard-apis/async-navigator-clipboard-change-event.tentative.https.html [ Skip ]
+crbug.com/41442253 [ Mac ] external/wpt/clipboard-apis/async-navigator-clipboard-change-event.tentative.https.html [ Skip ]
+crbug.com/41442253 [ iOS18-simulator ] external/wpt/clipboard-apis/async-navigator-clipboard-change-event.tentative.https.html [ Skip ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 47b54c0c..ef2580b3 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2813,8 +2813,8 @@
 crbug.com/413411328 external/wpt/css/css-values/urls/referrer-policy/unsafe-url/url-image-referrerpolicy-same-origin.sub.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
-[ Mac13 ] external/wpt/html/document-isolation-policy/service-worker-coep-credentialless-proxy.https.tentative.window.html [ Timeout ]
-[ Linux ] external/wpt/webrtc-encoded-transform/RTCRtpScriptTransform-encoded-transform.https.html [ Pass Timeout ]
+crbug.com/418024434 [ Mac13 ] external/wpt/html/document-isolation-policy/service-worker-coep-credentialless-proxy.https.tentative.window.html [ Timeout ]
+crbug.com/418031072 [ Linux ] external/wpt/webrtc-encoded-transform/RTCRtpScriptTransform-encoded-transform.https.html [ Pass Timeout ]
 crbug.com/417940356 [ Win11 ] external/wpt/page-lifecycle/child-display-none.tentative.html [ Timeout ]
 crbug.com/417940356 [ Win11-arm64 ] external/wpt/page-lifecycle/child-display-none.tentative.html [ Pass Timeout ]
 crbug.com/417940356 [ Win10.20h2 ] external/wpt/page-lifecycle/child-display-none.tentative.html [ Timeout ]
diff --git a/third_party/blink/web_tests/TestLists/trees-in-viz b/third_party/blink/web_tests/TestLists/trees-in-viz
index d5d7de15..af62e45e 100644
--- a/third_party/blink/web_tests/TestLists/trees-in-viz
+++ b/third_party/blink/web_tests/TestLists/trees-in-viz
@@ -1,2 +1 @@
-fast/canvas
-fast/dom
+fast
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 82c978d2..6319105 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -373952,7 +373952,7 @@
       []
      ],
      "content-with-transform-ref.html": [
-      "8c2b65e63e78c202c77aee02c0797ef81c25881c",
+      "10166b16a8c4ab41147b02783b20ec59d3e77d5c",
       []
      ],
      "content-with-transparent-background-ref.html": [
@@ -374092,7 +374092,7 @@
       []
      ],
      "inline-with-offset-from-containing-block-ref.html": [
-      "4a66af4ece26abd69a41840319783f86001746d0",
+      "2eebf8cf5966409a62ec3aba3c32231adbe79f42",
       []
      ],
      "intrinsic-aspect-ratio-ref.html": [
@@ -385539,6 +385539,22 @@
       "57b6f1e32182865fc12eecc4226a0740e3d0535c",
       []
      ],
+     "account_picture.py": [
+      "ae453086bc0fe9c238e61894ba7af6ced7bcdadf",
+      []
+     ],
+     "account_picture_get_count.py": [
+      "91c3601fbdc740813d8c98cf7e408b596e1ee3a7",
+      []
+     ],
+     "account_picture_uncached.py": [
+      "51289f37b7c94e6ad4330890155dca3fe5405a85",
+      []
+     ],
+     "account_picture_uncached_get_count.py": [
+      "e2adbd517b8e2475434be71a625ce98997db8518",
+      []
+     ],
      "accounts.py": [
       "c0117862816ff4668b6dd9416633bee6eb9af227",
       []
@@ -385602,7 +385618,7 @@
       ]
      },
      "fedcm-helper.sub.js": [
-      "4337adf9f87b045f93d6dbb33398d38f2fce72cf",
+      "b6bbbc19e21bca496df255cf16376c6443f8b1e2",
       []
      ],
      "fedcm-helper.sub.js.headers": [
@@ -385638,7 +385654,7 @@
       []
      ],
      "keys.py": [
-      "6b7d67e21e7eea7927a40ab094847b7224d49985",
+      "8ced364116b1c5d55be18b7f88e358b28bcca912",
       []
      ],
      "lfedcm-helpers.js": [
@@ -385669,6 +385685,10 @@
       "ed984a42387198f95b2510041e5f73758541e9d3",
       []
      ],
+     "manifest_accounts_push.json": [
+      "721fe05f94066fc17931573e2d6bf599691b7a3f",
+      []
+     ],
      "manifest_broken_login.json": [
       "d42bf8fb2692c50648ad412b46014a1a8c60e730",
       []
@@ -385762,7 +385782,7 @@
       []
      ],
      "mark_signedout.sub.headers": [
-      "69157b3a371e369df585331a2479144fee444f5c",
+      "5ddbacf85f4641b6ea65479106687bc976d92d65",
       []
      ],
      "no-cors.py": [
@@ -385773,8 +385793,16 @@
       "fad93088db5b9204c890fea871574c8b7d1ee4f8",
       []
      ],
+     "push_accounts": [
+      "ea7eb12beab9f8d38fb48ccf58cfece00a0c60c2",
+      []
+     ],
+     "push_accounts.sub.headers": [
+      "d560fade5a0e4237f751b0c90a87fc25f92aa14a",
+      []
+     ],
      "request-params-check.py": [
-      "08c28e32b7942d6db83f7b9c2e4664c9cf47987f",
+      "f5b48d9b05386292295e5df0b6b80e24dd1c42bf",
       []
      ],
      "resolve.html": [
@@ -391087,7 +391115,7 @@
      []
     ],
     "enabled-by-permission-policy-attribute-redirect-on-load.https.sub-expected.txt": [
-     "19ac246a013c7c018666845aa001659c702bce75",
+     "82deda51486f5f2ce6dbae18a09dd8a8bd67d2c2",
      []
     ],
     "enabled-by-permissions-policy.https.sub.html.headers": [
@@ -405926,6 +405954,10 @@
        "popover-utils.js": [
         "7878b125f2f6b1f6d39d1972f7387e8014b6dcf8",
         []
+       ],
+       "toggle-event-source-test.js": [
+        "93ecd270fac574d7a4928e54b71ded77540421e7",
+        []
        ]
       }
      },
@@ -467528,6 +467560,52 @@
        }
       ]
      ],
+     "rewriter-availability-available.tentative.https.window.js": [
+      "a2117486e236d31a424f1920ba468d7531ee5775",
+      [
+       "ai/rewriter/rewriter-availability-available.tentative.https.window.html",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "Rewriter Availability Available"
+         ],
+         [
+          "script",
+          "../resources/util.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ]
+     ],
+     "rewriter-availability.tentative.https.window.js": [
+      "e966b5df31ed5c0cb337e8736ae07cabc85dfdf4",
+      [
+       "ai/rewriter/rewriter-availability.tentative.https.window.html",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "Rewriter Availability"
+         ],
+         [
+          "script",
+          "../resources/util.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ]
+     ],
      "rewriter-from-detached-iframe.tentative.https.window.js": [
       "ecc24c77be7b9e05cd20a1a307432e25d4972d87",
       [
@@ -467559,7 +467637,7 @@
       ]
      ],
      "rewriter.tentative.https.window.js": [
-      "ab97765c3e6b50807b64c8249e8735f6d3bbabdb",
+      "6ad79385be0a54d47ee7398ad751f51b3017dd03",
       [
        "ai/rewriter/rewriter.tentative.https.window.html",
        {
@@ -467795,7 +467873,7 @@
       ]
      ],
      "translator.optional.https.window.js": [
-      "5abd683caafec136d37c7ec925f221b465dd8107",
+      "2a4c5a6c5dd3cd8b2fea087aa910428c2a3041bf",
       [
        "ai/translator/translator.optional.https.window.html",
        {
@@ -467858,6 +467936,52 @@
        }
       ]
      ],
+     "writer-availability-available.tentative.https.window.js": [
+      "0e3106a14c2ef4880a14132d74a9ed39ca97b4ac",
+      [
+       "ai/writer/writer-availability-available.tentative.https.window.html",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "Writer Availability Available"
+         ],
+         [
+          "script",
+          "../resources/util.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ]
+     ],
+     "writer-availability.tentative.https.window.js": [
+      "a68b7d35e64731fe561aa1792cecb50da935b70d",
+      [
+       "ai/writer/writer-availability.tentative.https.window.html",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "Writer Availability"
+         ],
+         [
+          "script",
+          "../resources/util.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ]
+     ],
      "writer-from-detached-iframe.tentative.https.window.js": [
       "6143bc4276fbe6ea2f9f16270b293174cda496f5",
       [
@@ -467889,7 +468013,7 @@
       ]
      ],
      "writer.tentative.https.window.js": [
-      "7c20dc3292c2a6b8739dec57b16da9b288c7af84",
+      "22c976bb0cb5f345638b54d7df53485d78163944",
       [
        "ai/writer/writer.tentative.https.window.html",
        {
@@ -565081,6 +565205,26 @@
       }
      ]
     ],
+    "fedcm-accounts-push": {
+     "fedcm-accounts-push-basic.tentative.https.html": [
+      "3affa4e3a6d745c8e5f0802e287cc3d16ee89ee1",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ],
+     "fedcm-accounts-push-caches-pictures.tentative.https.html": [
+      "5b069785f98348be621934a50a540dbad6ae52f3",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ]
+    },
     "fedcm-after-abort.https.html": [
      "3c2f981e82fab32d20303367289180c20e8eb71f",
      [
@@ -599621,11 +599765,14 @@
      ]
     ],
     "enabled-by-permission-policy-attribute-redirect-on-load.https.sub.html": [
-     "f3cf0bc97e107e41906ef0fcb0d8ca89885100fe",
+     "8ce0aab33898f3b4cf4e0110810f63c19247ebe4",
      [
       null,
       {
-       "testdriver": true
+       "testdriver": true,
+       "testdriver_features": [
+        "bidi"
+       ]
       }
      ]
     ],
@@ -649937,6 +650084,15 @@
           }
          ]
         ],
+        "select-optgroup-arrow-keys.tentative.html": [
+         "00ba0e3a39bdd971bf1a7ea26ec716ca04aecb44",
+         [
+          null,
+          {
+           "testdriver": true
+          }
+         ]
+        ],
         "select-option-arrow-scroll.tentative.html": [
          "cb65bddc0715bca966d456ebb7136f39a657ec0e",
          [
@@ -650595,6 +650751,15 @@
          }
         ]
        ],
+       "details-toggle-source.tentative.html": [
+        "1a571f5441449a964bf9aea27246ef13f937944c",
+        [
+         null,
+         {
+          "testdriver": true
+         }
+        ]
+       ],
        "details.html": [
         "5ed14c53afc9371275917d460f5b9638ab9a091e",
         [
@@ -650984,6 +651149,15 @@
          {}
         ]
        ],
+       "dialog-toggle-source.tentative.html": [
+        "7e6fe25dabf79d9430fcfa98afe83f3520d62686",
+        [
+         null,
+         {
+          "testdriver": true
+         }
+        ]
+       ],
        "focus-after-close.html": [
         "7137edcd1a18080c96d627e5f40da13b1d36437f",
         [
@@ -652020,6 +652194,13 @@
         {}
        ]
       ],
+      "popover-toggle-source.tentative.html": [
+       "00ced8f7070aa4ac677c7fc0a64b3b150f95039b",
+       [
+        null,
+        {}
+       ]
+      ],
       "popover-top-layer-combinations.html": [
        "024794f57853307e15532e7f1c5ff894a61fa51d",
        [
diff --git a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-navigator-clipboard-change-event.tentative.https.html b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-navigator-clipboard-change-event.tentative.https.html
new file mode 100644
index 0000000..37e2e6a7b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-navigator-clipboard-change-event.tentative.https.html
@@ -0,0 +1,127 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>
+  'clipboardchange' event should be fired upon setting clipboard using JS
+</title>
+<link rel="help" href="https://www.w3.org/TR/clipboard-apis/#clipboard-event-clipboardchange" />
+
+<body>
+  Body needed for test_driver.click()
+  <p><button id="button">Put payload in the clipboard</button></p>
+  <div id="output"></div>
+  <iframe id="iframe" srcdoc="<p>Some text</p>"></iframe>
+  <link rel="help" href="https://issues.chromium.org/issues/41442253" />
+
+  <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="resources/user-activation.js"></script>
+
+  <script>
+    function waitForRender() {
+      return new Promise(resolve => {
+        requestAnimationFrame(() => requestAnimationFrame(resolve));
+      });
+    }
+
+    button.onclick = () => document.execCommand("copy");
+    document.oncopy = (ev) => {
+      ev.preventDefault();
+      ev.clipboardData.setData("text/html", `<div>Test html</div>`);
+    };
+
+    function triggerCopyToClipboard() {
+      return test_driver.click(button);
+    }
+
+    promise_test(async (test) => {
+      let clipboardChangeEventCount = 0;
+      let eventType = "";
+      navigator.clipboard.addEventListener("clipboardchange", (ev) => {
+        clipboardChangeEventCount++;
+        eventType = ev.type;
+      });
+      await triggerCopyToClipboard();
+      assert_equals(clipboardChangeEventCount, 1, "clipboardchange event should be called exactly once");
+      assert_equals(eventType, "clipboardchange", "Event type should be 'clipboardchange'");
+    }, "clipboardchange event is invoked");
+
+    promise_test(async (test) => {
+      await tryGrantWritePermission();
+      let clipboardChangeEventCount = 0;
+      navigator.clipboard.addEventListener("clipboardchange", (ev) => {
+        clipboardChangeEventCount++;
+      });
+      await navigator.clipboard.writeText("Test text");
+      await waitForRender();
+      assert_equals(clipboardChangeEventCount, 1, "clipboardchange event should be called exactly once");
+    }, "clipboardchange event is invoked with async clipboard API");
+
+    promise_test(async (test) => {
+      let onClipboardChangeAttributeCount = 0;
+      navigator.clipboard.onclipboardchange = () => {
+        onClipboardChangeAttributeCount++;
+      };
+      await triggerCopyToClipboard();
+      assert_equals(onClipboardChangeAttributeCount, 1, "onclipboardchange attribute should be called exactly once");
+    }, "clipboardchange event is invoked using onclipboardchange attribute");
+
+    promise_test(async (test) => {
+      let listenerCallCount = 0;
+      function clipboardChangeListener() {
+        listenerCallCount++;
+      }
+
+      // 1. Add listener and verify it's called
+      navigator.clipboard.addEventListener("clipboardchange", clipboardChangeListener);
+      await triggerCopyToClipboard();
+      assert_equals(listenerCallCount, 1, "Event listener should be called exactly once after adding");
+
+      // 2. Remove listener and verify it's not called
+      navigator.clipboard.removeEventListener("clipboardchange", clipboardChangeListener);
+      await triggerCopyToClipboard();
+      assert_equals(listenerCallCount, 1, "Event listener should not be called after removing");
+
+      // 3. Re-add listener and verify it's called again
+      navigator.clipboard.addEventListener("clipboardchange", clipboardChangeListener);
+      await triggerCopyToClipboard();
+      assert_equals(listenerCallCount, 2, "Event listener should be called exactly once after re-adding");
+    }, "clipboardchange event listener behavior when adding, removing, and re-adding");
+
+    promise_test(async (test) => {
+      // Focus the document and acquire permission to write to the clipboard
+      await test_driver.click(document.body);
+      await tryGrantWritePermission();
+
+      const iframe = document.getElementById('iframe');
+
+      let frameEventCount = 0;
+      let focusEventFired = false;
+      iframe.contentWindow.addEventListener("focus", () => {
+        focusEventFired = true;
+      });
+
+      // Add listener to iframe
+      iframe.contentWindow.navigator.clipboard.addEventListener("clipboardchange", () => {
+        assert_true(focusEventFired, "focus event should fire before clipboardchange event");
+        frameEventCount++;
+      });
+
+      // Ensure iFrame doesn't have the focus
+      assert_false(iframe.contentWindow.document.hasFocus(), "iFrame should not have focus");
+      assert_false(focusEventFired, "focus event should not have fired yet");
+
+      // Trigger multiple clipboard changes
+      await navigator.clipboard.writeText("Test text");
+      await navigator.clipboard.writeText("Test text 2");
+      await waitForRender();
+
+      assert_equals(frameEventCount, 0, "iframe should not recieve any clipboardchange event yet");
+
+      iframe.focus();
+      assert_true(iframe.contentWindow.document.hasFocus(), "iFrame should have focus");
+      assert_equals(frameEventCount, 1, "iframe should receive event only 1 event after focus");
+    }, "clipboardchange event should only fire in the focused context");
+  </script>
+</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/content-with-transform-ref.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/content-with-transform-ref.html
index 8c2b65e6..10166b1 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/content-with-transform-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/content-with-transform-ref.html
@@ -7,7 +7,7 @@
   contain: paint;
   width: 100px;
   height: 100px;
-  transform: scale(2.0, 3.0);
+  transform: scale3d(2.0, 3.0, 1.0);
 }
 
 .embedded {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/inline-with-offset-from-containing-block-ref.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/inline-with-offset-from-containing-block-ref.html
index 4a66af4..2eebf8c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/inline-with-offset-from-containing-block-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/inline-with-offset-from-containing-block-ref.html
@@ -6,7 +6,7 @@
 
 <style>
   .outer {
-    transform: scale(2);
+    transform: scale3d(2, 2, 1);
     width: 100vw;
     text-align: center;
   }
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-optgroup-arrow-keys.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-optgroup-arrow-keys.tentative.html
new file mode 100644
index 0000000..00ba0e3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-optgroup-arrow-keys.tentative.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://issues.chromium.org/issues/417119055">
+<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="/resources/testdriver-actions.js"></script>
+
+<style>
+select, ::picker(select) {
+  appearance: base-select;
+}
+</style>
+
+<select>
+  <optgroup label=group1>
+    <option class=one>one</option>
+    <option class=two>two</option>
+  </optgroup>
+  <optgroup label=group2>
+    <option class=three>three</option>
+    <option class=four>four</option>
+  </optgroup>
+  <optgroup label=group3>
+    <option class=five>five</option>
+    <option class=six>six</option>
+  </optgroup>
+</select>
+
+<script>
+const ArrowUp = '\uE013';
+const ArrowDown = '\uE015';
+const Space = ' ';
+
+function sendKey(key) {
+  return (new test_driver.Actions()
+    .keyDown(key)
+    .keyUp(key))
+    .send();
+}
+
+promise_test(async () => {
+  const select = document.querySelector('select');
+  const options = [
+    document.querySelector('.one'),
+    document.querySelector('.two'),
+    document.querySelector('.three'),
+    document.querySelector('.four'),
+    document.querySelector('.five'),
+    document.querySelector('.six')
+  ];
+
+  assert_equals(getComputedStyle(select).appearance, 'base-select',
+    'appearance:base-select must be supported to run this test.');
+
+  select.focus();
+  await sendKey(Space);
+  assert_true(select.matches(':open'),
+    'Space should open picker.');
+  assert_equals(document.activeElement, options[0],
+    'First option should be initially focused.');
+
+  for (let i = 1; i < 6; i++) {
+    await sendKey(ArrowDown);
+    assert_equals(document.activeElement, options[i],
+      `Option ${i} should be focused after ArrowDown.`);
+  }
+
+  for (let i = 4; i > -1; i--) {
+    await sendKey(ArrowUp);
+    assert_equals(document.activeElement, options[i],
+      `Option ${i} should be focused after ArrowUp.`);
+  }
+}, 'Keyboard navigation forwards and backwards should visit each option with optgroups.');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-details-element/details-toggle-source.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-details-element/details-toggle-source.tentative.html
new file mode 100644
index 0000000..1a571f5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-details-element/details-toggle-source.tentative.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/9111">
+<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="/resources/testdriver-actions.js"></script>
+<script src="../../popovers/resources/toggle-event-source-test.js"></script>
+
+<button id=commandsource commandfor=details command=toggle>command source</button>
+<details id=details>
+  <summary>summary</summary>
+  details
+</details>
+
+<details id=detailsWithoutSummary>
+  details without summary
+</details>
+
+<script>
+const details = document.getElementById('details');
+const detailsWithoutSummary = document.getElementById('detailsWithoutSummary');
+const summary = details.querySelector('summary');
+const commandsource = document.getElementById('commandsource');
+
+async function click(element) {
+  // Click halfway up the element to click the activatable summary instead of
+  // the details.
+  const height = element.getBoundingClientRect().height;
+  return (new test_driver.Actions()
+    .pointerMove(0, -height / 2, {origin: element})
+    .pointerDown()
+    .pointerUp())
+    .send();
+}
+
+createToggleEventSourceTest({
+  description: 'ToggleEvent.source on <details> elements: details.open.',
+  target: details,
+  openFunc: async () => details.open = true,
+  closeFunc: async () => details.open = false,
+  openSource: null,
+  closeSource: null,
+  skipBeforetoggle: true
+});
+
+createToggleEventSourceTest({
+  description: 'ToggleEvent.source on <details> elements: click summary.',
+  target: details,
+  openFunc: async () => summary.click(),
+  closeFunc: async () => summary.click(),
+  openSource: null,
+  closeSource: null,
+  skipBeforetoggle: true
+});
+
+createToggleEventSourceTest({
+  description: 'ToggleEvent.source on <details> elements: click details.',
+  target: detailsWithoutSummary,
+  openFunc: async () => await click(detailsWithoutSummary),
+  closeFunc: async () => await click(detailsWithoutSummary),
+  openSource: null,
+  closeSource: null,
+  skipBeforetoggle: true
+});
+
+createToggleEventSourceTest({
+  description: 'ToggleEvent.source on <details> elements: command invokers.',
+  target: details,
+  openFunc: async () => commandsource.click(),
+  closeFunc: async () => commandsource.click(),
+  openSource: commandsource,
+  closeSource: commandsource,
+  skipBeforetoggle: true
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-toggle-source.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-toggle-source.tentative.html
new file mode 100644
index 0000000..7e6fe25d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-toggle-source.tentative.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/9111">
+<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="/resources/testdriver-actions.js"></script>
+<script src="../../popovers/resources/toggle-event-source-test.js"></script>
+
+<button id=showmodalbutton commandfor=dialog command=show-modal>show modal</button>
+<dialog id=dialog>
+  dialog
+  <button id=closebutton commandfor=dialog command=close>close</button>
+</dialog>
+
+<script>
+const showmodalbutton = document.getElementById('showmodalbutton');
+const dialog = document.getElementById('dialog');
+
+createToggleEventSourceTest({
+  description: 'ToggleEvent.source on <dialog> elements: dialog.showModal().',
+  target: dialog,
+  openFunc: async () => dialog.showModal(),
+  closeFunc: async () => dialog.close(),
+  openSource: null,
+  closeSource: null
+});
+
+createToggleEventSourceTest({
+  description: 'ToggleEvent.soruce on <dialog> elements: command button.',
+  target: dialog,
+  openFunc: async () => showmodalbutton.click(),
+  closeFunc: async () => closebutton.click(),
+  openSource: showmodalbutton,
+  closeSource: closebutton
+});
+
+createToggleEventSourceTest({
+  description: 'ToggleEvent.soruce on <dialog> elements: open with showModal, close with button.',
+  target: dialog,
+  openFunc: async () => dialog.showModal(),
+  closeFunc: async () => closebutton.click(),
+  openSource: null,
+  closeSource: closebutton
+});
+
+createToggleEventSourceTest({
+  description: 'ToggleEvent.soruce on <dialog> elements: open with button, close with dialog.close().',
+  target: dialog,
+  openFunc: async () => showmodalbutton.click(),
+  closeFunc: async () => dialog.close(),
+  openSource: showmodalbutton,
+  closeSource: null
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-toggle-source.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-toggle-source.tentative.html
new file mode 100644
index 0000000..00ced8f7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-toggle-source.tentative.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/9111">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/toggle-event-source-test.js"></script>
+
+<button id=popoversource popovertarget=popover>popovertarget source</button>
+<button id=commandsource commandfor=popover command=show-popover>command source</button>
+<div id=popover popover=auto>
+  popover
+  <button id=popoversourcehide popovertarget=popover>popovertarget source</button>
+  <button id=commandsourcehide commandfor=popover command=hide-popover>command source</button>
+</div>
+
+<script>
+const popoversource = document.getElementById('popoversource');
+const popoversourcehide = document.getElementById('popoversourcehide');
+const commandsource = document.getElementById('commandsource');
+const commandsourcehide = document.getElementById('commandsourcehide');
+const popover = document.getElementById('popover');
+
+let beforetoggleEvent = null;
+let toggleEvent = null;
+popover.addEventListener('beforetoggle', event => beforetoggleEvent = event);
+popover.addEventListener('toggle', event => toggleEvent = event);
+
+createToggleEventSourceTest({
+  description: 'ToggleEvent.source on popover elements: showPopover() without source.',
+  target: popover,
+  openFunc: async () => popover.showPopover(),
+  closeFunc: async () => popover.hidePopover(),
+  openSource: null,
+  closeSource: null
+});
+
+createToggleEventSourceTest({
+  description: 'ToggleEvent.source on popover elements: showPopover() with source.',
+  target: popover,
+  openFunc: async () => popover.showPopover({source: popoversource}),
+  closeFunc: async () => popover.hidePopover(),
+  openSource: popoversource,
+  closeSource: null
+});
+
+createToggleEventSourceTest({
+  description: 'ToggleEvent.source on popover elements: Calling click() on a popovertarget button.',
+  target: popover,
+  openFunc: async () => popoversource.click(),
+  closeFunc: async () => popoversourcehide.click(),
+  openSource: popoversource,
+  closeSource: popoversourcehide
+});
+
+createToggleEventSourceTest({
+  description: 'ToggleEvent.source on popover elements: Calling click() on a command button.',
+  target: popover,
+  openFunc: async () => commandsource.click(),
+  closeFunc: async () => commandsourcehide.click(),
+  openSource: commandsource,
+  closeSource: commandsourcehide
+});
+
+createToggleEventSourceTest({
+  description: 'ToggleEvent.source on popover elements: showPopover() then popovertarget button.',
+  target: popover,
+  openFunc: async () => popover.showPopover(),
+  closeFunc: async () => popoversourcehide.click(),
+  openSource: null,
+  closeSource: popoversourcehide
+});
+
+createToggleEventSourceTest({
+  description: 'ToggleEvent.source on popover elements: showPopover(invoker) then popovertarget button.',
+  target: popover,
+  openFunc: async () => popover.showPopover({source: popoversource}),
+  closeFunc: async () => popoversourcehide.click(),
+  openSource: popoversource,
+  closeSource: popoversourcehide
+});
+
+createToggleEventSourceTest({
+  description: 'ToggleEvent.source on popover elements: popovertarget button then hidePopover().',
+  target: popover,
+  openFunc: async () => popoversource.click(),
+  closeFunc: async () => popover.hidePopover(),
+  openSource: popoversource,
+  closeSource: null
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/resources/toggle-event-source-test.js b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/resources/toggle-event-source-test.js
new file mode 100644
index 0000000..93ecd27
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/resources/toggle-event-source-test.js
@@ -0,0 +1,76 @@
+function createToggleEventSourceTest({
+    description,
+    target,
+    openFunc,
+    closeFunc,
+    openSource,
+    closeSource,
+    skipBeforetoggle}) {
+  promise_test(async () => {
+    let beforetoggleEvent = null;
+    let beforetoggleDuplicate = false;
+    let toggleEvent = null;
+    let toggleDuplicate = false;
+    target.addEventListener('beforetoggle', event => {
+      if (beforetoggleEvent) {
+        beforetoggleDuplicate = true;
+      }
+      beforetoggleEvent = event;
+    });
+    target.addEventListener('toggle', event => {
+      if (toggleEvent) {
+        toggleDuplicate = true;
+      }
+      toggleEvent = event;
+    });
+
+    await openFunc();
+    await new Promise(requestAnimationFrame);
+    await new Promise(requestAnimationFrame);
+    if (!skipBeforetoggle) {
+      assert_true(!!beforetoggleEvent,
+        'An opening beforetoggle event should have been fired.');
+      assert_false(beforetoggleDuplicate,
+        'Only one opening beforetoggle event should have been fired.');
+      assert_equals(beforetoggleEvent.newState, 'open',
+        'beforetoggle newState should be open.');
+      assert_equals(beforetoggleEvent.source, openSource,
+        'Opening beforetoggle.source.');
+    }
+    assert_true(!!toggleEvent,
+      'An opening toggle event should have been fired.');
+    assert_false(toggleDuplicate,
+      'Only one opening toggle event should have been fired.');
+    assert_equals(toggleEvent.newState, 'open',
+      'toggle newstate should be open.');
+    assert_equals(toggleEvent.source, openSource,
+      'Opening toggle.source.');
+    beforetoggleEvent = null;
+    beforetoggleDuplicate = false;
+    toggleEvent = null;
+    toggleDuplicate = false;
+
+    await closeFunc();
+    await new Promise(requestAnimationFrame);
+    await new Promise(requestAnimationFrame);
+
+    if (!skipBeforetoggle) {
+      assert_true(!!beforetoggleEvent,
+        'A closing beforetoggle event should have been fired.');
+      assert_false(beforetoggleDuplicate,
+        'Only one closing beforetoggle event should have been fired.');
+      assert_equals(beforetoggleEvent.newState, 'closed',
+        'beforetoggle newState should be closed.');
+      assert_equals(beforetoggleEvent.source, closeSource,
+        'Closing beforetoggle.source.');
+    }
+    assert_true(!!toggleEvent,
+      'A closing toggle event should have been fired.');
+    assert_false(toggleDuplicate,
+      'Only one closing toggle event should have been fired.');
+    assert_equals(toggleEvent.newState, 'closed',
+      'toggle newstate should be closed.');
+    assert_equals(toggleEvent.source, closeSource,
+      'Closing toggle.source.');
+  }, description);
+}
diff --git a/third_party/blink/web_tests/external/wpt/lint.ignore b/third_party/blink/web_tests/external/wpt/lint.ignore
index 955e5df..b391808 100644
--- a/third_party/blink/web_tests/external/wpt/lint.ignore
+++ b/third_party/blink/web_tests/external/wpt/lint.ignore
@@ -282,6 +282,7 @@
 SET TIMEOUT: service-workers/service-worker/resources/opaque-response-preloaded-xhr.html
 SET TIMEOUT: service-workers/service-worker/resources/performance-timeline-worker.js
 SET TIMEOUT: service-workers/service-worker/resources/resource-timing-worker.js
+SET TIMEOUT: soft-navigation-heuristics/smoke/tentative/task-attribution.html
 SET TIMEOUT: shadow-dom/Document-prototype-currentScript.html
 SET TIMEOUT: shadow-dom/scroll-to-the-fragment-in-shadow-tree.html
 SET TIMEOUT: shadow-dom/slotchange-event.html
diff --git a/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/smoke/tentative/task-attribution.html b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/smoke/tentative/task-attribution.html
new file mode 100644
index 0000000..8f6f93c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/smoke/tentative/task-attribution.html
@@ -0,0 +1,155 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>Soft Navigation Detection: Task Attribution.</title>
+    <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>
+      // Appends a new div with a greeting to the document body.
+      function updateUI() {
+        const greeting = document.createElement("div");
+        greeting.textContent = "Hello, World.";
+        document.body.appendChild(greeting);
+      }
+
+      // Basic soft navigation (see basic.html) which directly does its work
+      // in the click handler. This is our baseline - the methods below
+      // differ in that they require propagating the soft navigation attribution
+      // to a different task.
+      function noSchedulingHop() {
+        updateUI();
+        history.pushState({}, "", "/no-scheduling-hop");
+      }
+
+      // A soft navigation where the click handler schedules its work with
+      // setTimeout, 0ms delay.
+      // https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout
+      function withSetTimeout0() {
+        setTimeout(() => {
+          updateUI();
+          history.pushState({}, "", "/with-set-timeout-0");
+        }, 0);
+      }
+
+      // A soft navigation where the click handler schedules its work with
+      // setTimeout, 10ms delay.
+      // https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout
+      function withSetTimeout10() {
+        setTimeout(() => {
+          updateUI();
+          history.pushState({}, "", "/with-set-timeout-10");
+        }, 10);
+      }
+
+      // A soft navigation where the click handler schedules its work with
+      // two setTimeouts.
+      // https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout
+      function twoSetTimeouts() {
+        setTimeout(updateUI, 20);
+        setTimeout(() => {
+          history.pushState({}, "", "/two-set-timeouts");
+        }, 10);
+      }
+
+      // A soft navigation where the click handler schedules its UI work with
+      // setTimeout and the URL update with a sync history.pushState call.
+      // https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout
+      function setTimeoutAndSyncUrlUpdate() {
+        setTimeout(updateUI, 10);
+        history.pushState({}, "", "/set-timeout-and-sync-url-update");
+      }
+
+      // A soft navigation where the click handler synchronously updates its UI
+      // and schedules the URL update with a setTimeout.
+      // https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout
+      function setTimeoutForUrlUpdate() {
+        updateUI();
+        setTimeout(() => {
+          history.pushState({}, "", "/set-timeout-for-url-update");
+        }, 10);
+      }
+
+      // A soft navigation where the click handler schedules its work with
+      // scheduler.postTask.
+      // https://developer.mozilla.org/en-US/docs/Web/API/Scheduler/postTask
+      function withSchedulerPostTask() {
+        scheduler.postTask(() => {
+          updateUI();
+          history.pushState({}, "", "/with-scheduler-post-task");
+        });
+      }
+
+      // A soft navigation where the click handler yields
+      // (using scheduler.yield) between UI update and URL update.
+      async function withSchedulerYield() {
+        updateUI();
+        await scheduler.yield();
+        history.pushState({}, "", "/with-scheduler-yield");
+      }
+
+      // A soft navigation where the click handler schedules its work with
+      // requestAnimationFrame.
+      // https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame
+      function withRequestAnimationFrame() {
+        requestAnimationFrame(() => {
+          updateUI();
+          history.pushState({}, "", "/with-request-animation-frame");
+        });
+      }
+    </script>
+  </head>
+  <body>
+    <div id="no-scheduling-hop" onclick="noSchedulingHop()">Click here!</div>
+    <div id="with-set-timeout-0" onclick="withSetTimeout0()">Click here!</div>
+    <div id="with-set-timeout-10" onclick="withSetTimeout10()">Click here!</div>
+    <div id="two-set-timeouts" onclick="twoSetTimeouts()">Click here!</div>
+    <div id="set-timeout-and-sync-url-update" onclick="setTimeoutAndSyncUrlUpdate()">
+      Click here!
+    </div>
+    <div id="set-timeout-for-url-update" onclick="setTimeoutForUrlUpdate()">Click here!</div>
+    <div id="with-scheduler-post-task" onclick="withSchedulerPostTask()">Click here!</div>
+    <div id="with-scheduler-yield" onclick="withSchedulerYield()">Click here!</div>
+    <div id="with-request-animation-frame" onclick="withRequestAnimationFrame()">Click here!</div>
+
+    <script>
+      function test_template(test_id, description) {
+        promise_test(async (t) => {
+          let entries;
+          new PerformanceObserver((list, observer) => {
+            entries = list.getEntries();
+            observer.disconnect();
+          }).observe({ type: "soft-navigation" });
+          if (test_driver) {
+            test_driver.click(document.getElementById(test_id));
+          }
+          await t.step_wait(() => entries !== undefined, "Soft navigation event not fired.");
+
+          assert_equals(entries.length, 1, "Expected exactly one soft navigation.");
+          assert_equals(
+            entries[0].name.replace(/.*\//, ""),
+            test_id,
+            "URL should end with the test ID.",
+          );
+        }, description);
+      }
+
+      test_template("no-scheduling-hop", "No scheduling hop.");
+      test_template("with-set-timeout-0", "With set_timeout, 0ms delay.");
+      test_template("with-set-timeout-10", "With set_timeout, 10ms delay.");
+      test_template("two-set-timeouts", "With two set_timeouts.");
+      test_template("set-timeout-and-sync-url-update", "With set_timeout and sync URL update.");
+      if (scheduler.postTask) {
+        // Skip test if scheduler.postTask is not supported.
+        test_template("with-scheduler-post-task", "With scheduler.postTask.");
+      }
+      if (scheduler.yield) {
+        // Skip test if scheduler.yield is not supported.
+        test_template("with-scheduler-yield", "With scheduler.yield.");
+      }
+      test_template("with-request-animation-frame", "With requestAnimationFrame.");
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/tensor.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/tensor.https.any.js
index 03057a6..1a46c35b 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/tensor.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/tensor.https.any.js
@@ -128,127 +128,6 @@
   }, `${testName} / ${tensorDescriptor.dataType}`);
 };
 
-/**
- * WebNN create constant tensor test.
- * @param {String} testName - The name of the test operation.
- * @param {MLOperandDescriptor} descriptor - The intended operand specs.
- */
-const testCreateConstantTensor = (testName, descriptor) => {
-  let mlContext;
-  let isConstantTensorSupported = false;
-  promise_setup(async () => {
-    try {
-      mlContext = await navigator.ml.createContext(contextOptions);
-    } catch (error) {
-      throw new AssertionError(
-          `Unable to create context for ${variant} variant. ${error}`);
-    }
-
-    // Check if WebNN has constant tensor support.
-    try {
-      await mlContext.createConstantTensor(
-          {
-            dataType: 'float32',
-            shape: [1],
-          },
-          new Float32Array([0xAA]));
-      isConstantTensorSupported = true;
-    } catch (error) {
-      if (error.name !== 'NotSupportedError') {
-        throw error;
-      }
-    }
-  });
-
-  promise_test(async t => {
-    if (!isConstantTensorSupported) {
-      return;
-    }
-
-    const inputData =
-        new TypedArrayDict[descriptor.dataType](sizeOfShape(descriptor.shape))
-            .fill(0xAA);
-    if (!mlContext.opSupportLimits().constant.dataTypes.includes(
-            descriptor.dataType)) {
-      await promise_rejects_js(
-          t, TypeError, mlContext.createConstantTensor(descriptor, inputData));
-      return;
-    }
-
-    const mlTensor =
-        await mlContext.createConstantTensor(descriptor, inputData);
-    assert_true(mlTensor.constant, 'constant tensors should be constant.');
-    assert_false(mlTensor.readable, 'constant tensors should not be readable.');
-    assert_false(mlTensor.writable, 'constant tensors should not be writable.');
-  }, `${testName} / ${descriptor.dataType}`);
-
-  promise_test(async t => {
-    if (!isConstantTensorSupported) {
-      return;
-    }
-
-    try {
-      const inputDataTooBig = new TypedArrayDict[descriptor.dataType](
-          sizeOfShape(descriptor.shape) + 1);
-      await promise_rejects_js(
-          t, TypeError,
-          mlContext.createConstantTensor(descriptor, inputDataTooBig));
-    } catch (error) {
-      if (error instanceof RangeError) {
-        return;  // Skip test when dataType is too big.
-      } else {
-        throw error;
-      }
-    }
-  }, `${testName} / ${descriptor.dataType} / source data too big`);
-
-  promise_test(async t => {
-    if (!isConstantTensorSupported) {
-      return;
-    }
-
-    try {
-      const inputDataTooSmall = new TypedArrayDict[descriptor.dataType](
-          sizeOfShape(descriptor.shape) - 1);
-      await promise_rejects_js(
-          t, TypeError,
-          mlContext.createConstantTensor(descriptor, inputDataTooSmall));
-    } catch (error) {
-      if (error instanceof RangeError) {
-        return;  // Skip test when dataType is too big.
-      } else {
-        throw error;
-      }
-    }
-  }, `${testName} / ${descriptor.dataType} / source data too small`);
-};
-
-/**
- * Same as above, but expect constant tensor creation to fail.
- * @param {String} testName - The name of the test operation.
- * @param {MLOperandDescriptor} descriptor - The intended operand specs.
- */
-const testCreateConstantTensorFails = (testName, descriptor) => {
-  let mlContext;
-
-  promise_setup(async () => {
-    try {
-      mlContext = await navigator.ml.createContext(contextOptions);
-    } catch (error) {
-      throw new AssertionError(
-          `Unable to create context for ${variant} variant. ${error}`);
-    }
-  });
-
-  promise_test(async t => {
-    await promise_rejects_js(
-        t, TypeError,
-        mlContext.createConstantTensor(
-            descriptor,
-            new TypedArrayDict[descriptor.dataType](
-                sizeOfShape(descriptor.shape))));
-  }, `${testName} / ${descriptor.dataType}`);
-};
 
 promise_test(async t => {
   const tensorDescriptor = {
@@ -545,7 +424,6 @@
   const shape = [3, 5];
   let inputs = {};
   let outputs = {};
-  let isConstantTensorSupported = false;
   promise_setup(async () => {
     try {
       mlContext = await navigator.ml.createContext(contextOptions);
@@ -553,22 +431,6 @@
       throw new AssertionError(
           `Unable to create context for ${variant} variant. ${e}`);
     }
-
-    // Check if WebNN has constant tensor support.
-    try {
-      await mlContext.createConstantTensor(
-          {
-            dataType: 'float32',
-            shape: [1],
-          },
-          new Float32Array([0xAA]));
-      isConstantTensorSupported = true;
-    } catch (error) {
-      if (error.name !== 'NotSupportedError') {
-        throw error;
-      }
-    }
-
     // Construct a simple graph: A = B + C, with two outputs.
     const builder = new MLGraphBuilder(mlContext);
     const tensorDescriptor = {
@@ -1227,98 +1089,6 @@
         mlContext, dispatchOutputs['output1'],
         new Float32Array(sizeOfShape(shape)).fill(3));
   }, `${testName} / same name diff outputs tensors destroy`);
-
-  promise_test(async () => {
-    if (!isConstantTensorSupported) {
-      return;
-    }
-
-    let constantTensor = await mlContext.createConstantTensor(
-        {
-          dataType: 'float32',
-          shape: shape,
-        },
-        new Float32Array(sizeOfShape(shape)).fill(3.0));
-
-    const builder = new MLGraphBuilder(mlContext);
-    const lhsConstantOperand = builder.constant(constantTensor);
-    const rhsConstantOperand = builder.constant(constantTensor);
-    const outputOperand = builder.add(lhsConstantOperand, rhsConstantOperand);
-    const graphWithOnlyConstants =
-        await builder.build({'output': outputOperand});
-
-    const outputTensor = await mlContext.createTensor(
-        getDescriptorFromTensor(outputs['output1']));
-
-    // Output = LHS + RHS = 3 + 3 = 6
-    mlContext.dispatch(graphWithOnlyConstants, {}, {'output': outputTensor});
-
-    await assert_tensor_data_equals(
-        mlContext, outputTensor,
-        new Float32Array(sizeOfShape(shape)).fill(6.0));
-  }, `${testName} / same constant same graph`);
-
-  promise_test(async () => {
-    if (!isConstantTensorSupported) {
-      return;
-    }
-
-    const rhsConstantTensor = await mlContext.createConstantTensor(
-        {
-          dataType: 'float32',
-          shape: shape,
-        },
-        new Float32Array(sizeOfShape(shape)).fill(3.0));
-
-    const lhsInputOperandDesc = {dataType: 'float32', shape};
-
-    let graphWithConstants;
-    {
-      const builder = new MLGraphBuilder(mlContext);
-      const lhsOperand = builder.input('lhs', lhsInputOperandDesc);
-      const rhsConstantOperand = builder.constant(rhsConstantTensor);
-      const outputOperand = builder.sub(lhsOperand, rhsConstantOperand);
-      graphWithConstants = await builder.build({'output': outputOperand});
-    }
-
-    const lhsTensor =
-        await mlContext.createTensor(getDescriptorFromTensor(inputs['lhs']));
-    mlContext.writeTensor(
-        lhsTensor, new Float32Array(sizeOfShape(shape)).fill(5.0));
-
-    const outputTensor = await mlContext.createTensor(
-        getDescriptorFromTensor(outputs['output1']));
-
-    // Output = LHS - RHS = 5 - 3 = 2
-    mlContext.dispatch(
-        graphWithConstants, {
-          'lhs': lhsTensor,
-        },
-        {'output': outputTensor});
-
-    // Create another graph reusing the same constants.
-    {
-      const builder = new MLGraphBuilder(mlContext);
-      const lhsOperand = builder.input('lhs', lhsInputOperandDesc);
-      const rhsConstantOperand = builder.constant(rhsConstantTensor);
-      const outputOperand = builder.sub(lhsOperand, rhsConstantOperand);
-      graphWithConstants = await builder.build({'output': outputOperand});
-    }
-
-    mlContext.writeTensor(
-        lhsTensor, new Float32Array(sizeOfShape(shape)).fill(4.0));
-
-    // Output = LHS - RHS = 4 - 3 = 1
-    mlContext.dispatch(
-        graphWithConstants, {
-          'lhs': lhsTensor,
-        },
-        {'output': outputTensor});
-
-    await assert_tensor_data_equals(
-        mlContext, outputTensor,
-        new Float32Array(sizeOfShape(shape)).fill(1.0));
-  }, `${testName} / same constant multiple graphs`);
 };
 
 if (navigator.ml) {
@@ -1334,14 +1104,6 @@
     shape: [kMaxUnsignedLong, kMaxUnsignedLong, kMaxUnsignedLong]
   });
 
-  testCreateConstantTensor('createConstant', {dataType: 'int32', shape: [4]});
-  testCreateConstantTensor(
-      'createConstant', {dataType: 'uint8', shape: [3, 2, 4]});
-
-  testCreateConstantTensorFails(
-      'createConstantFailsEmptyDimension',
-      {dataType: 'int32', shape: [2, 0, 3]});
-
   testDestroyTensor('destroyTwice');
   testReadTensor('read');
   testWriteTensor('write');
diff --git a/third_party/blink/web_tests/fast/dom/Window/resources/window-property-collector.js b/third_party/blink/web_tests/fast/dom/Window/resources/window-property-collector.js
index db94eb2b..e5a1a3a1 100644
--- a/third_party/blink/web_tests/fast/dom/Window/resources/window-property-collector.js
+++ b/third_party/blink/web_tests/fast/dom/Window/resources/window-property-collector.js
@@ -28,12 +28,18 @@
 
 function emitExpectedResult(path, expected)
 {
-    if (path[0] == 'clientInformation' // Just an alias for navigator.
-        || path[0] == 'testRunner' // Skip testRunner since they are only for testing.
-        || path[0] == 'eventSender'// Skip eventSender since they are only for testing.
-        || path[1] == 'gpuBenchmarking') { // Skip gpuBenchmarking since they're only for testing.
-        return;
-    }
+  if (path[0] == 'clientInformation'  // Just an alias for navigator.
+      || path[0] ==
+          'testRunner'  // Skip testRunner since they are only for testing.
+      || path[0] ==
+          'eventSender'  // Skip eventSender since they are only for testing.
+      || path[0] == 'navigator' &&
+          path[1] == 'clipboard'  // crbug.com/417636704 Clipboardchange event
+                                  // is not enabled on all platforms.
+      || path[1] == 'gpuBenchmarking') {  // Skip gpuBenchmarking since they're
+                                          // only for testing.
+    return;
+  }
 
     // Skip the properties which are hard to expect a stable result.
     if (path[0] == 'accessibilityController' // we can hardly estimate the states of the cached WebAXObjects.
diff --git a/third_party/blink/web_tests/platform/mac-mac13/external/wpt/webrtc-encoded-transform/RTCRtpScriptTransform-encoded-transform.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac13/external/wpt/webrtc-encoded-transform/RTCRtpScriptTransform-encoded-transform.https-expected.txt
new file mode 100644
index 0000000..5b37deb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac13/external/wpt/webrtc-encoded-transform/RTCRtpScriptTransform-encoded-transform.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+All subtests passed and are omitted for brevity.
+See https://chromium.googlesource.com/chromium/src/+/HEAD/docs/testing/writing_web_tests.md#Text-Test-Baselines for details.
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/mac-mac14/external/wpt/webrtc-encoded-transform/RTCRtpScriptTransform-encoded-transform.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac14/external/wpt/webrtc-encoded-transform/RTCRtpScriptTransform-encoded-transform.https-expected.txt
new file mode 100644
index 0000000..cae618b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac14/external/wpt/webrtc-encoded-transform/RTCRtpScriptTransform-encoded-transform.https-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Once a transform has been attached, it cannot be reused elsewhere.
+  assert_throws_dom: set a used transform 3 function "() => transceiver2.sender.transform = senderTransform" did not throw
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/win10/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/subgraph.https.any.serviceworker_cpu-expected.txt b/third_party/blink/web_tests/platform/win10/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/subgraph.https.any.serviceworker_cpu-expected.txt
new file mode 100644
index 0000000..2cf9296
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win10/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/subgraph.https.any.serviceworker_cpu-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] batchNormalization options.axis=0 + gelu
+  assert_less_than_equal: assert_array_approx_equals_ulp: test batchNormalization gelu float32 actual -0.035951387137174606 should be close enough to expected -0.03595117852091789 by ULP distance: expected a number less than or equal to 24n but got 56n
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/win10/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/subgraph.https.any.worker_cpu-expected.txt b/third_party/blink/web_tests/platform/win10/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/subgraph.https.any.worker_cpu-expected.txt
new file mode 100644
index 0000000..2cf9296
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win10/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/subgraph.https.any.worker_cpu-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] batchNormalization options.axis=0 + gelu
+  assert_less_than_equal: assert_array_approx_equals_ulp: test batchNormalization gelu float32 actual -0.035951387137174606 should be close enough to expected -0.03595117852091789 by ULP distance: expected a number less than or equal to 24n but got 56n
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/resources/global-interface-listing.js b/third_party/blink/web_tests/resources/global-interface-listing.js
index 855f5d9..ccc9b57 100644
--- a/third_party/blink/web_tests/resources/global-interface-listing.js
+++ b/third_party/blink/web_tests/resources/global-interface-listing.js
@@ -139,6 +139,9 @@
     Notification: new Set([
       'getter image',
     ]),
+    // crbug.com/417636703 This API is not yet supported on Mac
+    Clipboard:
+        new Set(['getter onclipboardchange', 'setter onclipboardchange']),
   };
 
   // List of all platform-specific global properties. Please update this list
diff --git a/third_party/blink/web_tests/virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-optgroup-arrow-keys.tentative-expected.txt b/third_party/blink/web_tests/virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-optgroup-arrow-keys.tentative-expected.txt
new file mode 100644
index 0000000..561a4c6
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-optgroup-arrow-keys.tentative-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Keyboard navigation forwards and backwards should visit each option with optgroups.
+  assert_equals: appearance:base-select must be supported to run this test. expected "base-select" but got "auto"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/select-parser-relaxation-opt-out/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-optgroup-arrow-keys.tentative-expected.txt b/third_party/blink/web_tests/virtual/select-parser-relaxation-opt-out/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-optgroup-arrow-keys.tentative-expected.txt
new file mode 100644
index 0000000..561a4c6
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/select-parser-relaxation-opt-out/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-optgroup-arrow-keys.tentative-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Keyboard navigation forwards and backwards should visit each option with optgroups.
+  assert_equals: appearance:base-select must be supported to run this test. expected "base-select" but got "auto"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-optgroup-arrow-keys.tentative-expected.txt b/third_party/blink/web_tests/virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-optgroup-arrow-keys.tentative-expected.txt
new file mode 100644
index 0000000..561a4c6
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/select-parser-relaxation/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-optgroup-arrow-keys.tentative-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Keyboard navigation forwards and backwards should visit each option with optgroups.
+  assert_equals: appearance:base-select must be supported to run this test. expected "base-select" but got "auto"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/idlharness.https.any-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/idlharness.https.any-expected.txt
index 1786a57..06e874bd 100644
--- a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/idlharness.https.any-expected.txt
+++ b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/idlharness.https.any-expected.txt
@@ -1,5 +1,19 @@
 This is a testharness.js-based test.
-Found 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
+[FAIL] MLContext interface: operation createConstantTensor(MLOperandDescriptor, AllowSharedBufferSource)
+  assert_own_property: interface prototype object missing non-static operation expected property "createConstantTensor" missing
+[FAIL] MLContext interface: context must inherit property "createConstantTensor(MLOperandDescriptor, AllowSharedBufferSource)" with the proper type
+  assert_inherits: property "createConstantTensor" not found in prototype chain
+[FAIL] MLContext interface: calling createConstantTensor(MLOperandDescriptor, AllowSharedBufferSource) on context with too few arguments must throw TypeError
+  assert_inherits: property "createConstantTensor" not found in prototype chain
+[FAIL] MLTensor interface: attribute constant
+  assert_true: The prototype object must have a property "constant" expected true got false
+[FAIL] MLGraphBuilder interface: operation constant(MLOperandDescriptor, AllowSharedBufferSource)
+  assert_equals: property has wrong .length expected 1 but got 2
+[FAIL] MLGraphBuilder interface: operation constant(MLOperandDataType, MLNumber)
+  assert_equals: property has wrong .length expected 1 but got 2
+[FAIL] MLGraphBuilder interface: operation constant(MLTensor)
+  assert_equals: property has wrong .length expected 1 but got 2
 [FAIL] MLGraphBuilder interface: operation softmax(MLOperand, unsigned long, optional MLOperatorOptions)
   assert_equals: property has wrong .length expected 2 but got 1
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/idlharness.https.any.serviceworker-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/idlharness.https.any.serviceworker-expected.txt
index 1786a57..06e874bd 100644
--- a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/idlharness.https.any.serviceworker-expected.txt
+++ b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/idlharness.https.any.serviceworker-expected.txt
@@ -1,5 +1,19 @@
 This is a testharness.js-based test.
-Found 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
+[FAIL] MLContext interface: operation createConstantTensor(MLOperandDescriptor, AllowSharedBufferSource)
+  assert_own_property: interface prototype object missing non-static operation expected property "createConstantTensor" missing
+[FAIL] MLContext interface: context must inherit property "createConstantTensor(MLOperandDescriptor, AllowSharedBufferSource)" with the proper type
+  assert_inherits: property "createConstantTensor" not found in prototype chain
+[FAIL] MLContext interface: calling createConstantTensor(MLOperandDescriptor, AllowSharedBufferSource) on context with too few arguments must throw TypeError
+  assert_inherits: property "createConstantTensor" not found in prototype chain
+[FAIL] MLTensor interface: attribute constant
+  assert_true: The prototype object must have a property "constant" expected true got false
+[FAIL] MLGraphBuilder interface: operation constant(MLOperandDescriptor, AllowSharedBufferSource)
+  assert_equals: property has wrong .length expected 1 but got 2
+[FAIL] MLGraphBuilder interface: operation constant(MLOperandDataType, MLNumber)
+  assert_equals: property has wrong .length expected 1 but got 2
+[FAIL] MLGraphBuilder interface: operation constant(MLTensor)
+  assert_equals: property has wrong .length expected 1 but got 2
 [FAIL] MLGraphBuilder interface: operation softmax(MLOperand, unsigned long, optional MLOperatorOptions)
   assert_equals: property has wrong .length expected 2 but got 1
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/idlharness.https.any.sharedworker-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/idlharness.https.any.sharedworker-expected.txt
index 1786a57..06e874bd 100644
--- a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/idlharness.https.any.sharedworker-expected.txt
+++ b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/idlharness.https.any.sharedworker-expected.txt
@@ -1,5 +1,19 @@
 This is a testharness.js-based test.
-Found 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
+[FAIL] MLContext interface: operation createConstantTensor(MLOperandDescriptor, AllowSharedBufferSource)
+  assert_own_property: interface prototype object missing non-static operation expected property "createConstantTensor" missing
+[FAIL] MLContext interface: context must inherit property "createConstantTensor(MLOperandDescriptor, AllowSharedBufferSource)" with the proper type
+  assert_inherits: property "createConstantTensor" not found in prototype chain
+[FAIL] MLContext interface: calling createConstantTensor(MLOperandDescriptor, AllowSharedBufferSource) on context with too few arguments must throw TypeError
+  assert_inherits: property "createConstantTensor" not found in prototype chain
+[FAIL] MLTensor interface: attribute constant
+  assert_true: The prototype object must have a property "constant" expected true got false
+[FAIL] MLGraphBuilder interface: operation constant(MLOperandDescriptor, AllowSharedBufferSource)
+  assert_equals: property has wrong .length expected 1 but got 2
+[FAIL] MLGraphBuilder interface: operation constant(MLOperandDataType, MLNumber)
+  assert_equals: property has wrong .length expected 1 but got 2
+[FAIL] MLGraphBuilder interface: operation constant(MLTensor)
+  assert_equals: property has wrong .length expected 1 but got 2
 [FAIL] MLGraphBuilder interface: operation softmax(MLOperand, unsigned long, optional MLOperatorOptions)
   assert_equals: property has wrong .length expected 2 but got 1
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/idlharness.https.any.worker-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/idlharness.https.any.worker-expected.txt
index 1786a57..06e874bd 100644
--- a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/idlharness.https.any.worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/idlharness.https.any.worker-expected.txt
@@ -1,5 +1,19 @@
 This is a testharness.js-based test.
-Found 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
+[FAIL] MLContext interface: operation createConstantTensor(MLOperandDescriptor, AllowSharedBufferSource)
+  assert_own_property: interface prototype object missing non-static operation expected property "createConstantTensor" missing
+[FAIL] MLContext interface: context must inherit property "createConstantTensor(MLOperandDescriptor, AllowSharedBufferSource)" with the proper type
+  assert_inherits: property "createConstantTensor" not found in prototype chain
+[FAIL] MLContext interface: calling createConstantTensor(MLOperandDescriptor, AllowSharedBufferSource) on context with too few arguments must throw TypeError
+  assert_inherits: property "createConstantTensor" not found in prototype chain
+[FAIL] MLTensor interface: attribute constant
+  assert_true: The prototype object must have a property "constant" expected true got false
+[FAIL] MLGraphBuilder interface: operation constant(MLOperandDescriptor, AllowSharedBufferSource)
+  assert_equals: property has wrong .length expected 1 but got 2
+[FAIL] MLGraphBuilder interface: operation constant(MLOperandDataType, MLNumber)
+  assert_equals: property has wrong .length expected 1 but got 2
+[FAIL] MLGraphBuilder interface: operation constant(MLTensor)
+  assert_equals: property has wrong .length expected 1 but got 2
 [FAIL] MLGraphBuilder interface: operation softmax(MLOperand, unsigned long, optional MLOperatorOptions)
   assert_equals: property has wrong .length expected 2 but got 1
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index a717693..a739a31 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -10128,6 +10128,7 @@
     attribute @@toStringTag
     getter newState
     getter oldState
+    getter source
     method constructor
 interface Touch
     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElement/clips-overflow-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElement/clips-overflow-ref.html
new file mode 100644
index 0000000..3582a76
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElement/clips-overflow-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<style>
+#child {
+  width: 100px;
+  height: 100px;
+  background: lightgreen;
+  border: 5px solid black;
+  padding: 10px;
+  contain: paint;
+  position: relative;
+  left: 20px;
+  top: 30px;
+
+}
+#grandchild {
+  width: 300px;
+  height: 300px;
+  background: blue;
+}
+#canvas {
+  width: 200px;
+  height: 200px;
+}
+</style>
+
+<div id=canvas>
+  <div id=child>
+    <div id=grandchild></div>
+  </div>
+</canvas>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElement/clips-overflow.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElement/clips-overflow.html
new file mode 100644
index 0000000..3ec528a3
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElement/clips-overflow.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<title>Canvas.drawElement: test that drawn elements draw box decorations and clip their children</title>
+<link rel="help" href="https://github.com/WICG/html-in-canvas">
+<link rel="author" href="mailto:vmpstr@chromium.org">
+<link rel="match" href="clips-overflow-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<style>
+#child {
+  width: 100px;
+  height: 100px;
+  background: lightgreen;
+  border: 5px solid black;
+  padding: 10px;
+
+}
+#grandchild {
+  width: 300px;
+  height: 300px;
+  background: blue;
+}
+</style>
+
+<canvas id=canvas width="200" height="200" layoutsubtree=true>
+  <div id=child>
+    <div id=grandchild></div>
+  </div>
+</canvas>
+<script>
+function runTest() {
+  canvas.getContext("2d").drawElement(child, 20, 30);
+  requestAnimationFrame(takeScreenshot);
+}
+
+onload = () => runTest();
+
+</script>
+</html>
diff --git a/third_party/crossbench b/third_party/crossbench
index a6913f4..354a6230 160000
--- a/third_party/crossbench
+++ b/third_party/crossbench
@@ -1 +1 @@
-Subproject commit a6913f411c9deff814721268135cba809042a669
+Subproject commit 354a6230a3a7bc437dd3f512a6decd3155eb15fc
diff --git a/third_party/dawn b/third_party/dawn
index bebd40b..f1ac40b 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit bebd40b19d96dea39d582b866dfe751dded1c33c
+Subproject commit f1ac40b54eb53282eff2e587a08fae81de9408ea
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 9e17651..2755713 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 9e176519afd505a405a50d9312c926ef97003ec3
+Subproject commit 2755713bf6b0ca46d6e60124a0633f45c2d54677
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index ed3362eb..d537d56 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-13-3-183-g7172bd11b
-Revision: 7172bd11badd468f6a86dba0b1769d624ead885c
+Version: VER-2-13-3-184-g8082aba5f
+Revision: 8082aba5f2ddc334b6dfe89a10db13a1b120f76e
 CPEPrefix: cpe:/a:freetype:freetype:2.13.3
 License: FTL
 License File: src/docs/FTL.TXT
diff --git a/third_party/freetype/src b/third_party/freetype/src
index 7172bd1..8082aba 160000
--- a/third_party/freetype/src
+++ b/third_party/freetype/src
@@ -1 +1 @@
-Subproject commit 7172bd11badd468f6a86dba0b1769d624ead885c
+Subproject commit 8082aba5f2ddc334b6dfe89a10db13a1b120f76e
diff --git a/third_party/googletest/src b/third_party/googletest/src
index 9f79a95..5719306 160000
--- a/third_party/googletest/src
+++ b/third_party/googletest/src
@@ -1 +1 @@
-Subproject commit 9f79a9597ad9b5394e5c620ebf76824f77ffbde4
+Subproject commit 571930618fa96eabcd05b573285edbee9fc13bae
diff --git a/third_party/openscreen/src b/third_party/openscreen/src
index bca4abc..4b64bb7 160000
--- a/third_party/openscreen/src
+++ b/third_party/openscreen/src
@@ -1 +1 @@
-Subproject commit bca4abcec3de975c0c1b9a60334d9fee1b7f43a1
+Subproject commit 4b64bb75ad30c5d9e170caa21c6f5065583cb49c
diff --git a/third_party/skia b/third_party/skia
index b70fc92..bb255dd 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit b70fc925125553a4eb8bb2f123e225f057aa35d8
+Subproject commit bb255dd0252e256a28542475b22711346c319030
diff --git a/third_party/webrtc b/third_party/webrtc
index 691c150..e30e216 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 691c150f9232901f75368bf741281f5a8f5ddefa
+Subproject commit e30e216756cb9bdee35074b8020a78e26c981d94
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 290c5ae..971ed08 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -12700,6 +12700,7 @@
   <int value="-515913489" label="EphemeralTabUsingBottomSheet:disabled"/>
   <int value="-515066652"
       label="WebAuthenticationAlignErrorTypeForPaymentCredentialCreate:disabled"/>
+  <int value="-514807488" label="TabStripDensityChangeAndroid:disabled"/>
   <int value="-514672474"
       label="PolicyProvidedTrustAnchorsAllowedAtLockScreen:enabled"/>
   <int value="-514076866" label="MovablePartialScreenshot:disabled"/>
@@ -19089,6 +19090,7 @@
   <int value="1891210939" label="enable-blink-features"/>
   <int value="1891218220" label="HttpsFirstDialogUi:disabled"/>
   <int value="1891312456" label="AndroidMessagesProdEndpoint:disabled"/>
+  <int value="1892005713" label="TabStripDensityChangeAndroid:enabled"/>
   <int value="1892124105" label="HistoryClustersVisitDeduping:disabled"/>
   <int value="1892201400" label="enable-password-separated-signin-flow"/>
   <int value="1892311584" label="NewStyleNotifications:disabled"/>
diff --git a/tools/metrics/histograms/metadata/android/enums.xml b/tools/metrics/histograms/metadata/android/enums.xml
index fbcbdd0..fa22dfb 100644
--- a/tools/metrics/histograms/metadata/android/enums.xml
+++ b/tools/metrics/histograms/metadata/android/enums.xml
@@ -1226,6 +1226,9 @@
   <int value="0" label="Other"/>
   <int value="1" label="InMultiWindow"/>
   <int value="2" label="InDesktopWindow"/>
+  <int value="3" label="InFullscreen"/>
+  <int value="4" label="ActivityNotVisible"/>
+  <int value="5" label="SystemBarInsetsEmpty"/>
 </enum>
 
 <enum name="MotionEventAction">
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 16973ad..c1104b5 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -1965,7 +1965,7 @@
   </summary>
 </histogram>
 
-<histogram name="Android.EdgeToEdge.MissingNavbarInsets"
+<histogram name="Android.EdgeToEdge.MissingNavbarInsets2"
     enum="MissingNavbarInsetsReason" expires_after="2025-12-01">
   <owner>clhager@google.com</owner>
   <owner>wenyufu@chromium.org</owner>
@@ -4231,6 +4231,17 @@
   </summary>
 </histogram>
 
+<histogram name="Android.Pdf.DocumentLoadInterval" units="ms"
+    expires_after="2025-09-14">
+  <owner>shuyng@google.com</owner>
+  <owner>clank-large-form-factors@google.com</owner>
+  <summary>
+    Records the elapsed time between two pdf document loads. Recorded when the
+    pdf load starts. Used to evaluate the threshold to keep PdfDocumentService
+    running after the last client unbinds.
+  </summary>
+</histogram>
+
 <histogram name="Android.Pdf.DocumentLoadResult.Detail"
     enum="AndroidPdfLoadResult" expires_after="2025-11-09">
   <owner>shuyng@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/performance_manager/enums.xml b/tools/metrics/histograms/metadata/performance_manager/enums.xml
index c21b6674..c82d409 100644
--- a/tools/metrics/histograms/metadata/performance_manager/enums.xml
+++ b/tools/metrics/histograms/metadata/performance_manager/enums.xml
@@ -34,6 +34,18 @@
   <int value="4" label="Mixed"/>
 </enum>
 
+<!-- LINT.IfChange(FrontEndHeapDebugOptionsOutcome) -->
+
+<enum name="FrontEndHeapDebugOptionsOutcome">
+  <int value="0" label="Cannot open the registry key"/>
+  <int value="1" label="Cannot read the value in the registry key"/>
+  <int value="2"
+      label="Success - value is reported to
+             PerformanceManager.RegistryStats.FrontEndHeapDebugOptionsValue"/>
+</enum>
+
+<!-- LINT.ThenChange(//chrome/browser/performance_manager/metrics/metrics_provider_desktop.cc:FrontEndHeapDebugOptionsOutcome) -->
+
 <enum name="InputScenarioUpdateReason">
   <int value="0" label="Key Event"/>
   <int value="1" label="Tap Event"/>
diff --git a/tools/metrics/histograms/metadata/performance_manager/histograms.xml b/tools/metrics/histograms/metadata/performance_manager/histograms.xml
index 0cd9f72..665cb9c 100644
--- a/tools/metrics/histograms/metadata/performance_manager/histograms.xml
+++ b/tools/metrics/histograms/metadata/performance_manager/histograms.xml
@@ -257,6 +257,33 @@
   </summary>
 </histogram>
 
+<histogram
+    name="PerformanceManager.RegistryStats.FrontEndHeapDebugOptionsOutcome"
+    enum="FrontEndHeapDebugOptionsOutcome" expires_after="2025-09-30">
+  <owner>fdoray@chromium.org</owner>
+  <owner>chrome-catan@google.com</owner>
+  <summary>
+    The outcome of attempting to read the value of the HKEY_LOCAL_MACHINE :
+    Software\Microsoft\Windows NT\CurrentVersion\Image File :
+    FrontEndHeapDebugOptions registry key. Emitted when UMA requests information
+    about the current session (see
+    metrics::MetricsProvider::ProvideCurrentSessionData).
+  </summary>
+</histogram>
+
+<histogram
+    name="PerformanceManager.RegistryStats.FrontEndHeapDebugOptionsValue"
+    units="bitfield" expires_after="2025-09-30">
+  <owner>fdoray@chromium.org</owner>
+  <owner>chrome-catan@google.com</owner>
+  <summary>
+    The value of the HKEY_LOCAL_MACHINE : Software\Microsoft\Windows
+    NT\CurrentVersion\Image File : FrontEndHeapDebugOptions registry key.
+    Emitted every time kSuccess is reported to
+    PerformanceManager.RegistryStats.FrontEndHeapDebugOptionsOutcome.
+  </summary>
+</histogram>
+
 <histogram name="PerformanceManager.TabRevisitTracker.TimeToClose2"
     units="seconds" expires_after="2025-02-16">
   <owner>anthonyvd@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/ui/enums.xml b/tools/metrics/histograms/metadata/ui/enums.xml
index a433abc..afa5f19c 100644
--- a/tools/metrics/histograms/metadata/ui/enums.xml
+++ b/tools/metrics/histograms/metadata/ui/enums.xml
@@ -375,6 +375,7 @@
   <int value="72" label="UMA_REVOKE_FILE_EDIT_GRANT"/>
   <int value="73" label="UMA_SEARCH_ENGINE_CHANGED_NOTIFICATION"/>
   <int value="74" label="UMA_BOOKMARK_BATCH_UPLOAD"/>
+  <int value="75" label="UMA_NTP_MOST_VISITED_UNPIN_UNDO"/>
 </enum>
 
 <enum name="WebUIPreloadResult">
diff --git a/tools/metrics/histograms/metadata/uma/enums.xml b/tools/metrics/histograms/metadata/uma/enums.xml
index 786d9f8a..f0556ee8 100644
--- a/tools/metrics/histograms/metadata/uma/enums.xml
+++ b/tools/metrics/histograms/metadata/uma/enums.xml
@@ -303,6 +303,12 @@
   <int value="2" label="Enabled successfully"/>
 </enum>
 
+<enum name="MountExistingCountsStorageResult">
+  <int value="0" label="Success"/>
+  <int value="1" label="Nothing to Read"/>
+  <int value="2" label="Corrupted Values"/>
+</enum>
+
 <enum name="NegativeSampleReason">
   <int value="0" label="Histogram had logged value but no active sample."/>
   <int value="1" label="Histogram active sample less than logged value."/>
diff --git a/tools/metrics/histograms/metadata/uma/histograms.xml b/tools/metrics/histograms/metadata/uma/histograms.xml
index cdf526b..bf0abb1 100644
--- a/tools/metrics/histograms/metadata/uma/histograms.xml
+++ b/tools/metrics/histograms/metadata/uma/histograms.xml
@@ -645,6 +645,18 @@
   </summary>
 </histogram>
 
+<histogram name="UMA.PersistentHistograms.MountExistingCountsStorage"
+    enum="MountExistingCountsStorageResult" expires_after="2026-02-28">
+  <owner>rogerm@chromium.org</owner>
+  <owner>src/base/metrics/OWNERS</owner>
+  <summary>
+    Counts the success/failure outcomes of reading data from a persistent sample
+    vector.
+
+    https://issues.chromium.org/410544723
+  </summary>
+</histogram>
+
 <histogram name="UMA.PrimaryUserType" enum="UserType"
     expires_after="2025-09-14">
   <owner>michaelpg@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/web_audio/histograms.xml b/tools/metrics/histograms/metadata/web_audio/histograms.xml
index addc1fe0..3a33e23 100644
--- a/tools/metrics/histograms/metadata/web_audio/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_audio/histograms.xml
@@ -260,10 +260,11 @@
   <owner>src/third_party/blink/renderer/modules/webaudio/OWNERS</owner>
   <summary>
     The audio delay in milliseconds based on the number of available frames of
-    the PushPUllFIFO in AudioDestination. Measured and reported at every render
-    callback. This metric is categorized by &quot;ModeTag&quot;, which indicates
-    the rendering mode: DualThread, used when audio worklet is involved. Or
-    SingleThread, used when audio worklet is not involved.
+    the PushPUllFIFO in AudioDestination. The metric is averaged and reported at
+    every 1000 render callbacks. This metric is categorized by
+    &quot;ModeTag&quot;, which indicates the rendering mode: DualThread, used
+    when audio worklet is involved. Or SingleThread, used when audio worklet is
+    not involved.
   </summary>
   <token key="ModeTag" variants="RenderMode"/>
   <token key="LatencyTag" variants="WebAudioLatencyTag"/>
@@ -343,7 +344,8 @@
     WebAudio graph to the speaker. This metric is categorized by
     &quot;ModeTag&quot;, which indicates the rendering mode: DualThread, used
     when audio worklet is involved. Or SingleThread, used when audio worklet is
-    not involved.
+    not involved. The metric is averaged and reported at every 1000 render
+    callbacks.
   </summary>
   <token key="ModeTag" variants="RenderMode"/>
   <token key="LatencyTag" variants="WebAudioLatencyTag"/>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 192c7bd..075dd6b7 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,7 +6,7 @@
         },
         "win": {
             "hash": "1eeed5d403b68eb498867f0c1ff665dfabda24e0",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/3c254be4f3e0e63ef363c8f4b8c97a0ab81fca4a/trace_processor_shell.exe"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/109a2ebe2b65215b8ba3ff0d6a3a8361220a0992/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "99f971ca131f6d11c73f4b918099d434bdd8093c",
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 3fcd525..9bf694f 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -163,6 +163,7 @@
 crbug.com/1452148 [ mac ] rendering.desktop/accu_weather_2018 [ Skip ]
 crbug.com/1452148 [ mac ] rendering.desktop/cnn_pinch_2018 [ Skip ]
 crbug.com/1452148 [ mac ] rendering.desktop/espn_pinch_2018 [ Skip ]
+crbug.com/418074735 [ mac-arm64 ] rendering.desktop/motionmark_ramp_images [ Skip ]
 
 # Benchmark: rendering.desktop.notracing
 crbug.com/1452148 [ chromeos-board-eve ] rendering.desktop.notracing/motionmark_ramp_composite [ Skip ]
diff --git a/ui/android/java/src/org/chromium/ui/base/ViewUtils.java b/ui/android/java/src/org/chromium/ui/base/ViewUtils.java
index 13709af..a18dde14 100644
--- a/ui/android/java/src/org/chromium/ui/base/ViewUtils.java
+++ b/ui/android/java/src/org/chromium/ui/base/ViewUtils.java
@@ -165,22 +165,56 @@
     }
 
     /**
-     * Sets clip children for the provided ViewGroup and all of its ancestors.
-     * @param view The ViewGroup whose children should (not) be clipped.
-     * @param clip Whether to clip children to the parent bounds.
+     * As {@link #setAncestorsShouldClipChildren(ViewGroup, boolean, int)}, defaulting to stopping
+     * at the view with id android.R.id.content.
      */
     public static void setAncestorsShouldClipChildren(ViewGroup view, boolean clip) {
+        setAncestorsShouldClipChildren(view, clip, android.R.id.content);
+    }
+
+    /**
+     * Sets clip children for the provided ViewGroup and all of its ancestors.
+     *
+     * @param view The ViewGroup whose children should (not) be clipped.
+     * @param clip Whether to clip children to the parent bounds.
+     * @param viewIdToStopAt The id of the last view in the ancestor list on which the operation
+     *     should performed; potentially NO_ID signifying the operation should traverse all the way
+     *     to the root.
+     */
+    public static void setAncestorsShouldClipChildren(
+            ViewGroup view, boolean clip, int viewIdToStopAt) {
         ViewGroup parent = view;
         while (parent != null) {
             parent.setClipChildren(clip);
             if (!(parent.getParent() instanceof ViewGroup)) break;
-            if (parent.getId() == android.R.id.content) break;
+            if (viewIdToStopAt != View.NO_ID && parent.getId() == viewIdToStopAt) break;
+            parent = (ViewGroup) parent.getParent();
+        }
+    }
+
+    /**
+     * Sets clipToPadding for the provided ViewGroup and all of its ancestors.
+     *
+     * @param view The ViewGroup who should (not) be clipped to padding.
+     * @param clip Whether to clip to padding.
+     * @param viewIdToStopAt The id of the last view in the ancestor list on which the operation
+     *     should performed; potentially NO_ID signifying the operation should traverse all the way
+     *     to the root.
+     */
+    public static void setAncestorsShouldClipToPadding(
+            ViewGroup view, boolean clip, int viewIdToStopAt) {
+        ViewGroup parent = view;
+        while (parent != null) {
+            parent.setClipToPadding(clip);
+            if (!(parent.getParent() instanceof ViewGroup)) break;
+            if (viewIdToStopAt != View.NO_ID && parent.getId() == viewIdToStopAt) break;
             parent = (ViewGroup) parent.getParent();
         }
     }
 
     /**
      * Creates a {@link RoundedBitmapDrawable} using the provided {@link Bitmap} and cornerRadius.
+     *
      * @param resources The {@link Resources}.
      * @param icon The {@link Bitmap} to round.
      * @param cornerRadius The corner radius.
diff --git a/ui/shell_dialogs/BUILD.gn b/ui/shell_dialogs/BUILD.gn
index ae1226d..a6d3edf8 100644
--- a/ui/shell_dialogs/BUILD.gn
+++ b/ui/shell_dialogs/BUILD.gn
@@ -56,8 +56,6 @@
     sources += [
       "select_file_dialog_linux.cc",
       "select_file_dialog_linux.h",
-      "select_file_dialog_linux_kde.cc",
-      "select_file_dialog_linux_kde.h",
       "shell_dialog_linux.cc",
       "shell_dialog_linux.h",
     ]
diff --git a/ui/shell_dialogs/select_file_dialog_linux_kde.cc b/ui/shell_dialogs/select_file_dialog_linux_kde.cc
deleted file mode 100644
index 9d45ec49..0000000
--- a/ui/shell_dialogs/select_file_dialog_linux_kde.cc
+++ /dev/null
@@ -1,601 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cstddef>
-#include <memory>
-#include <set>
-#include <string_view>
-
-#include "base/command_line.h"
-#include "base/files/file_util.h"
-#include "base/functional/bind.h"
-#include "base/functional/callback_helpers.h"
-#include "base/logging.h"
-#include "base/nix/mime_util_xdg.h"
-#include "base/nix/xdg_util.h"
-#include "base/process/launch.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/task/task_traits.h"
-#include "base/task/thread_pool.h"
-#include "base/threading/thread_restrictions.h"
-#include "base/version.h"
-#include "ui/aura/window_tree_host.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/shell_dialogs/select_file_dialog_linux.h"
-#include "ui/shell_dialogs/selected_file_info.h"
-#include "ui/strings/grit/ui_strings.h"
-#include "url/gurl.h"
-
-namespace {
-
-std::string GetTitle(const std::string& title, int message_id) {
-  return title.empty() ? l10n_util::GetStringUTF8(message_id) : title;
-}
-
-const char kKdialogBinary[] = "kdialog";
-
-}  // namespace
-
-namespace ui {
-
-// Implementation of SelectFileDialog that shows a KDE common dialog for
-// choosing a file or folder. This acts as a modal dialog.
-class SelectFileDialogLinuxKde : public SelectFileDialogLinux {
- public:
-  SelectFileDialogLinuxKde(Listener* listener,
-                           std::unique_ptr<ui::SelectFilePolicy> policy,
-                           base::nix::DesktopEnvironment desktop,
-                           const std::string& kdialog_version);
-
-  SelectFileDialogLinuxKde(const SelectFileDialogLinuxKde&) = delete;
-  SelectFileDialogLinuxKde& operator=(const SelectFileDialogLinuxKde&) = delete;
-
- protected:
-  ~SelectFileDialogLinuxKde() override;
-
-  // BaseShellDialog implementation:
-  bool IsRunning(gfx::NativeWindow parent_window) const override;
-
-  // SelectFileDialog implementation.
-  void SelectFileImpl(Type type,
-                      const std::u16string& title,
-                      const base::FilePath& default_path,
-                      const FileTypeInfo* file_types,
-                      int file_type_index,
-                      const base::FilePath::StringType& default_extension,
-                      gfx::NativeWindow owning_window,
-                      const GURL* caller) override;
-
- private:
-  bool HasMultipleFileTypeChoicesImpl() override;
-
-  struct KDialogParams {
-    KDialogParams(const std::string& type,
-                  const std::string& title,
-                  const base::FilePath& default_path,
-                  gfx::AcceleratedWidget parent,
-                  bool file_operation,
-                  bool multiple_selection)
-        : type(type),
-          title(title),
-          default_path(default_path),
-          parent(parent),
-          file_operation(file_operation),
-          multiple_selection(multiple_selection) {}
-
-    std::string type;
-    std::string title;
-    base::FilePath default_path;
-    gfx::AcceleratedWidget parent;
-    bool file_operation;
-    bool multiple_selection;
-  };
-
-  struct KDialogOutputParams {
-    std::string output;
-    int exit_code;
-  };
-
-  // Get the filters from |file_types_| and concatenate them into
-  // |filter_string|.
-  std::string GetMimeTypeFilterString();
-
-  // Get KDialog command line representing the Argv array for KDialog.
-  void GetKDialogCommandLine(const std::string& type,
-                             const std::string& title,
-                             const base::FilePath& default_path,
-                             gfx::AcceleratedWidget parent,
-                             bool file_operation,
-                             bool multiple_selection,
-                             base::CommandLine* command_line);
-
-  // Call KDialog on the FILE thread and return the results.
-  std::unique_ptr<KDialogOutputParams> CallKDialogOutput(
-      const KDialogParams& params);
-
-  // Notifies the listener that a single file was chosen.
-  void FileSelected(const base::FilePath& path);
-
-  // Notifies the listener that multiple files were chosen.
-  void MultiFilesSelected(const std::vector<base::FilePath>& files);
-
-  // Notifies the listener that no file was chosen (the action was canceled).
-  void FileNotSelected();
-
-  void CreateSelectFolderDialog(Type type,
-                                const std::string& title,
-                                const base::FilePath& default_path,
-                                gfx::AcceleratedWidget parent);
-
-  void CreateFileOpenDialog(const std::string& title,
-                            const base::FilePath& default_path,
-                            gfx::AcceleratedWidget parent);
-
-  void CreateMultiFileOpenDialog(const std::string& title,
-                                 const base::FilePath& default_path,
-                                 gfx::AcceleratedWidget parent);
-
-  void CreateSaveAsDialog(const std::string& title,
-                          const base::FilePath& default_path,
-                          gfx::AcceleratedWidget parent);
-
-  // Common function for OnSelectSingleFileDialogResponse and
-  // OnSelectSingleFolderDialogResponse.
-  void SelectSingleFileHelper(bool allow_folder,
-                              std::unique_ptr<KDialogOutputParams> results);
-
-  void OnSelectSingleFileDialogResponse(
-      gfx::AcceleratedWidget parent,
-      std::unique_ptr<KDialogOutputParams> results);
-  void OnSelectMultiFileDialogResponse(
-      gfx::AcceleratedWidget parent,
-      std::unique_ptr<KDialogOutputParams> results);
-  void OnSelectSingleFolderDialogResponse(
-      gfx::AcceleratedWidget parent,
-      std::unique_ptr<KDialogOutputParams> results);
-
-  // Should be either DESKTOP_ENVIRONMENT_KDE3, KDE4, KDE5, or KDE6.
-  base::nix::DesktopEnvironment desktop_;
-
-  // The set of all parent windows for which we are currently running
-  // dialogs. This should only be accessed on the UI thread.
-  std::set<gfx::AcceleratedWidget> parents_;
-
-  // Set to true if the kdialog version is new enough to support passing
-  // multiple extensions with descriptions, eliminating the need for the lossy
-  // conversion of extensions to mime-types.
-  bool kdialog_supports_multiple_extensions_ = false;
-
-  // A task runner for blocking pipe reads.
-  scoped_refptr<base::SequencedTaskRunner> pipe_task_runner_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-};
-
-// static
-bool SelectFileDialogLinux::CheckKDEDialogWorksOnUIThread(
-    std::string& kdialog_version) {
-  // No choice. UI thread can't continue without an answer here. Fortunately we
-  // only do this once, the first time a file dialog is displayed.
-  base::ScopedAllowBlocking scoped_allow_blocking;
-
-  base::CommandLine::StringVector cmd_vector;
-  cmd_vector.push_back(kKdialogBinary);
-  cmd_vector.push_back("--version");
-  base::CommandLine command_line(cmd_vector);
-  return base::GetAppOutput(command_line, &kdialog_version);
-}
-
-SelectFileDialog* NewSelectFileDialogLinuxKde(
-    SelectFileDialog::Listener* listener,
-    std::unique_ptr<ui::SelectFilePolicy> policy,
-    base::nix::DesktopEnvironment desktop,
-    const std::string& kdialog_version) {
-  return new SelectFileDialogLinuxKde(listener, std::move(policy), desktop,
-                                      kdialog_version);
-}
-
-SelectFileDialogLinuxKde::SelectFileDialogLinuxKde(
-    Listener* listener,
-    std::unique_ptr<ui::SelectFilePolicy> policy,
-    base::nix::DesktopEnvironment desktop,
-    const std::string& kdialog_version)
-    : SelectFileDialogLinux(listener, std::move(policy)),
-      desktop_(desktop),
-      pipe_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
-          {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
-           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {
-  DCHECK(desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE3 ||
-         desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE4 ||
-         desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE5 ||
-         desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE6);
-  // |kdialog_version| should be of the form "kdialog 1.2.3", so split on
-  // whitespace and then try to parse a version from the second piece. If
-  // parsing fails for whatever reason, we fall back to the behavior that works
-  // with all currently known versions of kdialog.
-  std::vector<std::string_view> version_pieces = base::SplitStringPiece(
-      kdialog_version, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-  if (version_pieces.size() >= 2) {
-    base::Version parsed_version(version_pieces[1]);
-    if (parsed_version.IsValid()) {
-      kdialog_supports_multiple_extensions_ =
-          parsed_version >= base::Version("19.12");
-    }
-  }
-}
-
-SelectFileDialogLinuxKde::~SelectFileDialogLinuxKde() = default;
-
-bool SelectFileDialogLinuxKde::IsRunning(
-    gfx::NativeWindow parent_window) const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (parent_window && parent_window->GetHost()) {
-    auto window = parent_window->GetHost()->GetAcceleratedWidget();
-    return parents_.find(window) != parents_.end();
-  }
-
-  return false;
-}
-
-// We ignore |default_extension|.
-void SelectFileDialogLinuxKde::SelectFileImpl(
-    Type type,
-    const std::u16string& title,
-    const base::FilePath& default_path,
-    const FileTypeInfo* file_types,
-    int file_type_index,
-    const base::FilePath::StringType& default_extension,
-    gfx::NativeWindow owning_window,
-    const GURL* caller) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  set_type(type);
-
-  gfx::AcceleratedWidget window = gfx::kNullAcceleratedWidget;
-  if (owning_window && owning_window->GetHost()) {
-    // |owning_window| can be null when user right-clicks on a downloadable item
-    // and chooses 'Open Link in New Tab' when 'Ask where to save each file
-    // before downloading.' preference is turned on. (http://crbug.com/29213)
-    window = owning_window->GetHost()->GetAcceleratedWidget();
-    parents_.insert(window);
-  }
-
-  std::string title_string = base::UTF16ToUTF8(title);
-
-  set_file_type_index(file_type_index);
-  if (file_types) {
-    set_file_types(*file_types);
-  } else {
-    auto file_types_copy = SelectFileDialogLinux::file_types();
-    file_types_copy.include_all_files = true;
-    set_file_types(file_types_copy);
-  }
-
-  switch (type) {
-    case SELECT_FOLDER:
-    case SELECT_UPLOAD_FOLDER:
-    case SELECT_EXISTING_FOLDER:
-      CreateSelectFolderDialog(type, title_string, default_path, window);
-      return;
-    case SELECT_OPEN_FILE:
-      CreateFileOpenDialog(title_string, default_path, window);
-      return;
-    case SELECT_OPEN_MULTI_FILE:
-      CreateMultiFileOpenDialog(title_string, default_path, window);
-      return;
-    case SELECT_SAVEAS_FILE:
-      CreateSaveAsDialog(title_string, default_path, window);
-      return;
-    case SELECT_NONE:
-      NOTREACHED();
-  }
-}
-
-bool SelectFileDialogLinuxKde::HasMultipleFileTypeChoicesImpl() {
-  return file_types().extensions.size() > 1;
-}
-
-std::string SelectFileDialogLinuxKde::GetMimeTypeFilterString() {
-  DCHECK(pipe_task_runner_->RunsTasksInCurrentSequence());
-
-  if (!kdialog_supports_multiple_extensions_) {
-    // We need a filter set because the same mime type can appear multiple
-    // times.
-    std::set<std::string> filter_set;
-    for (auto& extensions : file_types().extensions) {
-      for (auto& extension : extensions) {
-        if (!extension.empty()) {
-          std::string mime_type = base::nix::GetFileMimeType(
-              base::FilePath("name").ReplaceExtension(extension));
-          filter_set.insert(mime_type);
-        }
-      }
-    }
-    std::vector<std::string> filter_vector(filter_set.cbegin(),
-                                           filter_set.cend());
-    // Add the *.* filter, but only if we have added other filters (otherwise it
-    // is implied). It needs to be added last to avoid being picked as the
-    // default filter.
-    if (file_types().include_all_files && !file_types().extensions.empty()) {
-      DCHECK(filter_set.find("application/octet-stream") == filter_set.end());
-      filter_vector.push_back("application/octet-stream");
-    }
-    return base::JoinString(filter_vector, " ");
-  }
-
-  std::vector<std::string> filters;
-  for (size_t i = 0; i < file_types().extensions.size(); ++i) {
-    std::set<std::string> extension_filters;
-    for (const auto& extension : file_types().extensions[i]) {
-      if (extension.empty())
-        continue;
-      extension_filters.insert(std::string("*.") + extension);
-    }
-
-    // We didn't find any non-empty extensions to filter on.
-    if (extension_filters.empty())
-      continue;
-
-    std::vector<std::string> extension_filters_vector(extension_filters.begin(),
-                                                      extension_filters.end());
-
-    std::string description;
-    // The description vector may be blank, in which case we are supposed to
-    // use some sort of default description based on the filter.
-    if (i < file_types().extension_description_overrides.size()) {
-      description =
-          base::UTF16ToUTF8(file_types().extension_description_overrides[i]);
-      // Filter out any characters that would mess up kdialog's parsing.
-      base::ReplaceChars(description, "|()", "", &description);
-    } else {
-      // There is no system default filter description so we use
-      // the extensions themselves if the description is blank.
-      description = base::JoinString(extension_filters_vector, ",");
-    }
-
-    filters.push_back(description + " (" +
-                      base::JoinString(extension_filters_vector, " ") + ")");
-  }
-
-  if (file_types().include_all_files && !file_types().extensions.empty())
-    filters.push_back(l10n_util::GetStringUTF8(IDS_SAVEAS_ALL_FILES) + " (*)");
-
-  return base::JoinString(filters, "|");
-}
-
-std::unique_ptr<SelectFileDialogLinuxKde::KDialogOutputParams>
-SelectFileDialogLinuxKde::CallKDialogOutput(const KDialogParams& params) {
-  DCHECK(pipe_task_runner_->RunsTasksInCurrentSequence());
-  base::CommandLine::StringVector cmd_vector;
-  cmd_vector.push_back(kKdialogBinary);
-  base::CommandLine command_line(cmd_vector);
-  GetKDialogCommandLine(params.type, params.title, params.default_path,
-                        params.parent, params.file_operation,
-                        params.multiple_selection, &command_line);
-
-  auto results = std::make_unique<KDialogOutputParams>();
-  // Get output from KDialog
-  base::GetAppOutputWithExitCode(command_line, &results->output,
-                                 &results->exit_code);
-  if (!results->output.empty())
-    results->output.erase(results->output.size() - 1);
-  return results;
-}
-
-void SelectFileDialogLinuxKde::GetKDialogCommandLine(
-    const std::string& type,
-    const std::string& title,
-    const base::FilePath& path,
-    gfx::AcceleratedWidget parent,
-    bool file_operation,
-    bool multiple_selection,
-    base::CommandLine* command_line) {
-  CHECK(command_line);
-
-  // Attach to the current Chrome window.
-  if (parent != gfx::kNullAcceleratedWidget) {
-    command_line->AppendSwitchNative(
-        desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE3 ? "--embed"
-                                                        : "--attach",
-        base::NumberToString(static_cast<uint32_t>(parent)));
-  }
-
-  // Set the correct title for the dialog.
-  if (!title.empty())
-    command_line->AppendSwitchNative("--title", title);
-  // Enable multiple file selection if we need to.
-  if (multiple_selection) {
-    command_line->AppendSwitch("--multiple");
-    command_line->AppendSwitch("--separate-output");
-  }
-  command_line->AppendSwitch(type);
-  // The path should never be empty. If it is, set it to PWD.
-  auto pwd = base::FilePath(".");
-  if (path.empty()) {
-    command_line->AppendArgPath(pwd);
-  } else if (path.IsAbsolute()) {
-    command_line->AppendArgPath(path);
-  } else {
-    // KDialog won't set the default name in the Name field for relative paths.
-    auto abs_path = base::MakeAbsoluteFilePathNoResolveSymbolicLinks(path);
-    command_line->AppendArgPath(abs_path.value_or(pwd));
-  }
-  // Depending on the type of the operation we need, get the path to the
-  // file/folder and set up mime type filters.
-  if (file_operation)
-    command_line->AppendArg(GetMimeTypeFilterString());
-  DVLOG(1) << "KDialog command line: " << command_line->GetCommandLineString();
-}
-
-void SelectFileDialogLinuxKde::FileSelected(const base::FilePath& path) {
-  if (type() == SELECT_SAVEAS_FILE) {
-    set_last_saved_path(path.DirName());
-  } else if (type() == SELECT_OPEN_FILE) {
-    set_last_opened_path(path.DirName());
-  } else if (type() == SELECT_FOLDER || type() == SELECT_UPLOAD_FOLDER ||
-             type() == SELECT_EXISTING_FOLDER) {
-    set_last_opened_path(path);
-  } else {
-    NOTREACHED();
-  }
-  if (listener_) {  // What does the filter index actually do?
-    // TODO(dfilimon): Get a reasonable index value from somewhere.
-    listener_->FileSelected(SelectedFileInfo(path), 1);
-  }
-}
-
-void SelectFileDialogLinuxKde::MultiFilesSelected(
-    const std::vector<base::FilePath>& files) {
-  set_last_opened_path(files[0].DirName());
-  if (listener_)
-    listener_->MultiFilesSelected(FilePathListToSelectedFileInfoList(files));
-}
-
-void SelectFileDialogLinuxKde::FileNotSelected() {
-  if (listener_)
-    listener_->FileSelectionCanceled();
-}
-
-void SelectFileDialogLinuxKde::CreateSelectFolderDialog(
-    Type type,
-    const std::string& title,
-    const base::FilePath& default_path,
-    gfx::AcceleratedWidget parent) {
-  int title_message_id = (type == SELECT_UPLOAD_FOLDER)
-                             ? IDS_SELECT_UPLOAD_FOLDER_DIALOG_TITLE
-                             : IDS_SELECT_FOLDER_DIALOG_TITLE;
-  pipe_task_runner_->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(
-          &SelectFileDialogLinuxKde::CallKDialogOutput, this,
-          KDialogParams(
-              "--getexistingdirectory", GetTitle(title, title_message_id),
-              default_path.empty() ? *last_opened_path() : default_path, parent,
-              false, false)),
-      base::BindOnce(
-          &SelectFileDialogLinuxKde::OnSelectSingleFolderDialogResponse, this,
-          parent));
-}
-
-void SelectFileDialogLinuxKde::CreateFileOpenDialog(
-    const std::string& title,
-    const base::FilePath& default_path,
-    gfx::AcceleratedWidget parent) {
-  pipe_task_runner_->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(
-          &SelectFileDialogLinuxKde::CallKDialogOutput, this,
-          KDialogParams(
-              "--getopenfilename", GetTitle(title, IDS_OPEN_FILE_DIALOG_TITLE),
-              default_path.empty() ? *last_opened_path() : default_path, parent,
-              true, false)),
-      base::BindOnce(
-          &SelectFileDialogLinuxKde::OnSelectSingleFileDialogResponse, this,
-          parent));
-}
-
-void SelectFileDialogLinuxKde::CreateMultiFileOpenDialog(
-    const std::string& title,
-    const base::FilePath& default_path,
-    gfx::AcceleratedWidget parent) {
-  pipe_task_runner_->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(
-          &SelectFileDialogLinuxKde::CallKDialogOutput, this,
-          KDialogParams(
-              "--getopenfilename", GetTitle(title, IDS_OPEN_FILES_DIALOG_TITLE),
-              default_path.empty() ? *last_opened_path() : default_path, parent,
-              true, true)),
-      base::BindOnce(&SelectFileDialogLinuxKde::OnSelectMultiFileDialogResponse,
-                     this, parent));
-}
-
-void SelectFileDialogLinuxKde::CreateSaveAsDialog(
-    const std::string& title,
-    const base::FilePath& default_path,
-    gfx::AcceleratedWidget parent) {
-  pipe_task_runner_->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(
-          &SelectFileDialogLinuxKde::CallKDialogOutput, this,
-          KDialogParams(
-              "--getsavefilename", GetTitle(title, IDS_SAVE_AS_DIALOG_TITLE),
-              default_path.empty() ? *last_saved_path() : default_path, parent,
-              true, false)),
-      base::BindOnce(
-          &SelectFileDialogLinuxKde::OnSelectSingleFileDialogResponse, this,
-          parent));
-}
-
-void SelectFileDialogLinuxKde::SelectSingleFileHelper(
-    bool allow_folder,
-    std::unique_ptr<KDialogOutputParams> results) {
-  DVLOG(1) << "[kdialog] SingleFileResponse: " << results->output;
-  if (results->exit_code || results->output.empty()) {
-    FileNotSelected();
-    return;
-  }
-
-  base::FilePath path(results->output);
-  if (allow_folder) {
-    FileSelected(path);
-    return;
-  }
-
-  if (CallDirectoryExistsOnUIThread(path))
-    FileNotSelected();
-  else
-    FileSelected(path);
-}
-
-void SelectFileDialogLinuxKde::OnSelectSingleFileDialogResponse(
-    gfx::AcceleratedWidget parent,
-    std::unique_ptr<KDialogOutputParams> results) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  parents_.erase(parent);
-  SelectSingleFileHelper(false, std::move(results));
-}
-
-void SelectFileDialogLinuxKde::OnSelectSingleFolderDialogResponse(
-    gfx::AcceleratedWidget parent,
-    std::unique_ptr<KDialogOutputParams> results) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  parents_.erase(parent);
-  SelectSingleFileHelper(true, std::move(results));
-}
-
-void SelectFileDialogLinuxKde::OnSelectMultiFileDialogResponse(
-    gfx::AcceleratedWidget parent,
-    std::unique_ptr<KDialogOutputParams> results) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DVLOG(1) << "[kdialog] MultiFileResponse: " << results->output;
-
-  parents_.erase(parent);
-
-  if (results->exit_code || results->output.empty()) {
-    FileNotSelected();
-    return;
-  }
-
-  std::vector<base::FilePath> filenames_fp;
-  for (std::string_view line :
-       base::SplitStringPiece(results->output, "\n", base::KEEP_WHITESPACE,
-                              base::SPLIT_WANT_NONEMPTY)) {
-    base::FilePath path(line);
-    if (CallDirectoryExistsOnUIThread(path))
-      continue;
-    filenames_fp.push_back(path);
-  }
-
-  if (filenames_fp.empty()) {
-    FileNotSelected();
-    return;
-  }
-  MultiFilesSelected(filenames_fp);
-}
-
-}  // namespace ui
diff --git a/ui/shell_dialogs/select_file_dialog_linux_kde.h b/ui/shell_dialogs/select_file_dialog_linux_kde.h
deleted file mode 100644
index efb4be3e..0000000
--- a/ui/shell_dialogs/select_file_dialog_linux_kde.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_LINUX_KDE_H_
-#define UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_LINUX_KDE_H_
-
-#include <string>
-
-#include "base/nix/xdg_util.h"
-#include "ui/shell_dialogs/select_file_dialog.h"
-
-namespace ui {
-
-class SelectFileDialog;
-
-SelectFileDialog* NewSelectFileDialogLinuxKde(
-    SelectFileDialog::Listener* listener,
-    std::unique_ptr<ui::SelectFilePolicy> policy,
-    base::nix::DesktopEnvironment desktop,
-    const std::string& kdialog_version);
-
-}  // namespace ui
-
-#endif  // UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_LINUX_KDE_H_
diff --git a/ui/shell_dialogs/shell_dialog_linux.cc b/ui/shell_dialogs/shell_dialog_linux.cc
index 21ce7fae..1a4daf4e 100644
--- a/ui/shell_dialogs/shell_dialog_linux.cc
+++ b/ui/shell_dialogs/shell_dialog_linux.cc
@@ -4,13 +4,10 @@
 
 #include "ui/shell_dialogs/shell_dialog_linux.h"
 
-#include "base/environment.h"
-#include "base/no_destructor.h"
 #include "base/notreached.h"
 #include "build/config/linux/dbus/buildflags.h"
 #include "ui/linux/linux_ui.h"
 #include "ui/shell_dialogs/select_file_dialog_linux.h"
-#include "ui/shell_dialogs/select_file_dialog_linux_kde.h"
 #include "ui/shell_dialogs/select_file_policy.h"
 
 #if BUILDFLAG(USE_DBUS)
@@ -34,7 +31,6 @@
 enum FileDialogChoice {
   kUnknown,
   kToolkit,
-  kKde,
 #if BUILDFLAG(USE_DBUS)
   kPortal,
 #endif
@@ -42,11 +38,6 @@
 
 FileDialogChoice dialog_choice_ = kUnknown;
 
-std::string& KDialogVersion() {
-  static base::NoDestructor<std::string> version;
-  return *version;
-}
-
 FileDialogChoice GetFileDialogChoice() {
 #if BUILDFLAG(USE_DBUS)
   // Check to see if the portal is available.
@@ -54,24 +45,6 @@
     return kPortal;
 #endif
 
-  // Check to see if KDE is the desktop environment.
-  std::unique_ptr<base::Environment> env(base::Environment::Create());
-  base::nix::DesktopEnvironment desktop =
-      base::nix::GetDesktopEnvironment(env.get());
-  if (desktop == base::nix::DESKTOP_ENVIRONMENT_KDE3 ||
-      desktop == base::nix::DESKTOP_ENVIRONMENT_KDE4 ||
-      desktop == base::nix::DESKTOP_ENVIRONMENT_KDE5 ||
-      desktop == base::nix::DESKTOP_ENVIRONMENT_KDE6) {
-    // Check to see if the user dislikes the KDE file dialog.
-    if (!env->HasVar("NO_CHROME_KDE_FILE_DIALOG")) {
-      // Check to see if the KDE dialog works.
-      if (SelectFileDialogLinux::CheckKDEDialogWorksOnUIThread(
-              KDialogVersion())) {
-        return kKde;
-      }
-    }
-  }
-
   return kToolkit;
 }
 
@@ -93,13 +66,6 @@
     case kPortal:
       return new SelectFileDialogLinuxPortal(listener, std::move(policy));
 #endif
-    case kKde: {
-      std::unique_ptr<base::Environment> env(base::Environment::Create());
-      base::nix::DesktopEnvironment desktop =
-          base::nix::GetDesktopEnvironment(env.get());
-      return NewSelectFileDialogLinuxKde(listener, std::move(policy), desktop,
-                                         KDialogVersion());
-    }
     case kUnknown:
       NOTREACHED();
   }