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( - ®istry.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>(§ions)); + 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*>(¶ms), + }, + }; + + 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, ¶ms))); + EXPECT_HRESULT_SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters( + AudioDeviceDescription::kDefaultDeviceId, is_output_device, ¶ms)); 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, - ¶ms))); + ¶ms)); EXPECT_TRUE(params.IsValid()); + EXPECT_HRESULT_SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters( + AudioDeviceDescription::kApplicationLoopbackDeviceId, + is_output_device, ¶ms)); { 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, ¶ms, - 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, ¶ms, - 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 ¤tTarget()->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 "ModeTag", 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 + "ModeTag", 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 "ModeTag", 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(); }